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。如果没有模式更改,则可以使用默认的 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. 验证 storage 是否在 CustomResourceDefinition 中 spec.versions 列表中的新版本设置为 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 使用优先级最高的版本作为访问对象的默认版本。优先级是通过解析名称字段来确定版本号、稳定性(GA、Beta 或 Alpha)以及该稳定级别内的顺序来确定的。

用于对版本进行排序的算法旨在以 Kubernetes 项目对 Kubernetes 版本进行排序相同的方式对版本进行排序。版本以 v 开头,后跟一个数字,可选的 betaalpha 指示符,以及可选的附加数字版本信息。从广义上讲,版本字符串可能类似于 v2v2beta1。版本使用以下算法进行排序

  • 遵循 Kubernetes 版本模式的条目在不遵循 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 的文档与 admission webhook 示例服务 相同。下一节假设转换 webhook 服务器已部署到 default 命名空间中的名为 example-conversion-webhook-server 的服务中,并在路径 /crdconvert 上提供服务。

配置 CustomResourceDefinition 以使用转换 webhook

None 转换示例可以通过修改 specconversion 部分来扩展以使用转换 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 字段使用服务引用。主机可能会通过某些 API 服务器(例如 kube-apiserver 无法解析集群内 DNS,因为这将是分层违规)的外部 DNS 解析。host 也可以是 IP 地址。

请注意,除非您非常小心地将此 webhook 运行在所有运行需要调用此 webhook 的 API 服务器的主机上,否则使用 localhost127.0.0.1 作为 host 是有风险的。此类安装可能不可移植或无法轻松地在新的集群中运行。

方案必须为“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 的示例,该 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,并将 ConversionReview API 对象(在 apiextensions.k8s.io 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 会失败。

此示例显示了针对将 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 将以 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 中指定的内容

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

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

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

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

以前的存储版本

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

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

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

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

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

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

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

  1. 在 CustomResourceDefinition 文件中将 v1 设置为存储,并使用 kubectl 应用它。现在 storedVersionsv1beta1, v1
  2. 编写升级过程来列出所有现有对象并使用相同的内容写入它们。这将强制后端以当前存储版本(即 v1)写入对象。
  3. 从 CustomResourceDefinition status.storedVersions 字段中删除 v1beta1
最后修改时间:2024 年 6 月 19 日下午 4:41 PST: 修复 CRD 文档中错误命名的字段 (#46863) (1dca66b534)