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:
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 循环。