4. 自定义资源(CR)实例化:基于CRD创建自定义资源实例,理解spec与status的分离

在完成自定义资源定义(CRD)的注册后,Kubernetes API Server 已经能够识别新的资源类型。本章将深入实践如何基于 CRD 创建具体的自定义资源(CR)实例,并重点剖析 Kubernetes 声明式 API 的核心设计模式——spec 与 status 的分离。

4.1 从 CRD 到 CR:实例化的本质

CRD 相当于数据库中的“表结构定义”,而 CR 则是表中的“具体数据行”。当您向 API Server 提交一个 CR 对象时,Kubernetes 会验证其结构是否符合对应 CRD 定义的 openAPIV3Schema 规则,然后将其持久化存储到 etcd 中。

关键理解: CRD 是类型系统(Type),CR 是实例(Instance)。一个 CRD 可以对应无数个 CR,就像 Deployment 资源类型可以对应无数个具体的 Deployment 实例。

4.2 创建第一个自定义资源实例

假设我们已注册了如下简化的 Database CRD(仅展示核心结构):

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.example.com
spec:
  group: example.com
  names:
    kind: Database
    plural: databases
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                engine:
                  type: string
                version:
                  type: string
                storageGB:
                  type: integer
            status:
              type: object
              properties:
                phase:
                  type: string
                endpoint:
                  type: string

现在,我们创建一个 CR 实例 my-database:

apiVersion: example.com/v1
kind: Database
metadata:
  name: my-database
  namespace: default
spec:
  engine: postgresql
  version: "15.2"
  storageGB: 100

使用 kubectl apply -f database-instance.yaml 提交后,API Server 会返回成功响应。此时,资源已创建,但 status 字段为空。

4.3 spec 与 status 的分离设计

这是 Kubernetes 声明式 API 的基石。两者的职责截然不同:

维度 spec(期望状态) status(实际状态)
写入者 用户 / 运维人员 控制器(Controller)
内容 用户声明的理想配置 系统观测到的实时状态
变更触发 用户主动修改 控制器持续调和(Reconcile)后更新
数据来源 YAML / API 请求 控制器从集群或外部系统采集
是否可被用户直接修改 是 否(应仅由控制器写入)
最佳实践: 控制器应始终通过 status 子资源(/status endpoint)更新状态,而非直接修改主资源。这可以避免与用户对 spec 的修改产生冲突,并确保 RBAC 权限的精细控制。

4.4 实例化后的资源状态演变

下图展示了从用户提交 CR 到控制器更新 status 的完整生命周期:

CR 实例化与状态演变流程 用户 API Server etcd 控制器 外部系统 1. 提交 CR (spec) 2. 持久化 3. Watch 通知 4. 编排/查询 5. 返回状态 6. 更新 status 说明: 用户只负责定义 spec(期望状态),控制器负责观测并更新 status(实际状态),形成闭环。 status 的更新通过 /status 子资源进行,避免与 spec 的写操作冲突。

4.5 查看实例的完整状态

创建 CR 后,使用 kubectl get 和 kubectl describe 可以观察到 spec 与 status 的差异:

# 查看 CR 列表(此时 status 可能为空)
$ kubectl get databases.example.com -n default
NAME           AGE
my-database    2m

# 查看详细状态(控制器尚未更新 status)
$ kubectl get database my-database -n default -o yaml
apiVersion: example.com/v1
kind: Database
metadata:
  name: my-database
  namespace: default
  ...
spec:
  engine: postgresql
  version: "15.2"
  storageGB: 100
status: {}   # 注意:status 为空对象

当控制器完成调和后,再次查看:

$ kubectl get database my-database -n default -o yaml
...
spec:
  engine: postgresql
  version: "15.2"
  storageGB: 100
status:
  phase: Running
  endpoint: my-database.default.svc.cluster.local:5432

4.6 多实例与资源隔离

基于同一个 CRD,可以创建多个具有不同 spec 的 CR 实例,它们彼此独立:

实例名称 engine version storageGB status.phase
my-database postgresql 15.2 100 Running
analytics-db mysql 8.0 500 Provisioning
cache-db redis 7.0 20 Running
注意: 如果 CRD 的 scope 设置为 Namespaced,则 CR 实例的命名空间隔离由 Kubernetes 原生保障。若设置为 Cluster,则实例为集群级别,名称必须全局唯一。

4.7 常见错误与排查

  • CRD 未就绪: 创建 CR 时提示 the server could not find the requested resource。请确认 CRD 的 Established 条件为 True。
  • Schema 校验失败: 提交的 CR 字段类型或必填项不符合 CRD 定义。API Server 会返回详细的校验错误信息。
  • Status 字段被用户误修改: 控制器应使用 /status 子资源更新。如果用户直接修改 status,控制器下次调和时会覆盖回正确值。
  • 实例名称冲突: 同一命名空间下(或集群范围)的 CR 名称必须唯一。

4.8 小结

通过本章实践,您已经掌握了从 CRD 到 CR 的实例化过程,并深刻理解了 spec 与 status 分离的设计哲学。这种分离确保了用户意图(spec)与系统实际状态(status)的清晰边界,为后续控制器实现声明式调和循环奠定了坚实基础。