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 的完整生命周期:
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)的清晰边界,为后续控制器实现声明式调和循环奠定了坚实基础。