CustomResourceDefinition 中的版本

本页面解释如何向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。如果没有模式变更,则可以使用默认的 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 开头,后跟一个数字,可选的 betaalpha 指定,以及可选的附加数字版本信息。广义上,版本字符串可能看起来像 v2v2beta1。版本使用以下算法排序:

  • 遵循 Kubernetes 版本模式的条目排在不遵循的条目之前。
  • 对于遵循 Kubernetes 版本模式的条目,版本字符串的数字部分从大到小排序。
  • 如果字符串 betaalpha 跟在第一个数字部分之后,则它们按该顺序排序,排在没有 betaalpha 后缀的等效字符串(假定为 GA 版本)之后。
  • 如果在 betaalpha 之后还有另一个数字,则这些数字也从大到小排序。
  • 不符合上述格式的字符串按字母顺序排序,数字部分不作特殊处理。请注意,在下面的示例中,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 内的任何内容,除了 labelsannotations。尝试更改 nameUIDnamespace 将被拒绝,并导致引发转换的请求失败。所有其他更改都将被忽略。

部署转换 Webhook 服务

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

配置 CustomResourceDefinition 使用转换 Webhook

可以通过修改 specconversion 部分来扩展 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 或服务引用调用,并且可以选择包含自定义 CA 证书包来验证 TLS 连接。

URL

url 提供 Webhook 的位置,采用标准 URL 格式 (scheme://host:port/path)。

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

请注意,使用 localhost127.0.0.1 作为 host 是有风险的,除非你非常注意在所有运行可能需要调用此 Webhook 的 apiserver 的主机上运行此 Webhook。此类安装很可能不可移植或不易在新集群中运行。

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

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

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

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。路径是可选的,默认为 "/"。

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

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 服务器遇到以前创建的且不支持 API 服务器已知如何发送的任何 ConversionReview 版本的转换 Webhook 配置,则尝试调用该 Webhook 将失败。

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

{
  "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 响应时返回 HTTP 状态码 200,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 中指定的内容:

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

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

为了说明这一点,考虑以下假定的事件序列:

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

之前的存储版本

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

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

弃用版本和停止支持时,请选择一种存储升级过程。

选项 1:使用 Storage Version Migrator

  1. 运行Storage Version Migrator
  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)