Kubernetes: 为什么修补自定义资源的 /status 子资源会更新父资源?

4

我正在试图理解Kubernetes在自定义资源及其子资源方面的行为,特别是status子资源。

具体来说,我想更新status子资源而不修改父级自定义资源。

据我所知,这应该是可能的。我已经查阅了文档[1],但似乎无法按照预期工作。

我正在使用 Docker Desktop 的Kubernetes 1.19.3 进行测试。

以下是场景...

  1. 创建此简单的CRD:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: greetings.k8s.test.io
spec:
  group: k8s.test.io
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              description: Greeting is the Schema for the Greetings Operator
              type: object
              properties:
                message:
                  description: A friendly greeting
                  type: string
                  default: Hello World!
            status:
              type: object
              properties:
                ready:
                  description: The resource's readiness
                  type: boolean
      additionalPrinterColumns:
      - name: ready
        type: boolean
        description: Readiness of the created resource
        jsonPath: .status.ready
      subresources:
        status: {}
  scope: Namespaced
  names:
    plural: greetings
    singular: greeting
    kind: Greeting
    shortNames:
    - grt
  1. 创建演示资源:
apiVersion: k8s.test.io/v1alpha1
kind: Greeting
metadata:
  name: demo
spec:
  message: Hi there!
  1. 启动代理:
kubectl proxy &
  1. 建立自定义资源的监视器:
curl -L -s -X GET -H "Content-Type: application/json" \
-H "Accept: application/json, */*" \
127.0.0.1:8001/apis//k8s.test.io/v1alpha1/watch/namespaces/default/greetings

此时,您应该看到类似以下的输出:

{"type":"ADDED","object":{"apiVersion":"k8s.test.io/v1alpha1","kind":"Greeting","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"k8s.test.io/v1alpha1\",\"kind\":\"Greeting\",\"metadata\":{\"annotations\":{},\"name\":\"demo\",\"namespace\":\"default\"},\"spec\":{\"message\":\"Hi there!\"}}\n"},"creationTimestamp":"2020-12-10T04:02:55Z","generation":1,"managedFields":[{"apiVersion":"k8s.test.io/v1alpha1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:kubectl.kubernetes.io/last-applied-configuration":{}}},"f:spec":{".":{},"f:message":{}}},"manager":"kubectl-client-side-apply","operation":"Update","time":"2020-12-10T04:02:55Z"}],"name":"demo","namespace":"default","resourceVersion":"532930","selfLink":"/apis/k8s.test.io/v1alpha1/namespaces/default/greetings/demo","uid":"40f3a618-74e5-4b14-9bd4-2eb47366d804"},"spec":{"message":"Hi there!"}}}
  1. /status子资源执行PATCH操作
curl -k -s -X PATCH -H "Accept: application/json, */*" \
-H "Content-Type: application/merge-patch+json" \
127.0.0.1:8001/apis/k8s.test.io/v1alpha1/namespaces/default/greetings/demo/status \
--data '{"status":{"ready":true}}'

令人困惑的是,在提交PATCH之后,我们对父资源的监视产生了...

{"type":"MODIFIED","object":{"apiVersion":"k8s.test.io/v1alpha1","kind":"Greeting","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"k8s.test.io/v1alpha1\",\"kind\":\"Greeting\",\"metadata\":{\"annotations\":{},\"name\":\"demo\",\"namespace\":\"default\"},\"spec\":{\"message\":\"Hi there!\"}}\n"},"creationTimestamp":"2020-12-10T04:02:55Z","generation":1,"managedFields":[{"apiVersion":"k8s.test.io/v1alpha1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:kubectl.kubernetes.io/last-applied-configuration":{}}},"f:spec":{".":{},"f:message":{}}},"manager":"kubectl-client-side-apply","operation":"Update","time":"2020-12-10T04:02:55Z"},{"apiVersion":"k8s.test.io/v1alpha1","fieldsType":"FieldsV1","fieldsV1":{"f:status":{".":{},"f:ready":{}}},"manager":"curl","operation":"Update","time":"2020-12-10T04:05:22Z"}],"name":"demo","namespace":"default","resourceVersion":"533184","selfLink":"/apis/k8s.test.io/v1alpha1/namespaces/default/greetings/demo","uid":"40f3a618-74e5-4b14-9bd4-2eb47366d804"},"spec":{"message":"Hi there!"},"status":{"ready":true}}}

为什么要修改父资源?
如果我们检查资源,可以看到子资源的状态确实被更新了:
$ k get grt demo
NAME   READY
demo   true
我不知道这种行为是否符合预期,同时我对PATCH方法论的理解是否有误。 希望有人能帮忙。谢谢。 更新: 除了PATCH外,我还可以确认PUT(虽然是一个更复杂的操作)也展现了完全相同的行为。
示例:
curl -k -s -X PUT -H "Accept: application/json, */*" \
-H "Content-Type: application/json" \
127.0.0.1:8001/apis/k8s.test.io/v1alpha1/namespaces/default/greetings/demo/status \
--data '{"apiVersion":"k8s.test.io/v1alpha1","kind":"Greeting","metadata":{"name":"demo","resourceVersion":"533184"},"status":{"ready":false}}'

¯\(ツ)

1个回答

4
status子资源并不是一个独立的对象;它只是一个单独的API路径,仅能修改对象中顶层status:块。这很有用,因为您可以为控制器设置RBAC策略,允许读取整个对象,但只允许写入对象的状态。
特别地,在最后一个命令中您会看到这一点。如果您运行kubectl get grt demo -o yaml,扩展的YAML语法将包括status:子块。更新状态的控制器将导致status:中可见的变化,因此您应该合理地期望监视到这种变化。

如果是这样的话,那么k8s Operator如何监视资源或资源列表并根据不同事件(ADDED、MODIFIED等)做出适当反应呢?以下是一个示例:
  1. 创建资源
  2. Operator检测到ADDED
  3. Operator更新/status(PATCH)
  4. Operator检测到MODIFIED
如果没有区分资源及其子资源,监视将检测到相同的Operator更新自定义资源的/status。
- Brian Warsing
由于对象元数据的generation字段,这个字段在对象的spec子资源被修改时会更新。一个良好构建的操作员不应对与相关对象的generation字段小于或等于已处理事件的字段(status子资源中的observedGeneration可以帮助解决此问题)的任何进一步事件做出反应。 - Adrien Ferrand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接