Kubernetes v1.37:Storage Version Migration 默认启用后,CRD 升级更稳了

2026-09-01 43 预计阅读时间: 1 分钟
来源: kubernetes.io AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:7 分钟

Kubernetes v1.37 中,Storage Version Migration(SVM)正式达到 GA,并在所有 v1.37 集群中默认启用。它解决了一个容易被忽略但会直接影响 API 兼容性的运维问题:API 版本已经升级,etcd 中却可能仍然保存着旧版本序列化的数据。

对于 CRD 作者、集群管理员以及负责加密密钥轮换的团队来说,SVM 将过去依赖脚本或额外组件完成的数据重写,变成了一个可以声明式管理、查询状态并纳入发布流程的 Kubernetes API。

为什么“新写入使用新版本”还不够

Kubernetes 资源在写入存储时,会按照某个 storage version 进行序列化。假设一个 CRD 曾经支持 v1alpha1v1beta1,现在准备升级到 v1,并将 v1 标记为新的存储版本:

versions:
  - name: v1alpha1
    served: false
    storage: false
  - name: v1beta1
    served: true
    storage: false
  - name: v1
    served: true
    storage: true

这只能保证之后的新写入使用 v1。已经存在于存储中的对象不会自动被重写,它们可能仍以 v1alpha1v1beta1 的形式保存。

这会带来两个实际问题:

  • 在所有对象完成迁移前,不能安全地从 CRD 的 .status.storedVersions 中移除旧版本。
  • 如果直接删除旧版本的 serving 支持,API Server 可能无法正确读取仍使用旧版本序列化的数据。

类似问题也会出现在静态加密和密钥轮换中。启用 encryption at rest 或更换加密密钥后,已有对象通常要经过 API Server 的主动重写,才能从未加密状态或旧密钥迁移到新状态。

过去,管理员通常需要编写 kubectl getkubectl replace 脚本,或者部署集群外的 kube-storage-version-migrator。这些方式不仅容易漏掉资源,也不容易观察进度和判断是否真正完成。

用声明式对象启动迁移

Kubernetes v1.37 提供了稳定的 StorageVersionMigration API。内置的 StorageVersionMigrator controller 会监听这类对象,并将目标资源的已有实例重写为该 API 当前使用的默认存储版本。

下面的例子迁移 example.com API group 下的 crontabs 资源。运行前,请确认对应 CRD 已经将 v1 设置为 storage: true,并根据实际资源名称修改 manifest:

apiVersion: storagemigration.k8s.io/v1
kind: StorageVersionMigration
metadata:
  name: crontabs-migration
spec:
  resource:
    group: example.com
    resource: crontabs

保存为 crontabs-migration.yaml 后执行:

kubectl apply -f crontabs-migration.yaml
kubectl get storageversionmigration.storagemigration.k8s.io/crontabs-migration -o yaml

这里的 resource 是资源的复数形式,也就是 API 路径中的名称;它不是 CRD 的 kind。例如,CronTab 对应的资源通常是 crontabs

如果需要先确认 API Server 识别到的资源名称,可以运行:

kubectl api-resources --api-group=example.com

如何判断迁移是否完成

迁移对象的 status 会反映控制器处理进度。成功时,应看到 Succeeded condition 的 statusTrue。可以用 JSONPath 做一个适合脚本和 CI 检查的判断:

kubectl get storageversionmigration.storagemigration.k8s.io/crontabs-migration \
  -o jsonpath='{range .status.conditions[*]}{.type}={.status}{"\n"}{end}'

预期结果类似:

Running=False
Succeeded=True

也可以直接查看完整状态:

kubectl get storageversionmigration.storagemigration.k8s.io/crontabs-migration -o yaml

对于 CRD,迁移成功后,应该检查 .status.storedVersions 是否只剩下当前需要保留的版本:

kubectl get crd crontabs.example.com \
  -o jsonpath='{.status.storedVersions}{"\n"}'

如果迁移成功后仍然看到旧版本,不要直接删除旧版本。一个常见原因是 CRD 在迁移过程中又发生了更新。此时应重新检查 CRD 的 storage version 配置,并重新创建或重试对应的迁移任务,确认对象已经在最新版本下完成重写。

把迁移纳入 CRD 发布流程

StorageVersionMigration 本身是标准的声明式 Kubernetes API,因此 CRD 作者可以把 CRD 更新和迁移对象放在同一个发布清单中:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
      - ct
  versions:
    - name: v1beta1
      served: true
      storage: false
      schema:
        openAPIV3Schema:
          type: object
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
---
apiVersion: storagemigration.k8s.io/v1
kind: StorageVersionMigration
metadata:
  name: crontabs-migration
spec:
  resource:
    group: example.com
    resource: crontabs

不过,清单放在一起并不代表两个操作会以一个事务完成。生产发布流程仍应分别验证:CRD 更新是否成功、迁移对象是否成功、.status.storedVersions 是否符合预期,以及旧版本 serving 支持是否可以安全移除。

升级时的检查清单

可以把下面的步骤加入 CRD 版本升级或集群加密配置变更流程:

  1. 确认目标 API 版本已经设置为唯一或主要的 storage version。
  2. 检查 API group、resource 名称和 CRD 配置是否一致。
  3. 创建 StorageVersionMigration 对象。
  4. 等待 Succeeded=True,并记录迁移结果。
  5. 检查 CRD 的 .status.storedVersions
  6. 在确认旧版本对象已经完成重写后,再移除旧版本的 serving 支持。
  7. 在大规模集群中观察 API Server、etcd 和控制器负载,安排合适的变更窗口。

SVM 的价值不在于让版本升级变成“无感操作”,而在于提供了一条 Kubernetes 原生、可观察、可重复执行的资源重写路径。升级 CRD 时,storage version、历史对象和 serving 兼容性应该被视为同一个发布问题,而不是三个互不相关的配置项。


相关推荐