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 工作原理
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 版本管理最佳实践
- 版本命名规范: 遵循
v<major><minor><alpha/beta/stable>模式,如v1alpha1、v1beta1、v1。 - 存储版本选择: 始终选择最稳定、最成熟的版本作为存储版本。避免将 alpha 或 beta 版本设为存储版本。
- 转换 Webhook 健壮性: 为 Webhook 配置
failurePolicy: Fail以确保数据一致性;同时设置合理的timeoutSeconds(默认 30s)。 - 版本废弃流程: 先设置
served: false停止 serving,确认无客户端使用后再从 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 升级。