用户自定义资源的版本

本页介绍如何添加版本信息到 CustomResourceDefinitions, 如何表示 CustomResourceDefinitions 的稳定水平或者用 API 之间的表征的转换提高您的 API 到一个新的版本。 本页还描述如何将对象从一个版本升级到另一个版本。

准备开始

你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 如果你还没有集群,你可以通过 Minikube 构建一 个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:

你应该对自定义资源 有一些初步了解。

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

概览

CustomResourceDefinition API 提供了用于引入和升级的工作流程到 CustomResourceDefinition 的新版本。

创建 CustomResourceDefinition 时,会在 CustomResourceDefinition spec.versions 列表设置适当的稳定级和版本号。例如v1beta1表示第一个版本尚未稳定。所有自定义资源对象将首先存储在这个版本

创建 CustomResourceDefinition 后,客户端可以开始使用 v1beta1 API。

稍后可能需要添加新版本,例如 v1。

增加一个新版本:

  1. 选择一种转化策略。由于自定义资源对象需要能够两种版本都可用,这意味着它们有时会以与存储版本不同的版本。为了能够做到这一点, 有时必须在它们存储的版本和提供的版本。如果转换涉及结构变更,并且需要自定义逻辑,转换应该使用 webhook。如果没有结构变更, 则使用 None 默认转换策略,不同版本时只有apiVersion字段有变更。
  2. 如果使用转换 Webhook,请创建并部署转换 Webhook。希望看到更多详细信息,请参见 Webhook conversion
  3. 更新 CustomResourceDefinition,来将新版本包含在具有served:true的 spec.versions 列表。另外,设置spec.conversion字段 到所选的转换策略。如果使用转换 Webhook,请配置spec.conversion.webhookClientConfig来调用 webhook。

添加新版本后,客户端可以逐步迁移到新版本。对于某些客户而言,在使用旧版本的同时支持其他人使用新版本。

将存储的对象迁移到新版本:

  1. 请参阅 将现有对象升级到新的存储版本 章节。

对于客户来说,在将对象升级到新的存储版本之前,期间和之后使用旧版本和新版本都是安全的。

删除旧版本:

  1. 确保所有客户端都已完全迁移到新版本。kube-apiserver 可以查看日志以帮助识别仍通过进行访问的所有客户端旧版本。
  2. spec.versions列表中将旧版本的 served 设置为 false。如果任何客户端仍然意外地使用他们可能开始报告的旧版本尝试访问旧版本的自定义资源对象时出错。 如果发生这种情况,请切换回在旧版本上使用served:true,然后迁移其他客户使用新版本,然后重复此步骤。
  3. 确保已完成 将现有对象升级到新存储版本 的步骤。
    1. 在 CustomResourceDefinition 的spec.versions列表中,确认新版本的stored已设置为true
    2. 确认旧版本不再列在 CustomResourceDefinition status.storedVersions
  4. 从 CustomResourceDefinitionspec.versions 列表中删除旧版本。
  5. 在转换 webhooks 中放弃对旧版本的转换支持。

指定多个版本

CustomResourceDefinition API 的versions字段可用于支持您自定义资源的多个版本已经开发的。版本可以具有不同的架构,并且转换 Webhooks 可以在版本之间转换自定义资源。 在适用的情况下,Webhook 转换应遵循 Kubernetes API

说明:apiextensions.k8s.io/v1beta1 版本中,有一个 version 字段,名字不叫做 versionsversion 字段已经被废弃,成为可选项。不过如果该字段不是空,则必须与 versions 字段中的第一个条目匹配。

此示例显示了两个版本的 CustomResourceDefinition。第一个例子,假设所有的版本共享相同的模式而它们之间没有转换。YAML 中的评论提供了更多背景信息。

  1. apiVersion: apiextensions.k8s.io/v1
  2. kind: CustomResourceDefinition
  3. metadata:
  4. # name must match the spec fields below, and be in the form: <plural>.<group>
  5. name: crontabs.example.com
  6. spec:
  7. # group name to use for REST API: /apis/<group>/<version>
  8. group: example.com
  9. # list of versions supported by this CustomResourceDefinition
  10. versions:
  11. - name: v1beta1
  12. # Each version can be enabled/disabled by Served flag.
  13. served: true
  14. # One and only one version must be marked as the storage version.
  15. storage: true
  16. # A schema is required
  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. host:
  33. type: string
  34. port:
  35. type: string
  36. # The conversion section is introduced in Kubernetes 1.13+ with a default value of
  37. # None conversion (strategy sub-field set to None).
  38. conversion:
  39. # None conversion assumes the same schema for all versions and only sets the apiVersion
  40. # field of custom resources to the proper value
  41. strategy: None
  42. # either Namespaced or Cluster
  43. scope: Namespaced
  44. names:
  45. # plural name to be used in the URL: /apis/<group>/<version>/<plural>
  46. plural: crontabs
  47. # singular name to be used as an alias on the CLI and for display
  48. singular: crontab
  49. # kind is normally the CamelCased singular type. Your resource manifests use this.
  50. kind: CronTab
  51. # shortNames allow shorter string to match your resource on the CLI
  52. shortNames:
  53. - ct
  1. # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  2. apiVersion: apiextensions.k8s.io/v1beta1
  3. kind: CustomResourceDefinition
  4. metadata:
  5. # name must match the spec fields below, and be in the form: <plural>.<group>
  6. name: crontabs.example.com
  7. spec:
  8. # group name to use for REST API: /apis/<group>/<version>
  9. group: example.com
  10. # list of versions supported by this CustomResourceDefinition
  11. versions:
  12. - name: v1beta1
  13. # Each version can be enabled/disabled by Served flag.
  14. served: true
  15. # One and only one version must be marked as the storage version.
  16. storage: true
  17. - name: v1
  18. served: true
  19. storage: false
  20. validation:
  21. openAPIV3Schema:
  22. type: object
  23. properties:
  24. host:
  25. type: string
  26. port:
  27. type: string
  28. # The conversion section is introduced in Kubernetes 1.13+ with a default value of
  29. # None conversion (strategy sub-field set to None).
  30. conversion:
  31. # None conversion assumes the same schema for all versions and only sets the apiVersion
  32. # field of custom resources to the proper value
  33. strategy: None
  34. # either Namespaced or Cluster
  35. scope: Namespaced
  36. names:
  37. # plural name to be used in the URL: /apis/<group>/<version>/<plural>
  38. plural: crontabs
  39. # singular name to be used as an alias on the CLI and for display
  40. singular: crontab
  41. # kind is normally the CamelCased singular type. Your resource manifests use this.
  42. kind: CronTab
  43. # shortNames allow shorter string to match your resource on the CLI
  44. shortNames:
  45. - ct

你可以将 CustomResourceDefinition 存储在 YAML 文件中,然后使用kubectl apply来创建它。

  1. kubectl apply -f my-versioned-crontab.yaml

在创建之后,apiserver 开始在 HTTP REST 端点上为每个启用的版本提供服务。 在上面的示例中,API 版本可以在/apis/example.com/v1beta1/apis/example.com/v1中获得。

版本优先级

不考虑 CustomResourceDefinition 中版本被定义的顺序,kubectl 使用具有最高优先级的版本作为访问对象的默认版本。 通过解析 name 字段确定优先级来决定版本号,稳定性(GA,Beta,或者 Alpha),以及该稳定性水平内的序列。

用于对版本进行排序的算法被设计成与 Kubernetes 项目对 Kubernetes 版本进行排序的方式相同。 版本以v开头跟一个数字,一个可选的beta 或者 alpha命名,和一个可选的附加的数字型的版本信息。 从广义上讲,版本字符串可能看起来像v2或者v2beta1。 使用以下算法对版本进行排序:

  • 遵循 Kubernetes 版本模式的条目在不符合条件的条目之前进行排序。
  • 对于遵循 Kubernetes 版本模式的条目,版本字符串的数字部分从最大到最小排序。
  • 如果字符串 betaalpha 跟随第一数字部分,它们按顺序排序,在没有 betaalpha 后缀(假定为 GA 版本)的等效字符串后面。
  • 如果另一个数字跟在betaalpha之后,那么这些数字也是从最大到最小排序。
  • 不符合上述格式的字符串按字母顺序排序,数字部分不经过特殊处理。 请注意,在下面的示例中,foo1foo10上方排序。这与遵循 Kubernetes 版本模式的条目的数字部分排序不同。

如果查看以下排序版本列表可以明白:

  1. - v10
  2. - v2
  3. - v1
  4. - v11beta2
  5. - v10beta3
  6. - v3beta1
  7. - v12alpha1
  8. - v11alpha2
  9. - foo1
  10. - foo10

对于 指定多个版本 中的示例,版本排序顺序为 v1,后跟着 v1beta1。 这导致了 kubectl 命令使用 v1 作为默认版本,除非提供对象指定版本。

Webhook转换

FEATURE STATE: Kubernetes v1.16 [stable]

说明: Webhook 转换在 Kubernetes 1.15 中作为 beta 功能。 要使用它,应启用CustomResourceWebhookConversion功能。 在大多数集群上,这类 beta 特性应该时自动启用的。 请参阅特行门控 文档以获得更多信息。

上面的例子在版本之间有一个 None 转换,它只在转换时设置apiVersion字段而不改变对象的其余部分。apiserver 还支持在需要转换时调用外部服务的 webhook 转换。例如:

  • 自定义资源被要求在一个不同的版本里而不是一个存储的版本里。
  • Watch 在一个版本中创建,但更改对象存储在另一个版本中。
  • 自定义资源 PUT 请求在一个不同的版本里而不是一个存储的版本。

为了涵盖所有这些情况并通过 API 服务优化转换,转换对象可能包含多个对象,以便最大限度地减少外部调用。webhook 应该独立执行这些转换。

编写一个转换 Webhook 服务器

请参考自定义资源转换 webhook 服务 的实施,这在 Kubernetes e2e 测试中得到验证。webhook 处理由 apiserver 发送的ConversionReview请求,并发送回包含在ConversionResponse中的转换结果。请注意,请求包含需要独立转换不改变对象顺序的自定义资源列表。示例服务器的组织方式使其可以重用于其他转换。大多数常见代码都位于 框架文件 中,只留下 示例 用于实施不同的转换。

说明:

示例转换 webhook 服务器留下ClientAuth字段为 , 默认为NoClientCert

这意味着 webhook 服务器没有验证客户端的身份,据称是 apiserver。

如果您需要相互 TLS 或者其他方式来验证客户端,请参阅如何 验证 API 服务

部署转换 Webhook 服务

用于部署转换 webhook 的文档与 准入webhook示例服务。 下一节的假设是转换 webhook 服务器部署到default命名空间中名为example-conversion-webhook-server的服务器上,并在路径/crdconvert上提供流量。

说明: 当 webhook 服务器作为一个部署到 Kubernetes 集群中的服务器时,它必须通过端口443上的服务器公开(服务器本身可以有一个任意端口,但是服务器对象应该将它映射到端口443)。 如果为服务器使用不同的端口,则 apiserver 和 webhook 服务器之间的通信可能会失败。

配置 CustomResourceDefinition 以使用转换 Webhook

通过修改 spec 中的 conversion 部分,可以扩展 None 转换示例来使用转换 webhook。

  1. apiVersion: apiextensions.k8s.io/v1
  2. kind: CustomResourceDefinition
  3. metadata:
  4. # name must match the spec fields below, and be in the form: <plural>.<group>
  5. name: crontabs.example.com
  6. spec:
  7. # group name to use for REST API: /apis/<group>/<version>
  8. group: example.com
  9. # list of versions supported by this CustomResourceDefinition
  10. versions:
  11. - name: v1beta1
  12. # Each version can be enabled/disabled by Served flag.
  13. served: true
  14. # One and only one version must be marked as the storage version.
  15. storage: true
  16. # Each version can define it's own schema when there is no top-level
  17. # schema is defined.
  18. schema:
  19. openAPIV3Schema:
  20. type: object
  21. properties:
  22. hostPort:
  23. type: string
  24. - name: v1
  25. served: true
  26. storage: false
  27. schema:
  28. openAPIV3Schema:
  29. type: object
  30. properties:
  31. host:
  32. type: string
  33. port:
  34. type: string
  35. conversion:
  36. # a Webhook strategy instruct API server to call an external webhook for any conversion between custom resources.
  37. strategy: Webhook
  38. # webhook is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server.
  39. webhook:
  40. # conversionReviewVersions indicates what ConversionReview versions are understood/preferred by the webhook.
  41. # The first version in the list understood by the API server is sent to the webhook.
  42. # The webhook must respond with a ConversionReview object in the same version it received.
  43. conversionReviewVersions: ["v1","v1beta1"]
  44. clientConfig:
  45. service:
  46. namespace: default
  47. name: example-conversion-webhook-server
  48. path: /crdconvert
  49. caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  50. # either Namespaced or Cluster
  51. scope: Namespaced
  52. names:
  53. # plural name to be used in the URL: /apis/<group>/<version>/<plural>
  54. plural: crontabs
  55. # singular name to be used as an alias on the CLI and for display
  56. singular: crontab
  57. # kind is normally the CamelCased singular type. Your resource manifests use this.
  58. kind: CronTab
  59. # shortNames allow shorter string to match your resource on the CLI
  60. shortNames:
  61. - ct
  1. # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  2. apiVersion: apiextensions.k8s.io/v1beta1
  3. kind: CustomResourceDefinition
  4. metadata:
  5. # name must match the spec fields below, and be in the form: <plural>.<group>
  6. name: crontabs.example.com
  7. spec:
  8. # group name to use for REST API: /apis/<group>/<version>
  9. group: example.com
  10. # prunes object fields that are not specified in OpenAPI schemas below.
  11. preserveUnknownFields: false
  12. # list of versions supported by this CustomResourceDefinition
  13. versions:
  14. - name: v1beta1
  15. # Each version can be enabled/disabled by Served flag.
  16. served: true
  17. # One and only one version must be marked as the storage version.
  18. storage: true
  19. # Each version can define it's own schema when there is no top-level
  20. # schema is defined.
  21. schema:
  22. openAPIV3Schema:
  23. type: object
  24. properties:
  25. hostPort:
  26. type: string
  27. - name: v1
  28. served: true
  29. storage: false
  30. schema:
  31. openAPIV3Schema:
  32. type: object
  33. properties:
  34. host:
  35. type: string
  36. port:
  37. type: string
  38. conversion:
  39. # a Webhook strategy instruct API server to call an external webhook for any conversion between custom resources.
  40. strategy: Webhook
  41. # webhookClientConfig is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server.
  42. webhookClientConfig:
  43. service:
  44. namespace: default
  45. name: example-conversion-webhook-server
  46. path: /crdconvert
  47. caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  48. # either Namespaced or Cluster
  49. scope: Namespaced
  50. names:
  51. # plural name to be used in the URL: /apis/<group>/<version>/<plural>
  52. plural: crontabs
  53. # singular name to be used as an alias on the CLI and for display
  54. singular: crontab
  55. # kind is normally the CamelCased singular type. Your resource manifests use this.
  56. kind: CronTab
  57. # shortNames allow shorter string to match your resource on the CLI
  58. shortNames:
  59. - ct

您可以将 CustomResourceDefinition 保存在 YAML 文件中,然后使用kubectl apply来应用它。

  1. kubectl apply -f my-versioned-crontab-with-conversion.yaml

在应用新更改之前,请确保转换服务器已启动并正在运行。

调用 Webhook

apiserver 一旦确定请求应发送到转换 webhook,它需要知道如何调用 webhook。这是在webhookClientConfig中指定的 webhook 配置。

转换 webhook 可以通过调用 URL 或服务,并且可以选择包含自定义 CA 包,以用于验证 TLS 连接。

URL

url 以标准 URL 形式给出 webhook 的位置(scheme://host:port/path)。 host不应引用集群中运行的服务;采用通过指定service字段来提供服务引用。 host可以通过某些 apiserver 中的外部 DNS 进行解析(即kube-apiserver无法解析集群内 DNS 违反分层规则)。主机也可以是 IP 地址。

请注意,除非您非常小心在所有主机上运行此 Webhook,否则 localhost 或 127.0.0.1 用作主机是风险很大的,运行一个 apiserver 可能需要对此进行调用 webhook。这样的安装很可能是不可移植的,即不容易出现在新集群中。

方案必须为https:URL 必须以https://开头。

尝试使用用户或基本身份验证,例如不允许使用user:password@。片段(#...)和查询参数(?...)也不允许。

这是配置为调用 URL 的转换 Webhook 的示例(并且期望使用系统信任根来验证 TLS 证书,因此不指定 caBundle):

  1. apiVersion: apiextensions.k8s.io/v1
  2. kind: CustomResourceDefinition
  3. ...
  4. spec:
  5. ...
  6. conversion:
  7. strategy: Webhook
  8. webhook:
  9. clientConfig:
  10. url: "https://my-webhook.example.com:9443/my-webhook-path"
  11. ...
  1. # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  2. apiVersion: apiextensions.k8s.io/v1beta1
  3. kind: CustomResourceDefinition
  4. ...
  5. spec:
  6. ...
  7. conversion:
  8. strategy: Webhook
  9. webhookClientConfig:
  10. url: "https://my-webhook.example.com:9443/my-webhook-path"
  11. ...

服务引用

webhookClientConfig内部的service段是对转换 webhook 服务的引用。如果 Webhook 在集群中运行,则应使用service而不是url。 服务名称空间和名称是必需的。端口是可选的,默认为 443。该路径是可选的,默认为/

这是一个配置为在端口1234上调用服务的 Webhook 的示例在子路径/my-path下,并针对 ServerName 验证 TLS 连接 使用自定义 CA 捆绑包的my-service-name.my-service-namespace.svc

  1. apiVersion: apiextensions.k8s.io/v1b
  2. kind: CustomResourceDefinition
  3. ...
  4. spec:
  5. ...
  6. conversion:
  7. strategy: Webhook
  8. webhook:
  9. clientConfig:
  10. service:
  11. namespace: my-service-namespace
  12. name: my-service-name
  13. path: /my-path
  14. port: 1234
  15. caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  16. ...
  1. # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  2. apiVersion: apiextensions.k8s.io/v1beta1
  3. kind: CustomResourceDefinition
  4. ...
  5. spec:
  6. ...
  7. conversion:
  8. strategy: Webhook
  9. webhookClientConfig:
  10. service:
  11. namespace: my-service-namespace
  12. name: my-service-name
  13. path: /my-path
  14. port: 1234
  15. caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  16. ...

Webhook 请求和响应

请求

向 Webhooks 发送 POST 请求,请求的内容类型为:application/json,与 APIapitensions.k8s.io API 组中的 ConversionReview API 对象一起使用序列化为 JSON 作为主体。

Webhooks 可以指定他们接受的ConversionReview对象的版本在其 CustomResourceDefinition 中使用conversionReviewVersions字段:

  1. apiVersion: apiextensions.k8s.io/v1
  2. kind: CustomResourceDefinition
  3. ...
  4. spec:
  5. ...
  6. conversion:
  7. strategy: Webhook
  8. webhook:
  9. conversionReviewVersions: ["v1", "v1beta1"]
  10. ...

创建时,conversionReviewVersions是必填字段apiextensions.k8s.io/v1自定义资源定义。 需要 Webhooks 支持至少一个ConversionReview当前和以前的 apiserver 可以理解的版本。

  1. # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  2. apiVersion: apiextensions.k8s.io/v1beta1
  3. kind: CustomResourceDefinition
  4. ...
  5. spec:
  6. ...
  7. conversion:
  8. strategy: Webhook
  9. conversionReviewVersions: ["v1", "v1beta1"]
  10. ...

如果未指定conversionReviewVersions,则创建时的默认值 apiextensions.k8s.io/v1beta1 自定义资源定义为 v1beta1。

API servers send the first ConversionReview version in the conversionReviewVersions list they support. If none of the versions in the list are supported by the API server, the custom resource definition will not be allowed to be created. If an API server encounters a conversion webhook configuration that was previously created and does not support any of the ConversionReview versions the API server knows how to send, attempts to call to the webhook will fail.

此示例显示了包含在ConversionReview对象中的数据请求将CronTab对象转换为example.com/v1

  1. {
  2. "apiVersion": "apiextensions.k8s.io/v1",
  3. "kind": "ConversionReview",
  4. "request": {
  5. # Random uid uniquely identifying this conversion call
  6. "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
  7. # The API group and version the objects should be converted to
  8. "desiredAPIVersion": "example.com/v1",
  9. # The list of objects to convert.
  10. # May contain one or more objects, in one or more versions.
  11. "objects": [
  12. {
  13. "kind": "CronTab",
  14. "apiVersion": "example.com/v1beta1",
  15. "metadata": {
  16. "creationTimestamp": "2019-09-04T14:03:02Z",
  17. "name": "local-crontab",
  18. "namespace": "default",
  19. "resourceVersion": "143",
  20. "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
  21. },
  22. "hostPort": "localhost:1234"
  23. },
  24. {
  25. "kind": "CronTab",
  26. "apiVersion": "example.com/v1beta1",
  27. "metadata": {
  28. "creationTimestamp": "2019-09-03T13:02:01Z",
  29. "name": "remote-crontab",
  30. "resourceVersion": "12893",
  31. "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
  32. },
  33. "hostPort": "example.com:2345"
  34. }
  35. ]
  36. }
  37. }
  1. {
  2. # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  3. "apiVersion": "apiextensions.k8s.io/v1beta1",
  4. "kind": "ConversionReview",
  5. "request": {
  6. # Random uid uniquely identifying this conversion call
  7. "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
  8. # The API group and version the objects should be converted to
  9. "desiredAPIVersion": "example.com/v1",
  10. # The list of objects to convert.
  11. # May contain one or more objects, in one or more versions.
  12. "objects": [
  13. {
  14. "kind": "CronTab",
  15. "apiVersion": "example.com/v1beta1",
  16. "metadata": {
  17. "creationTimestamp": "2019-09-04T14:03:02Z",
  18. "name": "local-crontab",
  19. "namespace": "default",
  20. "resourceVersion": "143",
  21. "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
  22. },
  23. "hostPort": "localhost:1234"
  24. },
  25. {
  26. "kind": "CronTab",
  27. "apiVersion": "example.com/v1beta1",
  28. "metadata": {
  29. "creationTimestamp": "2019-09-03T13:02:01Z",
  30. "name": "remote-crontab",
  31. "resourceVersion": "12893",
  32. "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
  33. },
  34. "hostPort": "example.com:2345"
  35. }
  36. ]
  37. }
  38. }

响应

Webhooks 响应是以 200 HTTP 状态代码,Content-Type:application/json,和包含 ConversionReview 对象的主体(与发送的版本相同), 带有response节的序列,并序列化为 JSON。

如果转换成功,则 Webhook 应该返回包含以下字段的response节:

  • uid,从发送到 webhook 的request.uid复制而来
  • result,设置为{"status":"Success"}}
  • convertedObjects,包含来自request.objects的所有对象,转换为request.desiredVersion

Webhook 的最简单成功响应示例:

  1. {
  2. "apiVersion": "apiextensions.k8s.io/v1",
  3. "kind": "ConversionReview",
  4. "response": {
  5. # must match <request.uid>
  6. "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
  7. "result": {
  8. "status": "Success"
  9. },
  10. # Objects must match the order of request.objects, and have apiVersion set to <request.desiredAPIVersion>.
  11. # kind, metadata.uid, metadata.name, and metadata.namespace fields must not be changed by the webhook.
  12. # metadata.labels and metadata.annotations fields may be changed by the webhook.
  13. # All other changes to metadata fields by the webhook are ignored.
  14. "convertedObjects": [
  15. {
  16. "kind": "CronTab",
  17. "apiVersion": "example.com/v1",
  18. "metadata": {
  19. "creationTimestamp": "2019-09-04T14:03:02Z",
  20. "name": "local-crontab",
  21. "namespace": "default",
  22. "resourceVersion": "143",
  23. "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
  24. },
  25. "host": "localhost",
  26. "port": "1234"
  27. },
  28. {
  29. "kind": "CronTab",
  30. "apiVersion": "example.com/v1",
  31. "metadata": {
  32. "creationTimestamp": "2019-09-03T13:02:01Z",
  33. "name": "remote-crontab",
  34. "resourceVersion": "12893",
  35. "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
  36. },
  37. "host": "example.com",
  38. "port": "2345"
  39. }
  40. ]
  41. }
  42. }
  1. {
  2. # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  3. "apiVersion": "apiextensions.k8s.io/v1beta1",
  4. "kind": "ConversionReview",
  5. "response": {
  6. # must match <request.uid>
  7. "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
  8. "result": {
  9. "status": "Failed"
  10. },
  11. # Objects must match the order of request.objects, and have apiVersion set to <request.desiredAPIVersion>.
  12. # kind, metadata.uid, metadata.name, and metadata.namespace fields must not be changed by the webhook.
  13. # metadata.labels and metadata.annotations fields may be changed by the webhook.
  14. # All other changes to metadata fields by the webhook are ignored.
  15. "convertedObjects": [
  16. {
  17. "kind": "CronTab",
  18. "apiVersion": "example.com/v1",
  19. "metadata": {
  20. "creationTimestamp": "2019-09-04T14:03:02Z",
  21. "name": "local-crontab",
  22. "namespace": "default",
  23. "resourceVersion": "143",
  24. "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
  25. },
  26. "host": "localhost",
  27. "port": "1234"
  28. },
  29. {
  30. "kind": "CronTab",
  31. "apiVersion": "example.com/v1",
  32. "metadata": {
  33. "creationTimestamp": "2019-09-03T13:02:01Z",
  34. "name": "remote-crontab",
  35. "resourceVersion": "12893",
  36. "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
  37. },
  38. "host": "example.com",
  39. "port": "2345"
  40. }
  41. ]
  42. }
  43. }

如果转换失败,则 Webhook 应该返回包含以下字段的response节: *uid,从发送到 webhook 的request.uid复制而来 *result,设置为{"status":"Failed"}

警告:

转换失败会破坏对自定义资源的读写访问,包括更新或删除资源的能力。转换失败应尽可能避免使用,并且不应用于强制验证 约束(改用验证模式 或 Webhook admission)。

来自 Webhook 的响应示例,指示转换请求失败,并带有可选消息:

  1. {
  2. "apiVersion": "apiextensions.k8s.io/v1",
  3. "kind": "ConversionReview",
  4. "response": {
  5. "uid": "<value from request.uid>",
  6. "result": {
  7. "status": "Failed",
  8. "message": "hostPort could not be parsed into a separate host and port"
  9. }
  10. }
  11. }
  1. {
  2. # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  3. "apiVersion": "apiextensions.k8s.io/v1beta1",
  4. "kind": "ConversionReview",
  5. "response": {
  6. "uid": "<value from request.uid>",
  7. "result": {
  8. "status": "Failed",
  9. "message": "hostPort could not be parsed into a separate host and port"
  10. }
  11. }
  12. }

编写、读取和更新版本化的 CustomResourceDefinition 对象

写入对象时,它将保留在写入时指定为存储版本的版本中。如果存储版本发生变化,现有对象永远不会自动转换。然而,新创建或更新的对象将在新的存储版本中编写。对象可能已在不再被服务的版本中编写。

当读取对象时,将版本指定为路径的一部分。 如果指定的版本与对象的持久版本不同,Kubernetes 会在您请求的版本里将对象返还给您,但是在提供请求时,持久化对象既不会在磁盘上更改,也不会以任何方式进行转换(除了更改apiVersion字符串)。您可以在当前提供的任何版本中请求对象。

如果您更新了一个现有对象,它将在现在的存储版本中被重写。 这是对象可以从一个版本改到另一个版本的唯一办法。

为了说明这一点,请考虑以下假设的一系列事件:

  1. 存储版本是v1beta1。 它保存在版本v1beta1的存储中。
  2. 您将版本v1添加到 CustomResourceDefinition 中,并将其指定为存储版本。
  3. 您在版本v1beta1中读取您的对象,然后您再次在版本v1中读取对象。 除了 apiVersion 字段之外,两个返回的对象都是相同的。
  4. 您创建一个新对象。 它存储在版本v1的存储中。 您现在有两个对象,其中一个位于v1beta1,另一个位于v1
  5. 您更新第一个对象。 它现在保存在版本v1中,因为那是当前的存储版本。

以前的存储版本

API 服务在状态字段storedVersions中记录曾被标记为存储版本的每个版本。 对象可能已被保留在任何曾被指定为存储版本的版本中。 从未成为存储版本的版本的存储中不能存在任何对象。

将现有对象升级到新的存储版本

弃用版本并删除支持时,请设计存储升级过程。 以下是从v1beta1升级到v1的示例过程。

  1. v1设置为 CustomResourceDefinition 文件中的存储,并使用 kubectl 应用它。 storedVersions现在是v1beta1、 v1
  2. 编写升级过程以列出所有现有对象并使用相同内容编写它们。 这会强制后端在当前存储版本中写入对象,即v1
  3. 通过从storedVersions字段中删除v1beta1来更新 CustomResourceDefinitionStatus