CustomResourceDefinitions 中的版本

本页面介绍如何向 CustomResourceDefinitions 添加版本信息,以指示 CustomResourceDefinitions 的稳定级别,或通过 API 表示之间的转换将 API 提升到新版本。它还描述了如何将对象从一个版本升级到另一个版本。

准备工作

你需要有一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。建议在至少有两个不作为控制平面主机的节点组成的集群上运行本教程。如果你还没有集群,你可以通过使用 minikube 创建一个,或者你可以使用这些 Kubernetes 操场之一。

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

你的 Kubernetes 服务器版本必须是 v1.16 或更高版本。

要检查版本,请输入 kubectl version

概述

CustomResourceDefinition API 提供了一个引入和升级到 CustomResourceDefinition 新版本的工作流。

创建 CustomResourceDefinition 时,第一个版本会在 CustomResourceDefinition 的 `spec.versions` 列表中设置为适当的稳定性级别和版本号。例如,`v1beta1` 表示第一个版本尚未稳定。所有自定义资源对象最初都将以此版本存储。

一旦创建了 CustomResourceDefinition,客户端就可以开始使用 `v1beta1` API。

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

添加新版本

  1. 选择一种转换策略。由于自定义资源对象需要能够以两个版本提供服务,这意味着它们有时会以不同于存储版本的版本提供服务。为了实现这一点,自定义资源对象有时必须在它们存储的版本和它们提供服务的版本之间进行转换。如果转换涉及模式更改并需要自定义逻辑,则应使用转换 Webhook。如果 SCHEMA 没有变化,则可以使用默认的 `None` 转换策略,并且在提供不同版本时只修改 `apiVersion` 字段。
  2. 如果使用转换 Webhook,请创建并部署转换 Webhook。有关详细信息,请参阅Webhook 转换
  3. 更新 CustomResourceDefinition,在 `spec.versions` 列表中包含新版本,并设置 `served:true`。此外,将 `spec.conversion` 字段设置为所选的转换策略。如果使用转换 Webhook,请配置 `spec.conversion.webhookClientConfig` 字段以调用 Webhook。

一旦添加了新版本,客户端就可以逐步迁移到新版本。有些客户端使用旧版本,而另一些客户端使用新版本是完全安全的。

将存储对象迁移到新版本

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

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

移除旧版本

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

指定多个版本

CustomResourceDefinition API 的 `versions` 字段可用于支持你开发的自定义资源的多个版本。版本可以有不同的架构,并且转换 Webhook 可以在版本之间转换自定义资源。Webhook 转换应在适用的情况下遵循 Kubernetes API 约定。具体来说,请参阅 API 更改文档,了解一组有用的注意事项和建议。

此示例显示了一个具有两个版本的 CustomResourceDefinition。第一个示例假设所有版本共享相同的架构,之间没有转换。YAML 中的注释提供了更多上下文。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # name must match the spec fields below, and be in the form: <plural>.<group>
  name: crontabs.example.com
spec:
  # group name to use for REST API: /apis/<group>/<version>
  group: example.com
  # list of versions supported by this CustomResourceDefinition
  versions:
  - name: v1beta1
    # Each version can be enabled/disabled by Served flag.
    served: true
    # One and only one version must be marked as the storage version.
    storage: true
    # A schema is required
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  # The conversion section is introduced in Kubernetes 1.13+ with a default value of
  # None conversion (strategy sub-field set to None).
  conversion:
    # None conversion assumes the same schema for all versions and only sets the apiVersion
    # field of custom resources to the proper value
    strategy: None
  # either Namespaced or Cluster
  scope: Namespaced
  names:
    # plural name to be used in the URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular name to be used as an alias on the CLI and for display
    singular: crontab
    # kind is normally the CamelCased singular type. Your resource manifests use this.
    kind: CronTab
    # shortNames allow shorter string to match your resource on the CLI
    shortNames:
    - ct

# Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # name must match the spec fields below, and be in the form: <plural>.<group>
  name: crontabs.example.com
spec:
  # group name to use for REST API: /apis/<group>/<version>
  group: example.com
  # list of versions supported by this CustomResourceDefinition
  versions:
  - name: v1beta1
    # Each version can be enabled/disabled by Served flag.
    served: true
    # One and only one version must be marked as the storage version.
    storage: true
  - name: v1
    served: true
    storage: false
  validation:
    openAPIV3Schema:
      type: object
      properties:
        host:
          type: string
        port:
          type: string
  # The conversion section is introduced in Kubernetes 1.13+ with a default value of
  # None conversion (strategy sub-field set to None).
  conversion:
    # None conversion assumes the same schema for all versions and only sets the apiVersion
    # field of custom resources to the proper value
    strategy: None
  # either Namespaced or Cluster
  scope: Namespaced
  names:
    # plural name to be used in the URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular name to be used as an alias on the CLI and for display
    singular: crontab
    # kind is normally the PascalCased singular type. Your resource manifests use this.
    kind: CronTab
    # shortNames allow shorter string to match your resource on the CLI
    shortNames:
    - ct

你可以将 CustomResourceDefinition 保存到 YAML 文件中,然后使用 `kubectl apply` 创建它。

kubectl apply -f my-versioned-crontab.yaml

创建后,API 服务器开始在 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 版本模式的条目,版本字符串的数字部分从大到小排序。
  • 如果第一个数字部分后面跟着字符串 `beta` 或 `alpha`,它们会按该顺序排序,在没有 `beta` 或 `alpha` 后缀的等效字符串(假定为 GA 版本)之后。
  • 如果 `beta` 或 `alpha` 后面跟着另一个数字,这些数字也从大到小排序。
  • 不符合上述格式的字符串按字母顺序排序,数字部分不作特殊处理。请注意,在下面的示例中,`foo1` 排在 `foo10` 之前。这与遵循 Kubernetes 版本模式的条目的数字部分的排序方式不同。

如果你查看以下排序版本列表,这可能是有意义的。

- v10
- v2
- v1
- v11beta2
- v10beta3
- v3beta1
- v12alpha1
- v11alpha2
- foo1
- foo10

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

版本弃用

特性状态: Kubernetes v1.19 [stable]

从 v1.19 开始,CustomResourceDefinition 可以指示其定义的资源的特定版本已弃用。当对该资源的弃用版本发出 API 请求时,API 响应中会作为标头返回警告消息。如果需要,可以自定义每个弃用资源的警告消息。

定制的警告消息应指明已弃用的 API 组、版本和类型,并应指明如果适用,应使用哪个 API 组、版本和类型。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
  name: crontabs.example.com
spec:
  group: example.com
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
  scope: Namespaced
  versions:
  - name: v1alpha1
    served: true
    storage: false
    # This indicates the v1alpha1 version of the custom resource is deprecated.
    # API requests to this version receive a warning header in the server response.
    deprecated: true
    # This overrides the default warning returned to API clients making v1alpha1 API requests.
    deprecationWarning: "example.com/v1alpha1 CronTab is deprecated; see http://example.com/v1alpha1-v1 for instructions to migrate to example.com/v1 CronTab"
    
    schema: ...
  - name: v1beta1
    served: true
    # This indicates the v1beta1 version of the custom resource is deprecated.
    # API requests to this version receive a warning header in the server response.
    # A default warning message is returned for this version.
    deprecated: true
    schema: ...
  - name: v1
    served: true
    storage: true
    schema: ...

# Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
  scope: Namespaced
  validation: ...
  versions:
  - name: v1alpha1
    served: true
    storage: false
    # This indicates the v1alpha1 version of the custom resource is deprecated.
    # API requests to this version receive a warning header in the server response.
    deprecated: true
    # This overrides the default warning returned to API clients making v1alpha1 API requests.
    deprecationWarning: "example.com/v1alpha1 CronTab is deprecated; see http://example.com/v1alpha1-v1 for instructions to migrate to example.com/v1 CronTab"
  - name: v1beta1
    served: true
    # This indicates the v1beta1 version of the custom resource is deprecated.
    # API requests to this version receive a warning header in the server response.
    # A default warning message is returned for this version.
    deprecated: true
  - name: v1
    served: true
    storage: true

版本删除

在所有提供旧版自定义资源的集群的现有存储数据已迁移到新版 API 版本,并且旧版本已从 CustomResourceDefinition 的 `status.storedVersions` 中删除之前,不能从 CustomResourceDefinition 清单中删除旧版 API 版本。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
  name: crontabs.example.com
spec:
  group: example.com
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
  scope: Namespaced
  versions:
  - name: v1beta1
    # This indicates the v1beta1 version of the custom resource is no longer served.
    # API requests to this version receive a not found error in the server response.
    served: false
    schema: ...
  - name: v1
    served: true
    # The new served version should be set as the storage version
    storage: true
    schema: ...

Webhook 转换

特性状态: Kubernetes v1.16 [stable]

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

  • 自定义资源以与存储版本不同的版本请求时。
  • 在某个版本中创建 Watch,但更改后的对象存储在另一个版本中。
  • 自定义资源 PUT 请求的版本与存储版本不同。

为了涵盖所有这些情况并优化 API 服务器的转换,转换请求可能包含多个对象,以尽量减少外部调用。Webhook 应该独立执行这些转换。

编写转换 Webhook 服务器

请参阅在 Kubernetes e2e 测试中验证的自定义资源转换 Webhook 服务器的实现。Webhook 处理 API 服务器发送的 `ConversionReview` 请求,并发送回包装在 `ConversionResponse` 中的转换结果。请注意,请求包含需要独立转换且不改变对象顺序的自定义资源列表。示例服务器的组织方式是可重用于其他转换。大多数通用代码位于框架文件中,只留下一个函数需要为不同的转换实现。

允许的突变

转换 Webhook 不得修改转换对象 `metadata` 中的任何内容,`labels` 和 `annotations` 除外。尝试更改 `name`、`UID` 和 `namespace` 将被拒绝,并导致触发转换的请求失败。所有其他更改都将被忽略。

部署转换 Webhook 服务

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

配置 CustomResourceDefinition 以使用转换 Webhook

通过修改 `spec` 的 `conversion` 部分,可以将 None 转换示例扩展为使用转换 Webhook。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # name must match the spec fields below, and be in the form: <plural>.<group>
  name: crontabs.example.com
spec:
  # group name to use for REST API: /apis/<group>/<version>
  group: example.com
  # list of versions supported by this CustomResourceDefinition
  versions:
  - name: v1beta1
    # Each version can be enabled/disabled by Served flag.
    served: true
    # One and only one version must be marked as the storage version.
    storage: true
    # Each version can define its own schema when there is no top-level
    # schema is defined.
    schema:
      openAPIV3Schema:
        type: object
        properties:
          hostPort:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  conversion:
    # the Webhook strategy instructs the API server to call an external webhook for any conversion between custom resources.
    strategy: Webhook
    # webhook is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server.
    webhook:
      # conversionReviewVersions indicates what ConversionReview versions are understood/preferred by the webhook.
      # The first version in the list understood by the API server is sent to the webhook.
      # The webhook must respond with a ConversionReview object in the same version it received.
      conversionReviewVersions: ["v1","v1beta1"]
      clientConfig:
        service:
          namespace: default
          name: example-conversion-webhook-server
          path: /crdconvert
        caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  # either Namespaced or Cluster
  scope: Namespaced
  names:
    # plural name to be used in the URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular name to be used as an alias on the CLI and for display
    singular: crontab
    # kind is normally the CamelCased singular type. Your resource manifests use this.
    kind: CronTab
    # shortNames allow shorter string to match your resource on the CLI
    shortNames:
    - ct

# Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # name must match the spec fields below, and be in the form: <plural>.<group>
  name: crontabs.example.com
spec:
  # group name to use for REST API: /apis/<group>/<version>
  group: example.com
  # prunes object fields that are not specified in OpenAPI schemas below.
  preserveUnknownFields: false
  # list of versions supported by this CustomResourceDefinition
  versions:
  - name: v1beta1
    # Each version can be enabled/disabled by Served flag.
    served: true
    # One and only one version must be marked as the storage version.
    storage: true
    # Each version can define its own schema when there is no top-level
    # schema is defined.
    schema:
      openAPIV3Schema:
        type: object
        properties:
          hostPort:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  conversion:
    # the Webhook strategy instructs the API server to call an external webhook for any conversion between custom resources.
    strategy: Webhook
    # webhookClientConfig is required when strategy is `Webhook` and it configures the webhook endpoint to be called by API server.
    webhookClientConfig:
      service:
        namespace: default
        name: example-conversion-webhook-server
        path: /crdconvert
      caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  # either Namespaced or Cluster
  scope: Namespaced
  names:
    # plural name to be used in the URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular name to be used as an alias on the CLI and for display
    singular: crontab
    # kind is normally the CamelCased singular type. Your resource manifests use this.
    kind: CronTab
    # shortNames allow shorter string to match your resource on the CLI
    shortNames:
    - ct

你可以将 CustomResourceDefinition 保存到 YAML 文件中,然后使用 `kubectl apply` 应用它。

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

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

联系 Webhook

一旦 API 服务器确定需要向转换 Webhook 发送请求,它就需要知道如何联系该 Webhook。这在 Webhook 配置的 `webhookClientConfig` 节中指定。

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

URL

`url` 以标准 URL 格式(`scheme://host:port/path`)给出 Webhook 的位置。

`host` 不应引用集群中运行的服务;请改用 `service` 字段指定服务引用。在某些 apiserver(例如,`kube-apiserver` 无法解析集群内 DNS,因为这将违反分层)中,`host` 可能会通过外部 DNS 解析。`host` 也可以是 IP 地址。

请注意,将 `localhost` 或 `127.0.0.1` 用作 `host` 存在风险,除非你非常小心地在所有运行 apiserver(可能需要调用此 webhook)的主机上运行此 webhook。此类安装可能不可移植或不易在新集群中运行。

方案必须是“https”;URL 必须以“https://”开头。

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

以下是一个配置为调用 URL 的转换 Webhook 示例(并期望使用系统信任根验证 TLS 证书,因此未指定 caBundle)

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        url: "https://my-webhook.example.com:9443/my-webhook-path"
...

# Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhookClientConfig:
      url: "https://my-webhook.example.com:9443/my-webhook-path"
...

服务参考

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

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

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        service:
          namespace: my-service-namespace
          name: my-service-name
          path: /my-path
          port: 1234
        caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
...

# Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhookClientConfig:
      service:
        namespace: my-service-namespace
        name: my-service-name
        path: /my-path
        port: 1234
      caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
...

Webhook 请求和响应

请求

Webhook 会收到一个 POST 请求,其中 `Content-Type: application/json`,并且请求体中包含一个 `apiextensions.k8s.io` API 组中的 `ConversionReview` API 对象,序列化为 JSON。

Webhook 可以在其 CustomResourceDefinition 的 `conversionReviewVersions` 字段中指定它们接受的 `ConversionReview` 对象版本。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhook:
      conversionReviewVersions: ["v1", "v1beta1"]
      ...

创建 `apiextensions.k8s.io/v1` 自定义资源定义时,`conversionReviewVersions` 是一个必填字段。Webhook 必须支持当前和以前的 API 服务器理解的至少一个 `ConversionReview` 版本。

# Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    conversionReviewVersions: ["v1", "v1beta1"]
    ...

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

API 服务器发送它们支持的 `conversionReviewVersions` 列表中的第一个 `ConversionReview` 版本。如果 API 服务器不支持列表中的任何版本,则不允许创建自定义资源定义。如果 API 服务器遇到以前创建的转换 Webhook 配置,并且该配置不支持 API 服务器知道如何发送的任何 `ConversionReview` 版本,则尝试调用 Webhook 将失败。

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

{
  "apiVersion": "apiextensions.k8s.io/v1",
  "kind": "ConversionReview",
  "request": {
    # Random uid uniquely identifying this conversion call
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    
    # The API group and version the objects should be converted to
    "desiredAPIVersion": "example.com/v1",
    
    # The list of objects to convert.
    # May contain one or more objects, in one or more versions.
    "objects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "hostPort": "localhost:1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "hostPort": "example.com:2345"
      }
    ]
  }
}

{
  # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  "apiVersion": "apiextensions.k8s.io/v1beta1",
  "kind": "ConversionReview",
  "request": {
    # Random uid uniquely identifying this conversion call
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    
    # The API group and version the objects should be converted to
    "desiredAPIVersion": "example.com/v1",
    
    # The list of objects to convert.
    # May contain one or more objects, in one or more versions.
    "objects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "hostPort": "localhost:1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "hostPort": "example.com:2345"
      }
    ]
  }
}

响应

Webhook 以 200 HTTP 状态码、`Content-Type: application/json` 和包含一个 `ConversionReview` 对象(以发送时的相同版本)的响应体进行响应,其中 `response` 节已填充并序列化为 JSON。

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

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

来自 Webhook 的最小成功响应示例

{
  "apiVersion": "apiextensions.k8s.io/v1",
  "kind": "ConversionReview",
  "response": {
    # must match <request.uid>
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    "result": {
      "status": "Success"
    },
    # Objects must match the order of request.objects, and have apiVersion set to <request.desiredAPIVersion>.
    # kind, metadata.uid, metadata.name, and metadata.namespace fields must not be changed by the webhook.
    # metadata.labels and metadata.annotations fields may be changed by the webhook.
    # All other changes to metadata fields by the webhook are ignored.
    "convertedObjects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "host": "localhost",
        "port": "1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "host": "example.com",
        "port": "2345"
      }
    ]
  }
}

{
  # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  "apiVersion": "apiextensions.k8s.io/v1beta1",
  "kind": "ConversionReview",
  "response": {
    # must match <request.uid>
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    "result": {
      "status": "Failed"
    },
    # Objects must match the order of request.objects, and have apiVersion set to <request.desiredAPIVersion>.
    # kind, metadata.uid, metadata.name, and metadata.namespace fields must not be changed by the webhook.
    # metadata.labels and metadata.annotations fields may be changed by the webhook.
    # All other changes to metadata fields by the webhook are ignored.
    "convertedObjects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "host": "localhost",
        "port": "1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "host": "example.com",
        "port": "2345"
      }
    ]
  }
}

如果转换失败,Webhook 应返回一个包含以下字段的 `response` 节:

  • `uid`,从发送到 Webhook 的 `request.uid` 复制而来。
  • `result`,设置为 `{"status":"Failed"}`

Webhook 响应示例,指示转换请求失败,并包含可选消息

{
  "apiVersion": "apiextensions.k8s.io/v1",
  "kind": "ConversionReview",
  "response": {
    "uid": "<value from request.uid>",
    "result": {
      "status": "Failed",
      "message": "hostPort could not be parsed into a separate host and port"
    }
  }
}

{
  # Deprecated in v1.16 in favor of apiextensions.k8s.io/v1
  "apiVersion": "apiextensions.k8s.io/v1beta1",
  "kind": "ConversionReview",
  "response": {
    "uid": "<value from request.uid>",
    "result": {
      "status": "Failed",
      "message": "hostPort could not be parsed into a separate host and port"
    }
  }
}

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

当对象被写入时,它会以写入时指定为存储版本的版本存储。如果存储版本发生变化,现有对象不会自动转换。但是,新创建或更新的对象会以新的存储版本写入。对象可能以不再提供服务的版本写入。

当你读取对象时,你将版本指定为路径的一部分。你可以请求任何当前正在提供服务的版本的对象。如果你指定的版本与对象的存储版本不同,Kubernetes 会以你请求的版本将对象返回给你,但存储的对象在磁盘上不会更改。

在服务读取请求时返回的对象会发生什么取决于 CRD 的 `spec.conversion` 中指定的内容。

  • 如果指定了默认的 `strategy` 值 `None`,则对象唯一的修改是更改 `apiVersion` 字符串,并且可能裁剪未知字段(取决于配置)。请注意,如果存储版本和请求版本之间的 schema 不同,这不太可能产生好的结果。特别是,如果同一数据在不同版本之间以不同字段表示,则不应使用此策略。
  • 如果指定了Webhook 转换,则此机制控制转换。

如果你更新现有对象,它会以当前存储版本重写。这是对象从一个版本更改到另一个版本的唯一方式。

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

  1. 存储版本是 `v1beta1`。你创建了一个对象。它以 `v1beta1` 版本存储。
  2. 你将版本 `v1` 添加到你的 CustomResourceDefinition 并将其指定为存储版本。在这里,`v1` 和 `v1beta1` 的 schema 是相同的,这通常是在 Kubernetes 生态系统中将 API 提升到稳定版时的情况。
  3. 你以 `v1beta1` 版本读取你的对象,然后再次以 `v1` 版本读取该对象。除了 apiVersion 字段外,两个返回的对象都是相同的。
  4. 你创建了一个新对象。它以 `v1` 版本存储。你现在有两个对象,其中一个在 `v1beta1`,另一个在 `v1`。
  5. 你更新了第一个对象。它现在以 `v1` 版本存储,因为这是当前的存储版本。

以前的存储版本

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

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

弃用版本和删除支持时,请选择存储升级过程。

选项 1: 使用存储版本迁移器

  1. 运行 存储版本迁移器
  2. 从 CustomResourceDefinition 的 `status.storedVersions` 字段中移除旧版本。

选项 2: 手动将现有对象升级到新的存储版本

以下是将 `v1beta1` 升级到 `v1` 的示例过程。

  1. 在 CustomResourceDefinition 文件中将 `v1` 设置为存储版本,并使用 kubectl 应用。`storedVersions` 现在是 `v1beta1, v1`。
  2. 编写升级程序以列出所有现有对象并以相同内容写入它们。这会强制后端以当前存储版本(即 `v1`)写入对象。
  3. 从 CustomResourceDefinition 的 `status.storedVersions` 字段中移除 `v1beta1`。
最后修改于 2025 年 2 月 19 日太平洋标准时间上午 11:19:更新 kubetl 子资源文档 (ab6b9ae2ed)