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 策略校验需求:

  1. 创建(Creation):首次提交 CRD 定义到集群,kube-apiserver 注册新的 REST 端点。
  2. 版本演进(Versioning):添加/废弃版本,涉及存储版本转换和 schema 兼容性。
  3. 更新(Update):修改 CRD 的 schema、子资源或打印列,可能影响已有 CR 实例。
  4. 删除(Deletion):删除 CRD 前需确保关联 CR 已清理,否则数据残留。
OPA 集成要点: CRD 的创建和更新操作本身也应纳入策略校验范围。例如,禁止创建 scope: Cluster 的 CRD、限制 schema 中字段类型的使用、强制要求所有 CRD 包含 status 子资源等。
CRD 生命周期与 OPA 策略校验介入点 创建 CRD apiextensions/v1 版本演进 storage/served 更新 CRD schema 变更 删除 CRD 级联清理 OPA 策略校验介入点 AdmissionReview → 校验 CRD 定义合规性 → 校验 CR 实例变更 → 校验版本兼容性 CR 实例(Custom Resource) 每个 CR 实例受 CRD schema 约束,OPA 在 CR 创建/更新时进行策略校验

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 版本的字段结构。
CRD 变更 → OPA 策略影响链路 CRD 变更 Schema / 版本 / 作用域 字段增删改 / 类型变化 影响路径 ① CR 实例数据不一致 ② API 端点路径变化 ③ 字段类型/结构变化 OPA 策略后果 规则失效 / 类型错误 运行时 panic / 误判 策略覆盖率下降 自动化集成方案应对策略 CRD 变更触发 OPA 策略重新加载 → 策略版本与 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 策略校验自动化集成方案的基础。下一环节将基于这些知识,设计具体的策略规则和校验流程。