4. CRD 状态子资源:Status 子资源的启用、Status 与 Spec 的分离设计原则、Status 字段的典型结构

在 Kubernetes 自定义资源(CRD)的设计中,Status 子资源(Subresource) 是区分“用户期望”与“系统实际状态”的核心机制。启用 Status 子资源后,Kubernetes API 会将 spec 和 status 视为两个独立的 API 端点,从而在权限控制、并发写入和状态管理上获得更严格的保障。

4.1 启用 Status 子资源

在 CRD 的 YAML 定义中,通过 subresources.status 字段启用 Status 子资源:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: myresources.example.com
spec:
  group: example.com
  names:
    kind: MyResource
    plural: myresources
  scope: Namespaced
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              replicas:
                type: integer
          status:
            type: object
            properties:
              availableReplicas:
                type: integer
              conditions:
                type: array
                items:
                  type: object
                  properties:
                    type:
                      type: string
                    status:
                      type: string
                    lastTransitionTime:
                      type: string
    subresources:
      status: {}   # 启用 status 子资源
关键行为变化:启用 Status 子资源后,kubectl status 子命令可用;Operator 对 status 的写入不会触发 spec 的冲突检测;RBAC 可以分别控制 spec 和 status 的读写权限。

4.2 Status 与 Spec 的分离设计原则

分离设计是 CRD 状态管理的基石,遵循以下核心原则:

维度 Spec(期望状态) Status(实际状态)
写入主体 用户 / 控制器 Operator 控制器
变更触发 用户提交 YAML 控制器 reconcile 循环
并发控制 乐观锁(resourceVersion) 独立乐观锁(与 spec 隔离)
RBAC 粒度 update 权限 update/status 权限
持久化 etcd 完整对象 etcd 完整对象(同一 key)
可观测性 用户意图 集群真实状态
设计原则总结:
  • 只读原则:Status 字段只能由 Operator 控制器写入,用户不应手动修改。
  • 幂等原则:Status 更新应幂等,多次写入相同值不产生副作用。
  • 可推导原则:Status 应能从集群实际资源状态推导得出,不依赖外部状态。
  • 最小化原则:只暴露对用户和运维有意义的聚合状态,避免存储原始细节。

4.3 Status 字段的典型结构

一个设计良好的 Status 结构通常包含以下三个核心部分:

status:
  # 1. 聚合计数器:快速了解资源整体健康度
  availableReplicas: 3
  readyReplicas: 3
  currentRevision: "5"

  # 2. 条件数组:描述资源生命周期中的关键阶段
  conditions:
  - type: Available
    status: "True"
    reason: MinimumReplicasAvailable
    message: "Deployment has minimum availability."
    lastTransitionTime: "2025-03-20T10:30:00Z"
    observedGeneration: 3
  - type: Progressing
    status: "True"
    reason: NewReplicaSetAvailable
    message: "ReplicaSet myapp-7d9f8c6b4 has been successfully rolled out."
    lastTransitionTime: "2025-03-20T10:25:00Z"
    observedGeneration: 3

  # 3. 观测元数据:记录控制器最后一次处理的信息
  observedGeneration: 3
  lastHeartbeatTime: "2025-03-20T10:35:00Z"
常见陷阱:
  • 不要在 Status 中存储 Spec 的副本,这会导致数据冗余和一致性问题。
  • Conditions 中的 status 字段("True"/"False"/"Unknown")不要与 CRD 的 .status 子资源混淆。
  • observedGeneration 必须与 Spec 的 metadata.generation 对齐,否则用户无法判断控制器是否已处理最新 Spec。

4.4 Status 子资源与 Spec 的交互流程

下图展示了 Operator 在 Reconcile 循环中如何分别处理 Spec 和 Status:

Status 子资源与 Spec 分离交互流程 用户 / 客户端 API Server etcd 写入 Spec 持久化 Operator 控制器 Informer / Watch Watch 事件 触发 Reconcile Reconcile 循环内部 读取 Spec(期望) 比对实际资源 写入 Status /status 端点 独立写入

4.5 Conditions 设计最佳实践

Conditions 是 Status 中最核心的组成部分,设计时需遵循以下规范:

  • 类型命名:使用 PascalCase,如 Available、Progressing、Degraded。
  • 状态值:仅使用 True、False、Unknown 三种。
  • Reason 字段:使用驼峰式机器可读字符串,如 MinimumReplicasAvailable。
  • Message 字段:提供人类可读的详细说明,便于调试。
  • observedGeneration:必须记录,用于判断 Condition 是否过期。
  • lastTransitionTime:仅当 Condition 的 status 发生变化时更新,避免频繁变更。
典型 Condition 类型示例:
  • Available:资源是否可用,用户最关心的健康指标。
  • Progressing:资源是否正在变更,用于滚动更新或扩缩容。
  • Degraded:资源部分可用,但未完全失效。
  • Stalled:Reconcile 循环卡住,无法推进状态。

4.6 Status 子资源的 RBAC 控制

启用 Status 子资源后,RBAC 可以分别控制对 spec 和 status 的访问:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: myresource-operator
rules:
- apiGroups: ["example.com"]
  resources: ["myresources"]
  verbs: ["get", "list", "watch", "update", "patch"]   # 控制 spec 写入
- apiGroups: ["example.com"]
  resources: ["myresources/status"]
  verbs: ["get", "update", "patch"]                     # 控制 status 写入
安全建议:普通用户只授予 myresources 的 get/list/watch 权限和 myresources/status 的 get 权限;Operator 控制器才拥有 myresources/status 的 update/patch 权限。这样可以防止用户绕过控制器直接篡改状态。

4.7 状态更新策略:Update vs Patch

策略 方法 适用场景 风险
Update 读取完整对象 → 修改 status → 写回 需要读取完整 status 做决策 并发冲突(需重试)
Patch 仅发送 status 变更部分 频繁更新单个字段(如 Conditions) 可能覆盖其他控制器写入
Status Patch 使用 /status 子资源 Patch 推荐方式,隔离性好 需确保 patch 内容正确
重要提示:无论使用哪种策略,都必须通过 /status 子资源端点进行操作。直接修改 .status 字段并通过主资源 Update 写入,会导致 Spec 的并发控制失效,且可能触发不必要的 Reconcile 循环。