2. CRD 基础:CustomResourceDefinition 定义、版本管理与结构验证
CustomResourceDefinition(CRD)是 Kubernetes 扩展机制的核心,它允许用户在不修改核心代码的前提下,向集群注册自定义资源类型。本章将系统讲解 CRD 的声明式定义、多版本管理策略以及结构验证规则,为后续使用 Operator SDK 开发控制器奠定基础。
2.1 CRD 声明式定义
CRD 本身是一个标准的 Kubernetes API 资源,通过 YAML 清单向集群注册。一个完整的 CRD 定义包含以下关键字段:
| 字段 | 说明 | 是否必填 |
|---|---|---|
metadata.name |
CRD 名称,必须为 <plural>.<group> 格式 |
是 |
spec.group |
API 组,如 example.com |
是 |
spec.names |
资源名称配置(plural、singular、shortNames、kind) | 是 |
spec.scope |
作用域:Namespaced 或 Cluster |
是 |
spec.versions |
版本列表,每个版本包含 schema、subresources 等 | 是 |
以下是一个典型的 CRD 定义示例,注册名为 MyApp 的自定义资源:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: myapps.example.com
spec:
group: example.com
names:
kind: MyApp
plural: myapps
singular: myapp
shortNames:
- ma
scope: Namespaced
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
replicas:
type: integer
minimum: 1
maximum: 10
image:
type: string
subresources:
status: {}
plural 必须与 spec.names.plural 一致,且 group 不能与核心 API 组(如 core、apps)冲突。建议使用公司域名作为 group 前缀,避免命名空间污染。
2.2 CRD 版本管理
Kubernetes 1.16+ 支持 CRD 的多版本管理,允许同一资源在不同 API 版本间共存和转换。版本管理涉及三个核心概念:
- served:该版本是否在 API Server 上提供服务,客户端可通过该版本读写资源。
- storage:集群持久化存储使用哪个版本,只能有一个版本标记为
storage: true。 - conversion:不同版本间的转换策略,支持
None(无转换)和Webhook(自定义转换)。
版本管理的最佳实践遵循“先增后弃”原则:
- 新增版本时,将新版本标记为
served: true,旧版本保持storage: true。 - 通过 Webhook 实现版本间字段映射,确保数据不丢失。
- 确认所有客户端迁移到新版本后,将旧版本
served设为false。 - 最终移除旧版本定义,并将
storage切换到新版本。
下图展示了 CRD 版本管理的完整生命周期:
2.3 CRD 结构验证
CRD 通过 OpenAPI v3 Schema 定义资源的结构约束,API Server 在写入 etcd 前会自动校验。结构验证支持以下规则:
| 验证类型 | 关键字 | 示例 |
|---|---|---|
| 类型约束 | type |
type: integer |
| 必填字段 | required |
required: ["name", "version"] |
| 数值范围 | minimum / maximum |
minimum: 1, maximum: 100 |
| 字符串模式 | pattern |
pattern: "^[a-z0-9-]+$" |
| 枚举值 | enum |
enum: ["dev", "staging", "prod"] |
| 数组长度 | minItems / maxItems |
minItems: 1, maxItems: 10 |
| 嵌套对象 | properties |
支持多层嵌套和 additionalProperties: false |
以下是一个包含完整验证规则的 CRD Schema 示例:
schema:
openAPIV3Schema:
type: object
required:
- spec
properties:
spec:
type: object
required:
- image
- replicas
properties:
image:
type: string
pattern: "^[a-z0-9]+(?:[._-][a-z0-9]+)*/[a-z0-9]+(?:[._-][a-z0-9]+)*:[a-zA-Z0-9._-]+$"
replicas:
type: integer
minimum: 1
maximum: 20
env:
type: array
items:
type: object
required:
- name
properties:
name:
type: string
pattern: "^[A-Z_]+$"
value:
type: string
maxItems: 50
tier:
type: string
enum:
- dev
- staging
- production
- additionalProperties 默认 true:如果不显式设置为
false,用户可以在对象中写入未定义的字段,导致验证形同虚设。 - 整数与浮点数混淆:Kubernetes 1.25 之前
type: integer会接受浮点数(如 3.0),建议配合format: int32或format: int64使用。 - 嵌套 required 遗漏:
required只在当前对象层级生效,嵌套对象需要单独声明 required 字段。
2.4 CRD 结构验证与 PreserveUnknownFields
当 CRD 定义中未显式声明某些字段时,API Server 的行为由 spec.preserveUnknownFields 控制:
- false(默认):拒绝包含未定义字段的请求,严格遵循 Schema。
- true:允许未定义字段通过,但会绕过结构验证,适用于需要灵活扩展的场景。
从 Kubernetes 1.21 开始,推荐使用 spec.versions[*].schema.openAPIV3Schema.x-kubernetes-preserve-unknown-fields: true 替代顶层字段,实现更细粒度的控制。
下图对比了严格验证与宽松模式的处理流程:
preserveUnknownFields: true 快速迭代,但进入生产环境前必须切换到严格模式,并补全所有字段的 Schema 定义。Operator SDK 默认生成的 CRD 模板采用严格模式,建议保持此配置。
2.5 CRD 状态子资源与结构验证联动
CRD 支持 subresources.status 子资源,允许控制器独立更新资源状态而不触发完整的校验逻辑。状态子资源的结构验证规则如下:
- 状态字段在
spec.versions[*].schema.openAPIV3Schema中定义,但更新状态时只校验状态部分。 - 状态子资源默认只接受
status字段的变更,spec和metadata的修改会被忽略。 - 控制器通过
UpdateStatus方法更新状态,避免与用户对 spec 的修改产生冲突。
以下是一个包含状态子资源的 CRD 定义片段:
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
enum: ["True", "False", "Unknown"]
subresources:
status: {}
ValidateCreate 和 ValidateUpdate 接口提供了自定义校验入口,建议在控制器中实现更严格的业务规则。