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
。
添加新版本
- 选择一种转换策略。由于自定义资源对象需要能够以两种版本提供服务,这意味着它们有时会以与存储版本不同的版本提供服务。为了实现这一点,有时必须在自定义资源对象的存储版本和服务版本之间进行转换。如果转换涉及模式变更并需要自定义逻辑,则应使用转换 Webhook。如果没有模式变更,则可以使用默认的
None
转换策略,提供不同版本时仅修改apiVersion
字段。 - 如果使用转换 Webhook,则创建并部署转换 Webhook。有关更多详细信息,请参阅Webhook 转换。
- 更新 CustomResourceDefinition 以在
spec.versions
列表中包含新版本,并设置served:true
。此外,将spec.conversion
字段设置为所选的转换策略。如果使用转换 Webhook,配置spec.conversion.webhookClientConfig
字段以调用 Webhook。
添加新版本后,客户端可以逐步迁移到新版本。一些客户端使用旧版本而另一些客户端使用新版本是完全安全的。
将已存储对象迁移到新版本
- 请参阅将现有对象升级到新的存储版本章节。
在将对象升级到新的存储版本之前、期间和之后,客户端使用旧版本和新版本都是安全的。
移除旧版本
- 确保所有客户端都已完全迁移到新版本。可以查看 kube-apiserver 日志以帮助识别仍通过旧版本访问的任何客户端。
- 在
spec.versions
列表中将旧版本的served
设置为false
。如果仍有任何客户端意外使用旧版本,他们可能会开始报告在旧版本访问自定义资源对象时出现错误。如果发生这种情况,请将旧版本的served:true
切换回来,将剩余的客户端迁移到新版本,然后重复此步骤。 - 确保将现有对象升级到新的存储版本步骤已完成。
- 在 CustomResourceDefinition 的
spec.versions
列表中验证新版本的storage
是否设置为true
。 - 验证旧版本不再列在 CustomResourceDefinition 的
status.storedVersions
中。
- 在 CustomResourceDefinition 的
- 从 CustomResourceDefinition 的
spec.versions
列表中移除旧版本。 - 在转换 Webhook 中取消对旧版本的转换支持。
指定多个版本
CustomResourceDefinition API 的 versions
字段可用于支持你开发的多个自定义资源版本。版本可以有不同的模式,转换 Webhook 可以在版本之间转换自定义资源。Webhook 转换应遵循Kubernetes API 惯例(适用时)。具体来说,请参阅API 变更文档,了解一组有用的陷阱和建议。
注意
在apiextensions.k8s.io/v1beta1
中,有一个 version
字段而不是 versions
。version
字段已被弃用且是可选的,但如果它不为空,则必须与 versions
字段中的第一个项目匹配。此示例显示了一个包含两个版本的 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]
注意
Webhook 转换自 1.15 起可用作 Beta 功能,自 Kubernetes 1.13 起可用作 Alpha 功能。必须启用CustomResourceWebhookConversion
特性门控,对于 Beta 特性来说,许多集群都会自动启用。有关更多信息,请参阅特性门控文档。上面的示例在版本之间使用 None 转换,它只在转换时设置 apiVersion
字段,而不更改对象的其余部分。API 服务器还支持 Webhook 转换,如果需要转换,它会调用外部服务。例如,当:
- 自定义资源以不同于存储版本的版本被请求时。
- Watch 以一个版本创建,但更改后的对象以另一个版本存储时。
- 自定义资源 PUT 请求使用与存储版本不同的版本时。
为了涵盖所有这些情况并优化 API 服务器的转换,转换请求可能包含多个对象,以最大程度地减少外部调用。Webhook 应独立执行这些转换。
编写转换 Webhook 服务器
请参考在 Kubernetes e2e 测试中验证的自定义资源转换 Webhook 服务器实现。Webhook 处理 API 服务器发送的 ConversionReview
请求,并返回包装在 ConversionResponse
中的转换结果。请注意,请求包含需要独立转换而不改变对象顺序的自定义资源列表。示例服务器的组织方式便于重用于其他转换。大多数通用代码位于框架文件中,只需针对不同的转换实现一个函数。
注意
示例转换 Webhook 服务器将ClientAuth
字段留空,默认为 NoClientCert
。这意味着 Webhook 服务器不验证客户端(假定为 API 服务器)的身份。如果需要双向 TLS 或其他方式来验证客户端,请参阅如何验证 API 服务器。允许的变动
转换 Webhook 不得修改被转换对象的 metadata
内的任何内容,除了 labels
和 annotations
。尝试更改 name
、UID
和 namespace
将被拒绝,并导致引发转换的请求失败。所有其他更改都将被忽略。
部署转换 Webhook 服务
部署转换 Webhook 的文档与准入 Webhook 示例服务的文档相同。以下章节假定转换 Webhook 服务器部署在 default
命名空间中名为 example-conversion-webhook-server
的服务上,并在路径 /crdconvert
提供流量服务。
注意
当 Webhook 服务器作为服务部署到 Kubernetes 集群中时,必须通过服务在端口 443 上暴露(服务器本身可以使用任意端口,但服务对象应将其映射到端口 443)。如果服务使用不同的端口,API 服务器和 Webhook 服务之间的通信可能会失败。配置 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 或服务引用调用,并且可以选择包含自定义 CA 证书包来验证 TLS 连接。
URL
url
提供 Webhook 的位置,采用标准 URL 格式 (scheme://host:port/path
)。
host
不应指向集群中运行的服务;而是使用服务引用,通过指定 service
字段。在某些 apiserver 中(例如,kube-apiserver
无法解析集群内 DNS,因为这将违反分层),host
可能通过外部 DNS 解析。host
也可以是 IP 地址。
请注意,使用 localhost
或 127.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 准入)。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
字符串,并且(取决于配置)可能会修剪未知字段。请注意,如果存储版本和请求版本之间的模式不同,这不太可能产生好的结果。特别是,如果同一数据在不同版本中由不同字段表示,则不应使用此策略。 - 如果指定了Webhook 转换,则此机制控制转换。
如果你更新现有对象,它将以当前存储版本的版本重新写入。这是对象从一个版本更改为另一个版本的唯一方式。
为了说明这一点,考虑以下假定的事件序列:
- 存储版本是
v1beta1
。你创建一个对象。它存储在v1beta1
版本。 - 你在 CustomResourceDefinition 中添加版本
v1
,并将其指定为存储版本。在此,v1
和v1beta1
的模式是相同的,这在 Kubernetes 生态系统中将 API 提升到稳定阶段时通常是这种情况。 - 你以
v1beta1
版本读取你的对象,然后再次以v1
版本读取该对象。返回的两个对象除了 apiVersion 字段外是相同的。 - 你创建了一个新对象。它存储在
v1
版本。你现在有两个对象,其中一个在v1beta1
,另一个在v1
。 - 你更新了第一个对象。它现在存储在
v1
版本,因为这是当前的存储版本。
之前的存储版本
API 服务器在其状态字段 storedVersions
中记录了每个曾经被标记为存储版本的版本。对象可能以任何曾经被指定为存储版本的版本存储。在存储中,不存在以从未是存储版本的版本存储的对象。
将现有对象升级到新的存储版本
弃用版本和停止支持时,请选择一种存储升级过程。
选项 1:使用 Storage Version Migrator
- 运行Storage Version Migrator
- 从 CustomResourceDefinition 的
status.storedVersions
字段中移除旧版本。
选项 2:手动将现有对象升级到新的存储版本
以下是将 v1beta1
升级到 v1
的示例过程。
- 在 CustomResourceDefinition 文件中将
v1
设置为存储版本,并使用 kubectl 应用。storedVersions
现在是v1beta1, v1
。 - 编写一个升级过程,列出所有现有对象并以相同的内容重新写入。这强制后端以当前存储版本(即
v1
)写入对象。 - 从 CustomResourceDefinition 的
status.storedVersions
字段中移除v1beta1
。
注意
这是一个使用 kubectl
修补 CRD 对象的 status
子资源的示例:
kubectl patch customresourcedefinitions <CRD_Name> --subresource='status' --type='merge' -p '{"status":{"storedVersions":["v1"]}}'