3. CRD(自定义资源)基础:CRD定义、CRD生命周期、CRD变更场景分析
3.1 自定义资源定义(CRD)核心概念
CRD(Custom Resource Definition)是 Kubernetes 扩展机制的核心,允许用户定义集群中不存在的资源类型。CRD 本身是一个 API 资源,通过声明式 YAML 定义新资源的结构、验证规则和版本策略。
关键认知: CRD 是“资源的资源”——它定义了新资源类型的 schema,而 Custom Resource(CR)则是该 schema 的具体实例。OPA 策略校验的对象通常是 CR 的变更请求,但策略本身也需要理解 CRD 的结构才能正确评估。
3.1.1 CRD 定义结构
一个标准的 CRD 定义包含以下核心字段:
| 字段路径 | 说明 | 是否必填 |
|---|---|---|
spec.group |
API 组名称,如 example.com |
是 |
spec.names.kind |
资源类型名称,如 MyApp |
是 |
spec.names.plural |
复数名称,用于 API 路径,如 myapps |
是 |
spec.scope |
作用域:Namespaced 或 Cluster |
是 |
spec.versions |
版本列表,包含 schema、子资源、转换策略 | 是 |
spec.versions[].schema.openAPIV3Schema |
OpenAPI v3 格式的校验 schema | 是 |
spec.versions[].subresources |
子资源(status/scale)声明 | 否 |
spec.versions[].additionalPrinterColumns |
kubectl 输出额外列 | 否 |
# 示例:一个简单的 CRD 定义
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
enabled:
type: boolean
subresources:
status: {}
3.2 CRD 生命周期管理
CRD 的生命周期分为四个阶段,每个阶段都可能触发 OPA 策略校验需求:
- 创建(Creation):首次提交 CRD 定义到集群,kube-apiserver 注册新的 REST 端点。
- 版本演进(Versioning):添加/废弃版本,涉及存储版本转换和 schema 兼容性。
- 更新(Update):修改 CRD 的 schema、子资源或打印列,可能影响已有 CR 实例。
- 删除(Deletion):删除 CRD 前需确保关联 CR 已清理,否则数据残留。
OPA 集成要点: CRD 的创建和更新操作本身也应纳入策略校验范围。例如,禁止创建
scope: Cluster 的 CRD、限制 schema 中字段类型的使用、强制要求所有 CRD 包含 status 子资源等。
3.3 CRD 变更场景分析
在实际运维中,CRD 变更会触发多种连锁反应。以下按变更类型分类,分析对已有 CR 实例和 OPA 策略的影响:
3.3.1 Schema 变更场景
| 变更类型 | 对已有 CR 的影响 | OPA 策略关注点 |
|---|---|---|
| 新增可选字段 | 无影响,CR 保持原状 | 策略需兼容新旧 schema |
| 新增必填字段 | 已有 CR 缺少该字段,可能被拒绝 | 需设置 preserveUnknownFields: false 并处理默认值 |
| 修改字段类型 | 已有 CR 数据可能无法解析 | 策略应检测类型不兼容变更 |
| 删除字段 | 已有 CR 中该字段数据丢失 | 策略需评估数据丢失风险 |
| 收紧校验规则(如 minimum 从 1 改为 3) | 已有 CR 可能违反新规则 | 策略应允许存量 CR 豁免或触发重新校验 |
3.3.2 版本变更场景
CRD 支持多版本共存,通过 conversion 策略实现版本间数据转换:
- 存储版本切换:修改
spec.versions[].storage: true,etcd 中所有 CR 数据需转换到新版本。 - 版本废弃:设置
served: false,旧版本 API 端点关闭,但已有 CR 仍可读取。 - Webhook 转换:通过 ConversionReview 请求调用外部转换服务,OPA 可在此处校验转换结果。
常见陷阱: 修改 CRD 的
spec.names(如重命名 kind)会导致已有 CR 无法通过新名称访问,且 OPA 策略中硬编码的资源类型名称需要同步更新。建议在 CRD 设计阶段就确定名称,避免后期修改。
3.3.3 作用域变更场景
CRD 的 scope 字段在创建后不可修改。如果业务需要从 Namespaced 改为 Cluster,只能新建 CRD 并迁移数据。OPA 策略应提前检测此类不兼容变更:
# OPA Rego 示例:禁止修改 CRD 的 scope
deny[msg] {
input.request.kind.kind == "CustomResourceDefinition"
input.request.operation == "UPDATE"
old_scope := input.old_object.spec.scope
new_scope := input.object.spec.scope
old_scope != new_scope
msg := sprintf("CRD scope 不可修改:从 %v 改为 %v", [old_scope, new_scope])
}
3.4 CRD 变更对 OPA 策略体系的影响
CRD 变更不仅影响 CR 实例,还会影响 OPA 策略本身的运行:
- 策略规则失效:CRD 字段路径变化导致 Rego 规则中的
input.object.spec路径不匹配。 - 类型断言错误:字段类型从 string 改为 integer 时,Rego 的类型检查会报错。
- 缺失字段引用:策略引用了被删除的字段,导致运行时 panic。
- 版本兼容性:策略需要同时处理多个 CRD 版本的字段结构。
3.5 最佳实践与避坑指南
设计建议: 在 CRD 的 OpenAPI schema 中充分利用
default、nullable、oneOf 等关键字,减少因字段缺失导致的策略校验失败。OPA 策略应优先使用 input.object.spec 的通用路径,避免硬编码具体字段名。
避坑清单:
- 不要在生产集群直接修改 CRD 的
spec.versions[].schema,应通过新版本逐步迁移。 - CRD 删除前必须确认所有 CR 实例已删除,否则 etcd 中残留数据无法清理。
- OPA 策略中引用 CRD 字段时,使用
object.get()或walk()处理可选字段,避免直接索引。 - CRD 的
preserveUnknownFields设置为true时,OPA 策略无法校验未知字段,建议关闭。
理解 CRD 的定义、生命周期和变更场景,是构建 OPA 策略校验自动化集成方案的基础。下一环节将基于这些知识,设计具体的策略规则和校验流程。