--- title: 使用 CustomResourceDefinition 扩展 Kubernetes API content_type: task min-kubernetes-server-version: 1.16 weight: 20 --- 本页展示如何使用 [CustomResourceDefinition](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#customresourcedefinition-v1-apiextensions-k8s-io) 将 [定制资源(Custom Resource)](/zh/docs/concepts/extend-kubernetes/api-extension/custom-resources/) 安装到 Kubernetes API 上。 ## {{% heading "prerequisites" %}} {{< include "task-tutorial-prereqs.md" >}} {{< version-check >}} 如果你在使用较老的、仍处于被支持范围的 Kubernetes 版本,请切换到该版本的 文档查看对于的集群而言有用的建议。 ## 创建 CustomResourceDefinition {#create-a-customresourcedefinition} 当你创建新的 CustomResourceDefinition(CRD)时,Kubernetes API 服务器会为你所 指定的每一个版本生成一个 RESTful 的 资源路径。CRD 可以是名字空间作用域的,也可以 是集群作用域的,取决于 CRD 的 `scope` 字段设置。和其他现有的内置对象一样,删除 一个名字空间时,该名字空间下的所有定制对象也会被删除。CustomResourceDefinition 本身是不受名字空间限制的,对所有名字空间可用。 例如,如果你将下面的 CustomResourceDefinition 保存到 `resourcedefinition.yaml` 文件: ```yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: # 名字必需与下面的 spec 字段匹配,并且格式为 '<名称的复数形式>.<组名>' name: crontabs.stable.example.com spec: # 组名称,用于 REST API: /apis/<组>/<版本> group: stable.example.com # 列举此 CustomResourceDefinition 所支持的版本 versions: - name: v1 # 每个版本都可以通过 served 标志来独立启用或禁止 served: true # 其中一个且只有一个版本必需被标记为存储版本 storage: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: cronSpec: type: string image: type: string replicas: type: integer # 可以是 Namespaced 或 Cluster scope: Namespaced names: # 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式> plural: crontabs # 名称的单数形式,作为命令行使用时和显示时的别名 singular: crontab # kind 通常是单数形式的驼峰编码(CamelCased)形式。你的资源清单会使用这一形式。 kind: CronTab # shortNames 允许你在命令行使用较短的字符串来匹配资源 shortNames: - ct ``` 之后创建它: ```shell kubectl apply -f resourcedefinition.yaml ``` 这样一个新的受名字空间约束的 RESTful API 端点会被创建在: ``` /apis/stable.example.com/v1/namespaces/*/crontabs/... ``` 此端点 URL 自此可以用来创建和管理定制对象。对象的 `kind` 将是来自你上面创建时 所用的 spec 中指定的 `CronTab`。 创建端点的操作可能需要几秒钟。你可以监测你的 CustomResourceDefinition 的 `Established` 状况变为 true,或者监测 API 服务器的发现信息等待你的资源出现在 那里。 ## 创建定制对象 {#create-custom-objects} 在创建了 CustomResourceDefinition 对象之后,你可以创建定制对象(Custom Objects)。定制对象可以包含定制字段。这些字段可以包含任意的 JSON 数据。 在下面的例子中,在类别为 `CrontTab` 的定制对象中,设置了`cronSpec` 和 `image` 定制字段。类别 `CronTab` 来自你在上面所创建的 CRD 的规约。 如果你将下面的 YAML 保存到 `my-crontab.yaml`: ```yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata: name: my-new-cron-object spec: cronSpec: "* * * * */5" image: my-awesome-cron-image ``` 并执行创建命令: ```shell kubectl apply -f my-crontab.yaml ``` 你就可以使用 kubectl 来管理你的 CronTab 对象了。例如: ```shell kubectl get crontab ``` 应该会输出如下列表: ```none NAME AGE my-new-cron-object 6s ``` 使用 kubectl 时,资源名称是大小写不敏感的,而且你既可以使用 CRD 中所定义的单数 形式或复数形式,也可以使用其短名称: ```shell kubectl get ct -o yaml ``` 你可以看到输出中包含了你创建定制对象时在 YAML 文件中指定的定制字段 `cronSpec` 和 `image`: ```yaml apiVersion: v1 kind: List items: - apiVersion: stable.example.com/v1 kind: CronTab metadata: creationTimestamp: 2017-05-31T12:56:35Z generation: 1 name: my-new-cron-object namespace: default resourceVersion: "285" uid: 9423255b-4600-11e7-af6a-28d2447dc82b spec: cronSpec: '* * * * */5' image: my-awesome-cron-image metadata: resourceVersion: "" ``` ## 删除 CustomResourceDefinition {#delete-a-customresourcedefinition} 当你删除某 CustomResourceDefinition 时,服务器会卸载其 RESTful API 端点,并删除服务器上存储的所有定制对象。 ```shell kubectl delete -f resourcedefinition.yaml kubectl get crontabs ``` ```none Error from server (NotFound): Unable to list {"stable.example.com" "v1" "crontabs"}: the server could not find the requested resource (get crontabs.stable.example.com) ``` 如果你在以后创建相同的 CustomResourceDefinition 时,该 CRD 会是一个空的结构。 ## 设置结构化的模式 {#specifying-a-structural-schema} CustomResource 对象在定制字段中保存结构化的数据,这些字段和内置的字段 `apiVersion`、`kind` 和 `metadata` 等一起存储,不过内置的字段都会被 API 服务器隐式完成合法性检查。有了 [OpenAPI v3.0 检查](#validation) 能力之后,你可以设置一个模式(Schema),在创建和更新定制对象时,这一模式会被用来 对对象内容进行合法性检查。参阅下文了解这类模式的细节和局限性。 在 `apiextensions.k8s.io/v1` 版本中,CustomResourceDefinition 的这一结构化模式 定义是必需的。 在 CustomResourceDefinition 的 beta 版本中,结构化模式定义是可选的。 结构化模式本身是一个 [OpenAPI v3.0 验证模式](#validation),其中: 1. 为对象根(root)设置一个非空的 type 值(藉由 OpenAPI 中的 `type`),对每个 object 节点的每个字段(藉由 OpenAPI 中的 `properties` 或 `additionalProperties`)以及 array 节点的每个条目(藉由 OpenAPI 中的 `items`)也要设置非空的 type 值, 除非: * 节点包含属性 `x-kubernetes-int-or-string: true` * 节点包含属性 `x-kubernetes-preserve-unknown-fields: true` 2. 对于 object 的每个字段或 array 中的每个条目,如果其定义中包含 `allOf`、`anyOf`、`oneOf` 或 `not`,则模式也要指定这些逻辑组合之外的字段或条目(试比较例 1 和例 2)。 3. 在 `allOf`、`anyOf`、`oneOf` 或 `not` 上下文内不设置 `description`、`type`、`default`、 `additionalProperties` 或者 `nullable`。此规则的例外是 `x-kubernetes-int-or-string` 的两种模式(见下文)。 4. 如果 `metadata` 被设置,则只允许对 `metadata.name` 和 `metadata.generateName` 设置约束。 非结构化的例 1: ```yaml allOf: - properties: foo: ... ``` 违反了第 2 条规则。下面的是正确的: ```yaml properties: foo: ... allOf: - properties: foo: ... ``` 非结构化的例 2: ```yaml allOf: - items: properties: foo: ... ``` 违反了第 2 条规则。下面的是正确的: ```yaml items: properties: foo: ... allOf: - items: properties: foo: ... ``` 非结构化的例 3: ```yaml properties: foo: pattern: "abc" metadata: type: object properties: name: type: string pattern: "^a" finalizers: type: array items: type: string pattern: "my-finalizer" anyOf: - properties: bar: type: integer minimum: 42 required: ["bar"] description: "foo bar object" ``` 不是一个结构化的模式,因为其中存在以下违例: * 根节点缺失 type 设置(规则 1) * `foo` 的 type 缺失(规则 1) * `anyOf` 中的 `bar` 未在外部指定(规则 2) * `bar` 的 `type` 位于 `anyOf` 中(规则 3) * `anyOf` 中设置了 `description` (规则 3) * `metadata.finalizers` 不可以被限制 (规则 4) 作为对比,下面的 YAML 所对应的模式则是结构化的: ```yaml type: object description: "foo bar object" properties: foo: type: string pattern: "abc" bar: type: integer metadata: type: object properties: name: type: string pattern: "^a" anyOf: - properties: bar: minimum: 42 required: ["bar"] ``` 如果违反了结构化模式规则,CustomResourceDefinition 的 `NonStructural` 状况中 会包含报告信息。 ### 字段剪裁 {#field-pruning} CustomResourceDefinition 在集群的持久性存储 {{< glossary_tooltip term_id="etcd" text="etcd">}} 中保存经过合法性检查的资源数据。 就像原生的 Kubernetes 资源,例如 {{< glossary_tooltip text="ConfigMap" term_id="configmap" >}}, 如果你指定了 API 服务器所无法识别的字段,则该未知字段会在保存资源之前 被 _剪裁(Pruned)_ 掉(删除)。 {{< note >}} 从 `apiextensions.k8s.io/v1beta1` 转换到 `apiextensions.k8s.io/v1` 的 CRD 可能没有结构化的模式定义,因此其 `spec.preserveUnknownFields` 可能为 `true`。 对于使用 `apiextensions.k8s.io/v1beta1` 且将 `spec.preserveUnknownFields` 设置为 `true` 创建的旧 CustomResourceDefinition 对象,有以下表现: * 裁剪未启用。 * 可以存储任意数据。 为了与 `apiextensions.k8s.io/v1` 兼容,将你的自定义资源定义更新为: 1. 使用结构化的 OpenAPI 模式。 2. `spec.preserveUnknownFields` 设置为 `false`。 {{< /note >}} 如果你将下面的 YAML 保存到 `my-crontab.yaml` 文件: ```yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata: name: my-new-cron-object spec: cronSpec: "* * * * */5" image: my-awesome-cron-image someRandomField: 42 ``` 并创建之: ```shell kubectl create --validate=false -f my-crontab.yaml -o yaml ``` 输出类似于: ```console apiVersion: stable.example.com/v1 kind: CronTab metadata: creationTimestamp: 2017-05-31T12:56:35Z generation: 1 name: my-new-cron-object namespace: default resourceVersion: "285" uid: 9423255b-4600-11e7-af6a-28d2447dc82b spec: cronSpec: '* * * * */5' image: my-awesome-cron-image ``` 注意其中的字段 `someRandomField` 已经被剪裁掉。 本例中通过 `--validate=false` 命令行选项 关闭了客户端的合法性检查以展示 API 服务器的行为, 因为 [OpenAPI 合法性检查模式也会发布到](#publish-validation-schema-in-openapi-v2) 客户端,`kubectl` 也会检查未知的字段并在对象被发送到 API 服务器之前就拒绝它们。 #### 控制剪裁 {#controlling-pruning} 默认情况下,定制资源的所有版本中的所有未规定的字段都会被剪裁掉。 通过在结构化的 OpenAPI v3 [检查模式定义](#specifying-a-structural-schema) 中为特定字段的子树添加 `x-kubernetes-preserve-unknown-fields: true` 属性,可以 选择不对其执行剪裁操作。 例如: ```yaml type: object properties: json: x-kubernetes-preserve-unknown-fields: true ``` 字段 `json` 可以保存任何 JSON 值,其中内容不会被剪裁掉。 你也可以部分地指定允许的 JSON 数据格式;例如: ```yaml type: object properties: json: x-kubernetes-preserve-unknown-fields: true type: object description: this is arbitrary JSON ``` 通过这样设置,JSON 中只能设置 `object` 类型的值。 对于所指定的每个属性(或 `additionalProperties`),剪裁会再次被启用。 ```yaml type: object properties: json: x-kubernetes-preserve-unknown-fields: true type: object properties: spec: type: object properties: foo: type: string bar: type: string ``` 对于上述定义,如果提供的数值如下: ```yaml json: spec: foo: abc bar: def something: x status: something: x ``` 则该值会被剪裁为: ```yaml json: spec: foo: abc bar: def status: something: x ``` 这意味着所指定的 `spec` 对象中的 `something` 字段被剪裁掉,而其外部的内容都被保留。 ### IntOrString 模式定义中标记了 `x-kubernetes-int-or-string: true` 的节点不受前述规则 1 约束,因此下面的定义是结构化的模式: ```yaml type: object properties: foo: x-kubernetes-int-or-string: true ``` 此外,所有这类节点也不再受规则 3 约束,也就是说,下面两种模式是被允许的 (注意,仅限于这两种模式,不支持添加新字段的任何其他变种): ```yaml x-kubernetes-int-or-string: true anyOf: - type: integer - type: string ... ``` 和 ```yaml x-kubernetes-int-or-string: true allOf: - anyOf: - type: integer - type: string - ... # zero or more ... ``` 在以上两种规约中,整数值和字符串值都会被认为是合法的。 在[合法性检查模式定义的发布时](#publish-validation-schema-in-openapi-v2), `x-kubernetes-int-or-string: true` 会被展开为上述两种模式之一。 ### RawExtension RawExtensions(就像在 [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery/blob/03ac7a9ade429d715a1a46ceaa3724c18ebae54f/pkg/runtime/types.go#L94) 项目中 `runtime.RawExtension` 所定义的那样) 可以保存完整的 Kubernetes 对象,也就是,其中会包含 `apiVersion` 和 `kind` 字段。 通过 `x-kubernetes-embedded-resource: true` 来设定这些嵌套对象的规约(无论是 完全无限制还是部分指定都可以)是可能的。例如: ```yaml type: object properties: foo: x-kubernetes-embedded-resource: true x-kubernetes-preserve-unknown-fields: true ``` 这里,字段 `foo` 包含一个完整的对象,例如: ```yaml foo: apiVersion: v1 kind: Pod spec: ... ``` 由于字段上设置了 `x-kubernetes-preserve-unknown-fields: true`,其中的内容不会 被剪裁。不过,在这个语境中,`x-kubernetes-preserve-unknown-fields: true` 的 使用是可选的。 设置了 `x-kubernetes-embedded-resource: true` 之后,`apiVersion`、`kind` 和 `metadata` 都是隐式设定并隐式完成合法性验证。 ## 提供 CRD 的多个版本 {#serving-multiple-versions-of-a-crd} 关于如何为你的 CustomResourceDefinition 提供多个版本的支持,以及如何将你的对象 从一个版本迁移到另一个版本, 详细信息可参阅 [定制资源定义的版本](/zh/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/)。 ## 高级主题 {#advanced-topics} ### Finalizers *Finalizer* 能够让控制器实现异步的删除前(Pre-delete)回调。 与内置对象类似,定制对象也支持 Finalizer。 你可以像下面一样为定制对象添加 Finalizer: ```yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata: finalizers: - stable.example.com/finalizer ``` 自定义 Finalizer 的标识符包含一个域名、一个正向斜线和 finalizer 的名称。 任何控制器都可以在任何对象的 finalizer 列表中添加新的 finalizer。 对带有 Finalizer 的对象的第一个删除请求会为其 `metadata.deletionTimestamp` 设置一个值,但不会真的删除对象。一旦此值被设置,`finalizers` 列表中的表项 只能被移除。在列表中仍然包含 finalizer 时,无法强制删除对应的对象。 当 `metadata.deletionTimestamp` 字段被设置时,监视该对象的各个控制器会 执行它们所能处理的 finalizer,并在完成处理之后将其从列表中移除。 每个控制器负责将其 finalizer 从列表中删除。 `metadata.deletionGracePeriodSeconds` 的取值控制对更新的轮询周期。 一旦 finalizers 列表为空时,就意味着所有 finalizer 都被执行过, Kubernetes 会最终删除该资源, ### 合法性检查 {#validation} 定制资源是通过 [OpenAPI v3 模式定义](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject) 来执行合法性检查的, 你可以通过使用[准入控制 Webhook](/zh/docs/reference/access-authn-authz/admission-controllers/#validatingadmissionwebhook) 来添加额外的合法性检查逻辑。 此外,对模式定义存在以下限制: - 以下字段不可设置: - `definitions` - `dependencies` - `deprecated` - `discriminator` - `id` - `patternProperties` - `readOnly` - `writeOnly` - `xml` - `$ref` - 字段 `uniqueItems` 不可设置为 `true` - 字段 `additionalProperties` 不可设置为 `false` - 字段 `additionalProperties` 与 `properties` 互斥,不可同时使用 当[设置默认值特性](#defaulting)被启用时,可以设置字段 `default`。 就 `apiextensions.k8s.io/v1` 组的 CustomResourceDefinitions,这一条件是满足的。 设置默认值的功能特性从 1.17 开始正式发布。该特性在 1.16 版本中处于 Beta 状态,要求 `CustomResourceDefaulting` [特性门控](/zh/docs/reference/command-line-tools-reference/feature-gates/) 被启用。对于大多数集群而言,Beta 状态的特性门控默认都是自动启用的。 关于对某些 CustomResourceDefinition 特性所必需的限制,可参见 [结构化的模式定义](#specifying-a-structural-schema)小节。 模式定义是在 CustomResourceDefinition 中设置的。在下面的例子中, CustomResourceDefinition 对定制对象执行以下合法性检查: - `spec.cronSpec` 必须是一个字符串,必须是正则表达式所描述的形式; - `spec.replicas` 必须是一个整数,且其最小值为 1、最大值为 10。 将此 CustomResourceDefinition 保存到 `resourcedefinition.yaml` 文件中: ```yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: crontabs.stable.example.com spec: group: stable.example.com versions: - name: v1 served: true storage: true schema: # openAPIV3Schema is the schema for validating custom objects. openAPIV3Schema: type: object properties: spec: type: object properties: cronSpec: type: string pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' image: type: string replicas: type: integer minimum: 1 maximum: 10 scope: Namespaced names: plural: crontabs singular: crontab kind: CronTab shortNames: - ct ``` 并创建 CustomResourceDefinition: ```shell kubectl apply -f resourcedefinition.yaml ``` 对于一个创建 CronTab 类别对象的定制对象的请求而言,如果其字段中包含非法值,则 该请求会被拒绝。 在下面的例子中,定制对象中包含带非法值的字段: - `spec.cronSpec` 与正则表达式不匹配 - `spec.replicas` 数值大于 10。 如果你将下面的 YAML 保存到 `my-crontab.yaml`: ```yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata: name: my-new-cron-object spec: cronSpec: "* * * *" image: my-awesome-cron-image replicas: 15 ``` 并尝试创建定制对象: ```shell kubectl apply -f my-crontab.yaml ``` 你会看到下面的错误信息: ```console The CronTab "my-new-cron-object" is invalid: []: Invalid value: map[string]interface {}{"apiVersion":"stable.example.com/v1", "kind":"CronTab", "metadata":map[string]interface {}{"name":"my-new-cron-object", "namespace":"default", "deletionTimestamp":interface {}(nil), "deletionGracePeriodSeconds":(*int64)(nil), "creationTimestamp":"2017-09-05T05:20:07Z", "uid":"e14d79e7-91f9-11e7-a598-f0761cb232d1", "clusterName":""}, "spec":map[string]interface {}{"cronSpec":"* * * *", "image":"my-awesome-cron-image", "replicas":15}}: validation failure list: spec.cronSpec in body should match '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' spec.replicas in body should be less than or equal to 10 ``` 如果所有字段都包含合法值,则对象创建的请求会被接受。 将下面的 YAML 保存到 `my-crontab.yaml` 文件: ```yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata: name: my-new-cron-object spec: cronSpec: "* * * * */5" image: my-awesome-cron-image replicas: 5 ``` 并创建定制对象: ```shell kubectl apply -f my-crontab.yaml crontab "my-new-cron-object" created ``` ### 设置默认值 {#efaulting} {{< note >}} 要使用设置默认值功能,你的 CustomResourceDefinition 必须使用 API 版本 `apiextensions.k8s.io/v1`。 {{< /note >}} 设置默认值的功能允许在 [OpenAPI v3 合法性检查模式定义](#validation)中设置默认值: ```yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: crontabs.stable.example.com spec: group: stable.example.com versions: - name: v1 served: true storage: true schema: # openAPIV3Schema 是用来检查定制对象的模式定义 openAPIV3Schema: type: object properties: spec: type: object properties: cronSpec: type: string pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' default: "5 0 * * *" image: type: string replicas: type: integer minimum: 1 maximum: 10 default: 1 scope: Namespaced names: plural: crontabs singular: crontab kind: CronTab shortNames: - ct ``` 使用此 CRD 定义时,`cronSpec` 和 `replicas` 都会被设置默认值: ```yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata: name: my-new-cron-object spec: image: my-awesome-cron-image ``` 会生成: ```yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata: name: my-new-cron-object spec: cronSpec: "5 0 * * *" image: my-awesome-cron-image replicas: 1 ``` 默认值设定的行为发生在定制对象上: * 在向 API 服务器发送的请求中,基于请求版本的设定设置默认值; * 在从 etcd 读取对象时,使用存储版本来设置默认值; * 在 Mutating 准入控制插件执行非空的补丁操作时,基于准入 Webhook 对象 版本设置默认值。 从 etcd 中读取数据时所应用的默认值设置不会被写回到 etcd 中。 需要通过 API 执行更新请求才能将这种方式设置的默认值写回到 etcd。 默认值一定会被剪裁(除了 `metadata` 字段的默认值设置),且必须通过所提供 的模式定义的检查。 针对 `x-kubernetes-embedded-resource: true` 节点(或者包含 `metadata` 字段的结构的默认值) 的 `metadata` 字段的默认值设置不会在 CustomResourceDefinition 创建时被剪裁, 而是在处理请求的字段剪裁阶段被删除。 #### 设置默认值和字段是否可为空(Nullable) {#defaulting-and-nullable} **1.20 版本新增:** 对于未设置其 nullable 标志的字段或者将该标志设置为 `false` 的字段,其空值(Null)会在设置默认值之前被剪裁掉。如果对应字段 存在默认值,则默认值会被赋予该字段。当 `nullable` 被设置为 `true` 时, 字段的空值会被保留,且不会在设置默认值时被覆盖。 例如,给定下面的 OpenAPI 模式定义: ```yaml type: object properties: spec: type: object properties: foo: type: string nullable: false default: "default" bar: type: string nullable: true baz: type: string ``` 像下面这样创建一个为 `foo`、`bar` 和 `baz` 设置空值的对象时: ```yaml spec: foo: null bar: null baz: null ``` 其结果会是这样: ```yaml spec: foo: "default" bar: null ``` 其中的 `foo` 字段被剪裁掉并重新设置默认值,因为该字段是不可为空的。 `bar` 字段的 `nullable: true` 使得其能够保有其空值。 `baz` 字段则被完全剪裁掉,因为该字段是不可为空的,并且没有默认值设置。 ### 以 OpenAPI v2 形式发布合法性检查模式 {#publish-validation-schema-in-openapi-v2} CustomResourceDefinition 的[结构化的](#specifying-a-structural-schema)、 [启用了剪裁的](#preserving-unknown-fields) [OpenAPI v3 合法性检查模式](#validation) 会在 Kubernetes API 服务器上作为 [OpenAPI v2 规约](/zh/docs/concepts/overview/kubernetes-api/#openapi-and-swagger-definitions) 的一部分发布出来。 [kubectl](/zh/docs/reference/kubectl/overview) 命令行工具会基于所发布的模式定义来执行 客户端的合法性检查(`kubectl create` 和 `kubectl apply`),为定制资源的模式定义 提供解释(`kubectl explain`)。 所发布的模式还可被用于其他目的,例如生成客户端或者生成文档。 OpenAPI v3 合法性检查模式定义会被转换为 OpenAPI v2 模式定义,并出现在 [OpenAPI v2 规范](/zh/docs/concepts/overview/kubernetes-api/#openapi-and-swagger-definitions) 的 `definitions` 和 `paths` 字段中。 在转换过程中会发生以下修改,目的是保持与 1.13 版本以前的 kubectl 工具兼容。 这些修改可以避免 kubectl 过于严格,以至于拒绝它无法理解的 OpenAPI 模式定义。 转换过程不会更改 CRD 中定义的合法性检查模式定义,因此不会影响到 API 服务器中 的[合法性检查](#validation)。 1. 以下字段会被移除,因为它们在 OpenAPI v2 中不支持(在将来版本中将使用 OpenAPI v3, 因而不会有这些限制) - 字段 `allOf`、`anyOf`、`oneOf` 和 `not` 会被删除 2. 如果设置了 `nullable: true`,我们会丢弃 `type`、`nullable`、`items` 和 `properties` OpenAPI v2 无法表达 Nullable。为了避免 kubectl 拒绝正常的对象,这一转换是必要的。 ### 额外的打印列 {#additional-printer-columns} `kubectl` 工具依赖服务器端的输出格式化。你的集群的 API 服务器决定 `kubectl get` 命令要显示的列有哪些。 你可以为 CustomResourceDefinition 定制这些要打印的列。 下面的例子添加了 `Spec`、`Replicas` 和 `Age` 列: 将此 CustomResourceDefinition 保存到 `resourcedefinition.yaml` 文件: ```yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: crontabs.stable.example.com spec: group: stable.example.com scope: Namespaced names: plural: crontabs singular: crontab kind: CronTab shortNames: - ct versions: - name: v1 served: true storage: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: cronSpec: type: string image: type: string replicas: type: integer additionalPrinterColumns: - name: Spec type: string description: The cron spec defining the interval a CronJob is run jsonPath: .spec.cronSpec - name: Replicas type: integer description: The number of jobs launched by the CronJob jsonPath: .spec.replicas - name: Age type: date jsonPath: .metadata.creationTimestamp ``` 创建 CustomResourceDefinition: ```shell kubectl apply -f resourcedefinition.yaml ``` 使用前文中的 `my-crontab.yaml` 创建一个实例。 启用服务器端打印输出: ```shell kubectl get crontab my-new-cron-object ``` 注意输出中的 `NAME`、`SPEC`、`REPLICAS` 和 `AGE` 列: ``` NAME SPEC REPLICAS AGE my-new-cron-object * * * * * 1 7s ``` {{< note >}} `NAME` 列是隐含的,不需要在 CustomResourceDefinition 中定义。 {{< /note >}} #### 优先级 {#priority} 每个列都包含一个 `priority`(优先级)字段。当前,优先级用来区分标准视图(Standard View)和宽视图(Wide View)(使用 `-o wide` 标志)中显示的列: - 优先级为 `0` 的列会在标准视图中显示。 - 优先级大于 `0` 的列只会在宽视图中显示。 #### 类型 {#type} 列的 `type` 字段可以是以下值之一 (比较 [OpenAPI v3 数据类型](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#dataTypes)): - `integer` – 非浮点数字 - `number` – 浮点数字 - `string` – 字符串 - `boolean` – `true` 或 `false` - `date` – 显示为以自此时间戳以来经过的时长 如果定制资源中的值与列中指定的类型不匹配,该值会被忽略。 你可以通过定制资源的合法性检查来确保取值类型是正确的。 #### 格式 {#format} 列的 `format` 字段可以是以下值之一: - `int32` - `int64` - `float` - `double` - `byte` - `date` - `date-time` - `password` 列的 `format` 字段控制 `kubectl` 打印对应取值时采用的风格。 ### 子资源 {#subresources} 定制资源支持 `/status` 和 `/scale` 子资源。 通过在 CustomResourceDefinition 中定义 `status` 和 `scale`, 可以有选择地启用这些子资源。 #### Status 子资源 {#status-subresource} 当启用了 status 子资源时,对应定制资源的 `/status` 子资源会被暴露出来。 - status 和 spec 内容分别用定制资源内的 `.status` 和 `.spec` JSON 路径来表达; - 对 `/status` 子资源的 `PUT` 请求要求使用定制资源对象作为其输入,但会忽略 status 之外的所有内容。 - 对 `/status` 子资源的 `PUT` 请求仅对定制资源的 status 内容进行合法性检查。 - 对定制资源的 `PUT`、`POST`、`PATCH` 请求会忽略 status 内容的改变。 - 对所有变更请求,除非改变是针对 `.metadata` 或 `.status`,`.metadata.generation` 的取值都会增加。 - 在 CRD OpenAPI 合法性检查模式定义的根节点,只允许存在以下结构: - `description` - `example` - `exclusiveMaximum` - `exclusiveMinimum` - `externalDocs` - `format` - `items` - `maximum` - `maxItems` - `maxLength` - `minimum` - `minItems` - `minLength` - `multipleOf` - `pattern` - `properties` - `required` - `title` - `type` - `uniqueItems` #### Scale 子资源 {#scale-subresource} 当启用了 scale 子资源时,定制资源的 `/scale` 子资源就被暴露出来。 针对 `/scale` 所发送的对象是 `autoscaling/v1.Scale`。 为了启用 scale 子资源,CustomResourceDefinition 定义了以下字段: - `specReplicasPath` 指定定制资源内与 `scale.spec.replicas` 对应的 JSON 路径。 - 此字段为必需值。 - 只可以使用 `.spec` 下的 JSON 路径,只可使用带句点的路径。 - 如果定制资源的 `specReplicasPath` 下没有取值,则针对 `/scale` 子资源执行 GET 操作时会返回错误。 - `statusReplicasPath` 指定定制资源内与 `scale.status.replicas` 对应的 JSON 路径。 - 此字段为必需值。 - 只可以使用 `.status` 下的 JSON 路径,只可使用带句点的路径。 - 如果定制资源的 `statusReplicasPath` 下没有取值,则针对 `/scale` 子资源的 副本个数状态值默认为 0。 - `labelSelectorPath` 指定定制资源内与 `scale.status.selector` 对应的 JSON 路径。 - 此字段为可选值。 - 此字段必须设置才能使用 HPA。 - 只可以使用 `.status` 或 `.spec` 下的 JSON 路径,只可使用带句点的路径。 - 如果定制资源的 `labelSelectorPath` 下没有取值,则针对 `/scale` 子资源的 选择算符状态值默认为空字符串。 - 此 JSON 路径所指向的字段必须是一个字符串字段(而不是复合的选择算符结构), 其中包含标签选择算符串行化的字符串形式。 在下面的例子中,`status` 和 `scale` 子资源都被启用。 将此 CustomResourceDefinition 保存到 `resourcedefinition.yaml` 文件: ```yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: crontabs.stable.example.com spec: group: stable.example.com versions: - name: v1 served: true storage: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: cronSpec: type: string image: type: string replicas: type: integer status: type: object properties: replicas: type: integer labelSelector: type: string # subresources 描述定制资源的子资源 subresources: # status 启用 status 子资源 status: {} # scale 启用 scale 子资源 scale: # specReplicasPath 定义定制资源中对应 scale.spec.replicas 的 JSON 路径 specReplicasPath: .spec.replicas # statusReplicasPath 定义定制资源中对应 scale.status.replicas 的 JSON 路径 statusReplicasPath: .status.replicas # labelSelectorPath 定义定制资源中对应 scale.status.selector 的 JSON 路径 labelSelectorPath: .status.labelSelector scope: Namespaced names: plural: crontabs singular: crontab kind: CronTab shortNames: - ct ``` 之后创建此 CustomResourceDefinition: ```shell kubectl apply -f resourcedefinition.yaml ``` CustomResourceDefinition 对象创建完毕之后,你可以创建定制对象,。 如果你将下面的 YAML 保存到 `my-crontab.yaml` 文件: ```yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata: name: my-new-cron-object spec: cronSpec: "* * * * */5" image: my-awesome-cron-image replicas: 3 ``` 并创建定制对象: ```shell kubectl apply -f my-crontab.yaml ``` 那么会创建新的、命名空间作用域的 RESTful API 端点: ``` /apis/stable.example.com/v1/namespaces/*/crontabs/status ``` 和 ``` /apis/stable.example.com/v1/namespaces/*/crontabs/scale ``` 定制资源可以使用 `kubectl scale` 命令来扩缩其规模。 例如下面的命令将前面创建的定制资源的 `.spec.replicas` 设置为 5: ```shell kubectl scale --replicas=5 crontabs/my-new-cron-object ``` ``` crontabs "my-new-cron-object" scaled ``` ```shell kubectl get crontabs my-new-cron-object -o jsonpath='{.spec.replicas}' ``` ``` 5 ``` 你可以使用 [PodDisruptionBudget](/zh/docs/tasks/run-application/configure-pdb/) 来保护启用了 scale 子资源的定制资源。 ### 分类 {#categories} 分类(Categories)是定制资源所归属的分组资源列表(例如,`all`)。 你可以使用 `kubectl get <分类名称>` 来列举属于某分类的所有资源。 下面的示例在 CustomResourceDefinition 中将 `all` 添加到分类列表中, 并展示了如何使用 `kubectl get all` 来输出定制资源: 将下面的 CustomResourceDefinition 保存到 `resourcedefinition.yaml` 文件中: ```yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: crontabs.stable.example.com spec: group: stable.example.com versions: - name: v1 served: true storage: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: cronSpec: type: string image: type: string replicas: type: integer scope: Namespaced names: plural: crontabs singular: crontab kind: CronTab shortNames: - ct # categories 是定制资源所归属的分类资源列表 categories: - all ``` 之后创建此 CRD: ```shell kubectl apply -f resourcedefinition.yaml ``` 创建了 CustomResourceDefinition 对象之后,你可以创建定制对象。 将下面的 YAML 保存到 `my-crontab.yaml` 中: ```yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata: name: my-new-cron-object spec: cronSpec: "* * * * */5" image: my-awesome-cron-image ``` 并创建定制对象: ```shell kubectl apply -f my-crontab.yaml ``` 你可以在使用 `kubectl get` 时指定分类: ```shell kubectl get all ``` 输出中会包含类别为 `CronTab` 的定制资源: ```console NAME AGE crontabs/my-new-cron-object 3s ``` ## {{% heading "whatsnext" %}} * 阅读了解[定制资源](/zh/docs/concepts/extend-kubernetes/api-extension/custom-resources/) * 参阅 [CustomResourceDefinition](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#customresourcedefinition-v1-apiextensions-k8s-io) * 参阅支持 CustomResourceDefinition 的[多个版本](/zh/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/)