4. 自定义资源定义(CRD)版本管理

自定义资源定义(CRD)是 Kubernetes 扩展能力的基础。随着业务迭代,CRD 的 API 版本也需要演进。Kubernetes 从 1.16 开始要求 CRD 必须支持多版本管理,包括版本转换、存储版本与 serving 版本的策略选择。本章深入剖析 CRD 版本管理的核心机制与最佳实践。

4.1 CRD 多版本支持的核心概念

CRD 多版本支持允许一个自定义资源同时存在多个 API 版本(如 v1alpha1、v1beta1、v1),客户端可以按需使用不同版本进行读写操作。Kubernetes 在 etcd 中只存储一个版本(存储版本),但通过版本转换 Webhook 或内置转换策略,将其他版本的数据转换为存储版本后再持久化。

概念 说明 关键属性
存储版本(storage version) 实际写入 etcd 的版本,每个 CRD 只能指定一个版本为存储版本 storage: true
Serving 版本 API 服务器对外提供服务的版本,可以同时有多个 serving 版本 served: true
版本转换 将请求中的版本与存储版本之间进行数据格式转换 conversion 策略
转换 Webhook 通过外部 Webhook 服务实现复杂的版本转换逻辑 conversion.webhook
最佳实践: 建议在 CRD 设计初期就规划好版本演进路径。通常先以 v1alpha1 或 v1beta1 发布,待 API 稳定后再增加 v1 版本并将 v1 设为存储版本。

4.2 版本转换 Webhook 配置

当 CRD 的多个版本之间字段结构差异较大时,内置的 None 转换策略无法满足需求,必须配置版本转换 Webhook。Webhook 负责在请求到达 API 服务器时,将请求版本的数据转换为存储版本的数据格式。

4.2.1 Webhook 配置示例

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  names:
    kind: CronTab
    listKind: CronTabList
    plural: crontabs
    singular: crontab
  scope: Namespaced
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              cronSpec:
                type: string
              image:
                type: string
  - name: v1beta1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              cron:
                type: string
              containerImage:
                type: string
  conversion:
    strategy: Webhook
    webhook:
      conversionReviewVersions: ["v1", "v1beta1"]
      clientConfig:
        service:
          namespace: default
          name: crd-conversion-webhook
          path: /convert
        caBundle: <base64-encoded-ca-cert>

4.2.2 Webhook 工作原理

CRD 版本转换 Webhook 工作流程 客户端 v1beta1 请求 API Server 版本检测 转换 Webhook v1beta1 → v1 etcd 存储 v1 版本 1. 提交 v1beta1 2. 调用转换 3. 返回 v1 格式 4. 写入 v1 说明: Webhook 必须返回 ConversionReview 响应,包含转换后的对象及转换结果状态。API Server 验证通过后写入 etcd。

4.3 存储版本与 Serving 版本选择策略

存储版本和 serving 版本的选择直接影响集群的兼容性、升级成本和运维复杂度。以下是三种常见策略及其适用场景。

策略 存储版本 Serving 版本 适用场景 风险等级
保守策略 最旧稳定版本(如 v1) 所有版本 生产环境,客户端版本混杂,需保证向后兼容 低
激进策略 最新版本(如 v2) 仅最新版本 新项目,客户端统一升级,无需兼容旧版 高
渐进策略 当前稳定版本 新旧版本同时 serving,逐步废弃旧版 版本迁移期间,允许客户端逐步升级 中

4.3.1 存储版本切换注意事项

  • 存储版本变更后,etcd 中已有对象不会自动转换。需要手动或通过控制器触发重新写入,才能将旧存储版本的对象转换为新版本。
  • 转换 Webhook 必须处理所有 serving 版本到存储版本的转换。如果 Webhook 只实现了部分版本转换,会导致请求失败。
  • 建议在 CRD 定义中为每个版本提供完整的 OpenAPI v3 schema,确保 API 服务器能正确校验不同版本的请求。
警告: 切勿在集群中有大量存量对象时直接切换存储版本。必须先确保转换 Webhook 稳定可靠,并通过灰度方式逐步迁移存量对象,否则可能导致数据丢失或 API 不可用。

4.4 版本转换的两种模式对比

特性 None 转换策略 Webhook 转换策略
实现复杂度 低,无需额外服务 高,需要部署和维护 Webhook 服务
版本间字段映射 必须完全一致(字段名、类型、结构) 支持任意字段映射和转换逻辑
性能开销 无额外开销 每次请求增加网络调用和转换处理时间
适用场景 版本间仅增减字段,无结构变化 版本间字段重命名、类型变更、结构重组
高可用要求 无 Webhook 服务必须高可用,否则影响所有 CRD 操作

4.5 版本管理最佳实践

  1. 版本命名规范: 遵循 v<major><minor><alpha/beta/stable> 模式,如 v1alpha1、v1beta1、v1。
  2. 存储版本选择: 始终选择最稳定、最成熟的版本作为存储版本。避免将 alpha 或 beta 版本设为存储版本。
  3. 转换 Webhook 健壮性: 为 Webhook 配置 failurePolicy: Fail 以确保数据一致性;同时设置合理的 timeoutSeconds(默认 30s)。
  4. 版本废弃流程: 先设置 served: false 停止 serving,确认无客户端使用后再从 CRD 定义中移除该版本。
  5. 自动化测试: 为每个版本编写转换测试用例,覆盖正向转换和反向转换,确保数据不丢失。
CRD 版本生命周期管理 v1alpha1 开发阶段 v1beta1 预览阶段 v1 稳定阶段 v1 (废弃) 停止 serving 存储版本 版本演进建议: 1. v1alpha1 → v1beta1:API 不稳定,可随时变更,不保证兼容性。 2. v1beta1 → v1:API 稳定后发布 v1,同时保留 v1beta1 serving 以便客户端迁移。 3. v1 废弃:确认无客户端使用后,设置 served: false,最终从 CRD 定义中移除。

4.6 常见问题与排查

问题现象 可能原因 解决方案
创建 CR 时报错 no conversion webhook configured CRD 定义了多个版本但未配置转换 Webhook 为 CRD 添加 conversion.webhook 配置,或确保所有版本 schema 兼容
Webhook 调用超时 Webhook 服务响应慢或网络延迟高 优化 Webhook 处理逻辑,增加 timeoutSeconds,或升级 Webhook 实例规格
读取 CR 时字段丢失 存储版本与 serving 版本字段映射不完整 检查 Webhook 转换逻辑,确保所有字段都被正确映射
无法切换存储版本 etcd 中已有对象未迁移 使用控制器或脚本遍历所有对象并重新写入,触发存储版本更新
核心要点: CRD 版本管理的本质是在 API 演进与数据一致性之间取得平衡。存储版本决定数据持久化格式,serving 版本决定客户端交互接口,转换 Webhook 是连接两者的桥梁。合理规划版本生命周期,才能实现平滑的 API 升级。