使用存储版本迁移功能来迁移 Kubernetes 对象

特性状态: Kubernetes v1.30 [alpha]

Kubernetes 依赖主动重写的 API 数据来支持与静态存储相关的一些维护活动。 两个著名的例子是已存储资源的版本化模式(即针对给定资源的首选存储模式从 v1 更改为 v2) 和静态加密(即基于数据加密方式的变化来重写过时的数据)。

准备开始

安装 kubectl

你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:

你的 Kubernetes 服务器版本必须不低于版本 v1.30. 要获知版本信息,请输入 kubectl version.

确保你的集群启用了 StorageVersionMigratorInformerResourceVersion 特性门控。 你需要有控制平面管理员权限才能执行此项变更。

在 API 服务器上将运行时配置 storagemigration.k8s.io/v1alpha1 设为 true,启用存储版本迁移 REST API。 有关如何执行此操作的更多信息,请阅读启用或禁用 Kubernetes API

使用存储版本迁移来重新加密 Kubernetes Secret

  • 首先配置 KMS 驱动, 以便使用如下加密配置来加密 etcd 中的静态数据。

    1. kind: EncryptionConfiguration
    2. apiVersion: apiserver.config.k8s.io/v1
    3. resources:
    4. - resources:
    5. - secrets
    6. providers:
    7. - aescbc:
    8. keys:
    9. - name: key1
    10. secret: c2VjcmV0IGlzIHNlY3VyZQ==

    确保通过将 --encryption-provider-config-automatic-reload 设置为 true,允许自动重新加载加密配置文件。

  • 使用 kubectl 创建 Secret。

    1. kubectl create secret generic my-secret --from-literal=key1=supersecret
  • 验证该 Secret 对象的序列化数据带有前缀 k8s:enc:aescbc:v1:key1

  • 按照以下方式更新加密配置文件,以轮换加密密钥。

    1. kind: EncryptionConfiguration
    2. apiVersion: apiserver.config.k8s.io/v1
    3. resources:
    4. - resources:
    5. - secrets
    6. providers:
    7. - aescbc:
    8. keys:
    9. - name: key2
    10. secret: c2VjcmV0IGlzIHNlY3VyZSwgaXMgaXQ/
    11. - aescbc:
    12. keys:
    13. - name: key1
    14. secret: c2VjcmV0IGlzIHNlY3VyZQ==
  • 要确保之前创建的 Secret my-secert 使用新密钥 key2 进行重新加密,你将使用存储版本迁移

  • 创建以下名为 migrate-secret.yaml 的 StorageVersionMigration 清单:

    1. kind: StorageVersionMigration
    2. apiVersion: storagemigration.k8s.io/v1alpha1
    3. metadata:
    4. name: secrets-migration
    5. spec:
    6. resource:
    7. group: ""
    8. version: v1
    9. resource: secrets

    使用以下 kubectl 命令创建对象:

    1. kubectl apply -f migrate-secret.yaml
  • 通过检查 StorageVersionMigration 的 .status 来监控 Secret 的迁移。 成功的迁移应将其 Succeeded 状况设置为 true。 获取 StorageVersionMigration 对象的方式如下:

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

    输出类似于:

    1. kind: StorageVersionMigration
    2. apiVersion: storagemigration.k8s.io/v1alpha1
    3. metadata:
    4. name: secrets-migration
    5. uid: 628f6922-a9cb-4514-b076-12d3c178967c
    6. resourceVersion: "90"
    7. creationTimestamp: "2024-03-12T20:29:45Z"
    8. spec:
    9. resource:
    10. group: ""
    11. version: v1
    12. resource: secrets
    13. status:
    14. conditions:
    15. - type: Running
    16. status: "False"
    17. lastUpdateTime: "2024-03-12T20:29:46Z"
    18. reason: StorageVersionMigrationInProgress
    19. - type: Succeeded
    20. status: "True"
    21. lastUpdateTime: "2024-03-12T20:29:46Z"
    22. reason: StorageVersionMigrationSucceeded
    23. resourceVersion: "84"
  • 验证存储的 Secret 现在带有前缀 k8s:enc:aescbc:v1:key2

更新 CRD 的首选存储模式

考虑这样一种情况: 用户创建了 CustomResourceDefinition (CRD) 来提供自定义资源 (CR),并将其设置为首选的存储模式。 当需要引入 CRD 的 v2 版本时,只需提供转换 Webhook 就可以为 v2 版本提供服务。 基于转换 Webhook 的方式能够实现更平滑的过渡,用户可以使用 v1 或 v2 模式创建 CR,并通过合适的 Webhook 执行必要的模式转换。 在将 v2 设置为首选的存储模式版本之前,重要的是要确保将当前已存储为 v1 的所有 CR 已被迁移到 v2。 这种迁移可以通过使用存储版本迁移将所有 CR 从 v1 迁移到 v2 来达成。

  • 如下针对名为 test-crd.yaml 的 CRD 创建一个清单:

    1. apiVersion: apiextensions.k8s.io/v1
    2. kind: CustomResourceDefinition
    3. metadata:
    4. name: selfierequests.stable.example.com
    5. spec:
    6. group: stable.example.com
    7. names:
    8. plural: SelfieRequests
    9. singular: SelfieRequest
    10. kind: SelfieRequest
    11. listKind: SelfieRequestList
    12. scope: Namespaced
    13. versions:
    14. - name: v1
    15. served: true
    16. storage: true
    17. schema:
    18. openAPIV3Schema:
    19. type: object
    20. properties:
    21. hostPort:
    22. type: string
    23. conversion:
    24. strategy: Webhook
    25. webhook:
    26. clientConfig:
    27. url: https://127.0.0.1:9443/crdconvert
    28. caBundle: <CABundle info>
    29. conversionReviewVersions:
    30. - v1
    31. - v2

    使用 kubectl 创建 CRD:

    1. kubectl apply -f test-crd.yaml
  • 为 testcrd 示例创建一个清单。命名为 cr1.yaml 并使用以下内容:

    1. apiVersion: stable.example.com/v1
    2. kind: SelfieRequest
    3. metadata:
    4. name: cr1
    5. namespace: default

    使用 kubectl 创建 CR:

    1. kubectl apply -f cr1.yaml
  • 通过从 etcd 获取对象来验证 CR 是否以 v1 格式被写入和存储。

    1. ETCDCTL_API=3 etcdctl get /kubernetes.io/stable.example.com/testcrds/default/cr1 [...] | hexdump -C

    其中 [...] 包含连接到 etcd 服务器的额外参数。

  • 如下更新 CRD test-crd.yaml,将 v2 版本设置为 served 和 storage,并将 v1 设置为仅 served:

    1. apiVersion: apiextensions.k8s.io/v1
    2. kind: CustomResourceDefinition
    3. metadata:
    4. name: selfierequests.stable.example.com
    5. spec:
    6. group: stable.example.com
    7. names:
    8. plural: SelfieRequests
    9. singular: SelfieRequest
    10. kind: SelfieRequest
    11. listKind: SelfieRequestList
    12. scope: Namespaced
    13. versions:
    14. - name: v2
    15. served: true
    16. storage: true
    17. schema:
    18. openAPIV3Schema:
    19. type: object
    20. properties:
    21. host:
    22. type: string
    23. port:
    24. type: string
    25. - name: v1
    26. served: true
    27. storage: false
    28. schema:
    29. openAPIV3Schema:
    30. type: object
    31. properties:
    32. hostPort:
    33. type: string
    34. conversion:
    35. strategy: Webhook
    36. webhook:
    37. clientConfig:
    38. url: "https://127.0.0.1:9443/crdconvert"
    39. caBundle: <CABundle info>
    40. conversionReviewVersions:
    41. - v1
    42. - v2

    使用 kubectl 更新 CRD:

    1. kubectl apply -f test-crd.yaml
  • 如下创建名为 cr2.yaml 的 CR 资源文件:

    1. apiVersion: stable.example.com/v2
    2. kind: SelfieRequest
    3. metadata:
    4. name: cr2
    5. namespace: default
  • 使用 kubectl 创建 CR:

    1. kubectl apply -f cr2.yaml
  • 通过从 etcd 获取对象来验证 CR 是否以 v2 格式被写入和存储。

    1. ETCDCTL_API=3 etcdctl get /kubernetes.io/stable.example.com/testcrds/default/cr2 [...] | hexdump -C

    其中 [...] 包含连接到 etcd 服务器的额外参数。

  • 如下创建名为 migrate-crd.yaml 的 StorageVersionMigration 清单:

    1. kind: StorageVersionMigration
    2. apiVersion: storagemigration.k8s.io/v1alpha1
    3. metadata:
    4. name: crdsvm
    5. spec:
    6. resource:
    7. group: stable.example.com
    8. version: v1
    9. resource: SelfieRequest

    使用如下 kubectl 命令创建此对象:

    1. kubectl apply -f migrate-crd.yaml
  • 使用 status 监控 Secret 的迁移。 若迁移成功,应在 status 字段中将 Succeeded 状况设置为 “True”。 获取迁移资源的方式如下:

    1. kubectl get storageversionmigration.storagemigration.k8s.io/crdsvm -o yaml

    输出类似于:

    1. kind: StorageVersionMigration
    2. apiVersion: storagemigration.k8s.io/v1alpha1
    3. metadata:
    4. name: crdsvm
    5. uid: 13062fe4-32d7-47cc-9528-5067fa0c6ac8
    6. resourceVersion: "111"
    7. creationTimestamp: "2024-03-12T22:40:01Z"
    8. spec:
    9. resource:
    10. group: stable.example.com
    11. version: v1
    12. resource: testcrds
    13. status:
    14. conditions:
    15. - type: Running
    16. status: "False"
    17. lastUpdateTime: "2024-03-12T22:40:03Z"
    18. reason: StorageVersionMigrationInProgress
    19. - type: Succeeded
    20. status: "True"
    21. lastUpdateTime: "2024-03-12T22:40:03Z"
    22. reason: StorageVersionMigrationSucceeded
    23. resourceVersion: "106"
  • 通过从 etcd 获取对象来验证之前创建的 cr1 是否现在以 v2 格式被写入和存储。

    1. ETCDCTL_API=3 etcdctl get /kubernetes.io/stable.example.com/testcrds/default/cr1 [...] | hexdump -C

    其中 [...] 包含连接到 etcd 服务器的额外参数。