Kubernetes CRD详解
# Kubernetes CRD详解
# CRD基本概念
# 定制资源
资源(Resource) 是 Kubernetes API 中的一个端点
, 其中存储的是某个类别的 API 对象 的一个集合。 例 如内置的 pods 资源包含一组 Pod 对象。定制资源(Custom Resource) 是对 Kubernetes API 的扩展
,不一定在默认的 Kubernetes 安装中就可 用。定制资源所代表的是对特定 Kubernetes 安装的一种定制。 不过,很多 Kubernetes 核心功能现在都用定 制资源来实现,这使得 Kubernetes 更加模块化。定制资源可以通过动态注册的方式在运行中的集群内或出现或消失
,集群管理员可以独立于集群 更新定制资源。一旦 某定制资源被安装,用户可以使用 kubectl 来创建和访问其中的对象,就像他们为 pods 这种内置资源所做的一 样。
# 定制控制器
- 就定制资源本身而言,它只能用来存取结构化的数据。 当你将定制资源与 定制控制器(Custom Controller) 相结合时,定制资源就能够 提供真正的 声明式 API(Declarative API)。
- 使用声明式 API, 你可以 声明 或者设定你的资源的期望状态,并尝试让 Kubernetes 对象的当前状态 同步到 其期望状态。控制器负责将结构化的数据解释为用户所期望状态的记录,并 持续地维护该状态。
- 你可以在一个运行中的集群上部署和更新定制控制器,这类操作与集群的生命周期无关。 定制控制器可以用于任何类 别的资源,不过它们与定制资源结合起来时最为有效。 Operator 模式就是将定制资源 与定制控制器相结合的。你 可以使用定制控制器来将特定于某应用的领域知识组织 起来,以编码的形式构造对 Kubernetes API 的扩展。
# 声明式 APIs
典型地,在声明式 API 中:
- 你的 API 包含相对而言为数不多的、尺寸较小的对象(资源)。
- 对象定义了应用或者基础设施的配置信息。
- 对象更新操作频率较低。
- 通常需要人来读取或写入对象。
- 对象的主要操作是 CRUD 风格的(创建、读取、更新和删除)。
- 不需要跨对象的事务支持:API 对象代表的是期望状态而非确切实际状态。
命令式 API(Imperative API)与声明式有所不同。 以下迹象表明你的 API 可能不是声明式的:
- 客户端发出“做这个操作”的指令,之后在该操作结束时获得同步响应。
- 客户端发出“做这个操作”的指令,并获得一个操作 ID,之后需要检查一个 Operation(操作) 对象来判断 请求是否成功完成。
- 你会将你的 API 类比为远程过程调用(Remote Procedure Call,RPCs)。
- 直接存储大量数据;例如每个对象几 kB,或者存储上千个对象。
- 需要较高的访问带宽(长期保持每秒数十个请求)。
- 存储有应用来处理的最终用户数据(如图片、个人标识信息(PII)等)或者其他大规模数据。
- 在对象上执行的常规操作并非 CRUD 风格。
- API 不太容易用对象来建模。
- 你决定使用操作 ID 或者操作对象来表现悬决的操作。
# 添加定制资源
Kubernetes 提供了两种方式供你向集群中添加定制资源:
CRD 相对简单,创建 CRD 可以不必编程。
API 聚合 需要编程,但支持对 API 行为进行更多的控制,例如数据如何存储以及在不同 API 版本间如何转 换等。
聚合 API 指的是一些下位的 API 服务器,运行在主 API 服务器后面
;主 API 服务器以代理的方式工作。这种 组织形式称作 API 聚合(API Aggregation,AA) 。 对用户而言,看起来仅仅是 Kubernetes API 被扩展 了。
CRD 允许用户创建新的资源类别同时又不必添加新的 API 服务器
。 使用 CRD 时,你并不需要理解 API 聚合。
# CustomResourceDefinitions
CustomResourceDefinition API 资源允许你定义定制资源。
定义 CRD 对象的操作会使用你所设定的名字和 模式定义(Schema)
创建一个新的定制资源, Kubernetes API 负责为你的定制资源提供存储和访问服务。 CRD 对象的名称必须是合法的 DNS 子域名。
CRD 使得你不必编写自己的 API 服务器来处理定制资源,不过其背后实现的通用性也意味着 你所获得的灵活性要 比 API 服务器聚合少很多。
# 比较易用性
CRD 比聚合 API 更容易创建
CRDs | 聚合 API |
---|---|
无需编程。用户可选择任何语言来实现 CRD 控制器。 | 需要使用 Go 来编程,并构建可执行文件和 镜像。 |
无需额外运行服务;CRD 由 API 服务器处理。 | 需要额外创建服务,且该服务可能失效。 |
一旦 CRD 被创建,不需要持续提供支持。Kubernetes 主控 节点升级过程中自动会带入缺陷修复。 | 可能需要周期性地从上游提取缺陷修复并更新 聚合 API 服务器。 |
无需处理 API 的多个版本;例如,当你控制资源的客户端 时,你可以更新它使之与 API 同步。 | 你需要处理 API 的多个版本;例如,在开发 打算与很多人共享的扩展时。 |
# CRD基本使用
# 如何使用
在K8S系统扩展点中,开发者可以通过CRD(CustomResourceDefinition)来扩展K8S API,其功能主要由APIExtensionServer负责。使用CRD扩展资源分为三步:
- 注册自定义资源:开发者需要通过K8S提供的方式注册自定义资源,即通过CRD进行注册,注册之后,K8S就知道我们自定义资源的存在了,然后我们就可以像使用K8S内置资源一样使用自定义资源(CR)
- 使用自定义资源:像内置资源比如Pod一样声明资源,使用CR声明我们的资源信息
- 删除自定义资源:当我们不再需要时,可以删除自定义资源
# 创建自定义资源
当你创建新的 CustomResourceDefinition(CRD)时,Kubernetes API 服务器会为你所 指定的每一个版本 生成一个 RESTful 的 资源路径。CRD 可以是名字空间作用域的,也可以 是集群作用域的,取决于 CRD 的 scope
字段设置。和其他现有的内置对象一样,删除 一个名字空间时,该名字空间下的所有定制对象也会被删 除。CustomResourceDefinition 本身是不受名字空间限制的,对所有名字空间可用。
将下面的 CustomResourceDefinition 保存到 resourcedefinition.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:
properties:
spec:
properties:
cronSpec:
type: string
image:
type: string
replicas:
type: integer
type: object
type: object
# 可以是 Namespaced 或 Cluster
scope: Namespaced
names:
# 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>
plural: crontabs
# kind 通常是单数形式的驼峰编码(CamelCased)形式。你的资源清单会使用这一形式。
singular: crontab
kind: CronTab
# shortNames 允许你在命令行使用较短的字符串来匹配资源
shortNames:
- ct
创建CRD资源,查看当前的新增加的自定义资源
[root@k8s-master-node1 ~]# kubectl apply -f resourcedefinition.yaml
customresourcedefinition.apiextensions.k8s.io/crontabs.stable.example.com created
[root@k8s-master-node1 ~]# kubectl get crd
NAME CREATED AT
crontabs.stable.example.com 2022-05-03T00:35:58Z
kuboardaddonresources.kuboard.cn 2022-05-02T23:38:10Z
kuboardaddons.kuboard.cn 2022-05-02T23:38:10Z
这样一个新的受名字空间约束的 RESTful API 端点会被创建在:
注意: 此端点 URL 自此可以用来创建和管理定制对象。对象的 kind 将是来自你上面创建时 所用的 spec 中指定的 CronTab 。
/apis/stable.example.com/v1/namespaces/*/crontabs/...
# 使用自定义资源
在创建了 CustomResourceDefinition 对象之后,你可以创建定制对象(Custom Objects)。定制对象可以 包含定制字段。这些字段可以包含任意的 JSON 数据。 在下面的例子中,在类别为 CrontTab
的定制对象中,设 置了 cronSpec
和 image
定制字段。类别 CronTab
来自你在上面所创建的 CRD 的规约。
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image
执行创建资源后,查看详细的信息
你可以看到输出中包含了你创建定制对象时在 YAML 文件中指定的定制字段 cronSpec 和 image :
[root@k8s-master-node1 ~]# kubectl apply -f my-crontab.yaml
crontab.stable.example.com/my-new-cron-object created
[root@k8s-master-node1 ~]# kubectl get crontabs.stable.example.com
NAME AGE
my-new-cron-object 11s
[root@k8s-master-node1 ~]# kubectl get crontabs.stable.example.com my-new-cron-object -o yaml
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
creationTimestamp: "2022-05-03T00:43:18Z"
generation: 1
name: my-new-cron-object
namespace: default
resourceVersion: "6570"
uid: 20b3d16b-5b44-4ec2-ac95-e5f5a9183b77
spec:
cronSpec: '* * * * */5'
image: my-awesome-cron-image
# 牛刀小试
创建一个自定义API资源exam,API资源类型为Scaling,作用域为命名空间。
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: exam.stable.example.com
spec:
group: stable.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
scope: Namespaced
names:
plural: exam
singular: exam
kind: Scaling
查看创建的结果:
[root@k8s-master-node1 ~]# kubectl api-resources | grep -w exam
exam stable.example.com/v1 true Scaling
# 设置结构化的模式
CustomResource 对象在定制字段中保存结构化的数据,这些字段和内置的字段 apiVersion
、 kind
和 metadata
等一起存储,不过内置的字段都会被 API 服务器隐式完成合法性检查。有了 OpenAPI v3.0 检查 能 力之后,你可以设置一个模式(Schema),在创建和更新定制对象时,这一模式会被用来 对对象内容进行合法性检 查。参阅下文了解这类模式的细节和局限性。
在 apiextensions.k8s.io/v1
版本中,CustomResourceDefinition 的这一结构化模式 定义是必需的。 在 CustomResourceDefinition 的 beta 版本中,结构化模式定义是可选的。
结构化模式本身是一个 OpenAPI v3.0 验证模式,其中:
- 为对象根(root)设置一个非空的 type 值(藉由 OpenAPI 中的
type
),对每个 object 节点的每个 字段(藉由 OpenAPI 中的properties
或additionalProperties
)以及 array 节点的每个条目(藉由 OpenAPI 中的items
)也要设置非空的 type 值, 除非:- 节点包含属性
x-kubernetes-int-or-string: true
- 节点包含属性
x-kubernetes-preserve-unknown-fields: true
- 节点包含属性
- 对于 object 的每个字段或 array 中的每个条目,如果其定义中包含
allOf
、anyOf
、oneOf
或not
,则模式也要指定这些逻辑组合之外的字段或条目(试比较例 1 和例 2)。 - 在
allOf
、anyOf
、oneOf
或not
上下文内不设置description
、type
、default
、additionalProperties
或者nullable
。此规则的例外是 x-kubernetes-int-or-string 的两种模式(见下 文)。 - 如果
metadata
被设置,则只允许对metadata.name
和metadata.generateName
设置约束。
非结构化的例 1:
allOf:
- properties:
foo:
违反了第 2 条规则。下面的是正确的:
properties:
foo:
allOf:
- properties:
foo:
非结构化的例 2:
allOf:
- items:
properties:
foo:
违反了第 2 条规则。下面的是正确的:
items:
properties:
foo:
allOf:
- items:
properties:
foo:
非结构化的例 3:
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 所对应的模式则是结构化的:
注意: 如果违反了结构化模式规则,CustomResourceDefinition 的 NonStructural 状况中 会包含报告信息。
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 在集群的持久性存储 etcd 中保存经过合法性检查的资源数据。 就像原生的 Kubernetes 资源,例如 ConfigMap, 如果你指定了 API 服务器所无法识别的字段,则该未知字段会在保存资 源之前 被 剪裁(Pruned) 掉(删除)。
将下面的 YAML 保存到 my-crontab.yaml 文件:
[root@k8s-master-node1 ~]# cat my-crontab.yaml
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-imagae
someRandomFied: 42
并创建之:
[root@k8s-master-node1 ~]# kubectl apply -f my-crontab.yaml --validate=false
crontab.stable.example.com/my-new-cron-object created
查看crontab的详细信息:
[root@k8s-master-node1 ~]# kubectl get crontabs.stable.example.com my-new-cron-object -o yaml
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
creationTimestamp: "2022-05-03T01:26:00Z"
generation: 1
name: my-new-cron-object
namespace: default
resourceVersion: "9919"
uid: 81f709aa-4b78-4f55-bf11-f51b4efab576
spec:
cronSpec: '* * * * */5'
image: my-awesome-cron-imagae
注意: 其中的字段 someRandomField 已经被剪裁掉。
通过 --validate=false
命令行选项 关闭了客户端的合法性检查以展示 API 服务器的行为, 因为 OpenAPI 合法性检查模式也会发布到 客户端, kubectl
也会检查未知的字段并在对象被发送到 API 服务器之前 就拒绝它们。
# CRD进阶
# Finalizers
Finalizer 能够让控制器实现异步的删除前(Pre-delete)回调。
与内置对象类似,定制对象也支持 Finalizer。
Finalizers的作用:
自定义 Finalizer 的标识符包含一个域名、一个正向斜线和 finalizer 的名称。
任何控制器都可以在任何对象 的 finalizer 列表中添加新的 finalizer。- 对带有 Finalizer 的对象的第一个删除请求会为其 metadata.deletionTimestamp 设置一个值,但不会真的删除对 象。一旦此值被设置, finalizers 列表中的表项 只能被移除。在列表中仍然包含 finalizer 时,无法强制删除 对应的对象。
- 当 metadata.deletionTimestamp 字段被设置时,监视该对象的各个控制器会 执行它们所能处理的 finalizer,并 在完成处理之后将其从列表中移除。 每个控制器负责将其 finalizer 从列表中删除。
- metadata.deletionGracePeriodSeconds 的取值控制对更新的轮询周期。 一旦 finalizers 列表为空时,就意味着所有 finalizer 都被执行过, Kubernetes 会最终删除该资源。
你可以像下面一样为定制对象添加 Finalizer:
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-new-cron-object
finalizers:
- stable.example.com/finalizer
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-imagae
# 合法性检查
定制资源是通过 OpenAPI v3 模式定义 来执行合法性检查的, 你可以通过使用准入控制 Webhook 来添加额外的合法性检查 。
此外,对模式定义存在以下限制:
以下字段不可设置:
definitions
dependencies
deprecated
discriminator
id
patternProperties
readOnly
writeOnly
xml
$ref
字段
uniqueItems
不可设置为true
字段
additionalProperties
不可设置为false
字段
additionalProperties
与properties
互斥,不可同时使用
当设置默认值特性被启用时,可以设置字段 default
。 就 apiextensions.k8s.io/v1
组的 CustomResourceDefinitions,这一条件是满足的。 设置默认值的功能特性从 1.17 开始正式发布。该特性在 1.16 版本中处于 Beta 状态,要求 CustomResourceDefaulting
特性门控 被启用。对于大多数集群而言,Beta 状态的特性门控默认都是自动启用的。
模式定义是在 CustomResourceDefinition 中设置的。在下面的例子中, CustomResourceDefinition 对 定制对象执行以下合法性检查:
spec.cronSpec
必须是一个字符串,必须是正则表达式所描述的形式;spec.replicas
必须是一个整数,且其最小值为 1、最大值为 10。
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
# 添加正则表达式
pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
image:
type: string
replicas:
type: integer
# 设置副本数为的最小值和最大值
minimum: 1
maximum: 10
# 可以是 Namespaced 或 Cluster
scope: Namespaced
names:
# 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>
plural: crontabs
# kind 通常是单数形式的驼峰编码(CamelCased)形式。你的资源清单会使用这一形式。
singular: crontab
kind: CronTab
# shortNames 允许你在命令行使用较短的字符串来匹配资源
shortNames:
- ct
并创建 CustomResourceDefinition:
[root@k8s-master-node1 ~]# kubectl apply -f resourcedefinition.yaml
对于一个创建 CronTab 类别对象的定制对象的请求而言,如果其字段中包含非法值,则 该请求会被拒绝。 在下面 的例子中,定制对象中包含带非法值的字段:
spec.cronSpec
与正则表达式不匹配spec.replicas
数值大于 10。
如果你将下面的 YAML 保存到 my-crontab.yaml :
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * *"
image: my-awesome-cron-imagae
replicas: 15
尝试创建对象,则会报如下错误:
[root@k8s-master-node1 ~]# kubectl apply -f my-crontab.yaml
The CronTab "my-new-cron-object" is invalid:
* spec.replicas: Invalid value: 15: spec.replicas in body should be less than or equal to 10
* spec.cronSpec: Invalid value: "* * * *": spec.cronSpec in body should match '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
如果所有字段都包含合法值,则对象创建的请求会被接受。
将下面的 YAML 保存到 my-crontab.yaml 文件:
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-imagae
replicas: 5
并创建定制对象:
[root@k8s-master-node1 ~]# kubectl apply -f my-crontab.yaml
crontab "my-new-cron-object" created
# 设置默认值
说明: 要使用设置默认值功能,你的 CustomResourceDefinition 必须使用 API 版本 apiextensions.k8s.io/v1 。
设置默认值的功能允许在 OpenAPI v3 合法性检查模式定义中设置默认值:
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 都会被设置默认值:
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-new-cron-object
spec:
image: my-awesome-cron-imagae
会生成:
[root@k8s-master-node1 ~]# kubectl get crontabs.stable.example.com my-new-cron-object -o yaml
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
creationTimestamp: "2022-05-03T16:41:05Z"
generation: 1
name: my-new-cron-object
namespace: default
resourceVersion: "81618"
uid: 5127b8d6-46c1-48c3-8b92-7cce6c7d3929
spec:
# 这里默认设置了参数
cronSpec: 5 0 * * *
image: my-awesome-cron-imagae
# 这里也默认设置了参数
replicas: 1
默认值设定的行为发生在定制对象上:
- 在向 API 服务器发送的请求中,基于请求版本的设定设置默认值;
- 在从 etcd 读取对象时,使用存储版本来设置默认值;
- 在 Mutating 准入控制插件执行非空的补丁操作时,基于准入 Webhook 对象 版本设置默认值。
# Nullable
设置默认值和字段是否可为空(Nullable)
1.20 版本新增: 对于未设置其 nullable 标志的字段或者将该标志设置为 false
的字段,其空值(Null)会 在设置默认值之前被剪裁掉。如果对应字段 存在默认值,则默认值会被赋予该字段。当 nullable
被设置为 true
时, 字段的空值会被保留,且不会在设置默认值时被覆盖。
例如,给定下面的 OpenAPI 模式定义:
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:
# 设置nullable的可选参数选项
foo:
type: string
nullable: false
default: "default"
bar:
type: string
nullable: true
baz:
type: string
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
···
像下面这样创建一个为 foo 、 bar 和 baz 设置空值的对象时:
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-new-cron-object
spec:
image: my-awesome-cron-imagae
foo: null
bar: null
baz: null
其返回的结果将会是这样的:
其中的 foo 字段被剪裁掉并重新设置默认值,因为该字段是不可为空的。
bar
字段的nullable: true
使 得其能够保有其空值。baz 字段则被完全剪裁掉
,因为该字段是不可为空的,并且没有默认值设置。
[root@k8s-master-node1 ~]# kubectl get crontab my-new-cron-object -o yaml
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
creationTimestamp: "2022-05-03T17:14:31Z"
generation: 1
name: my-new-cron-object
namespace: default
resourceVersion: "84245"
uid: 73f5fe25-0aac-4631-b33a-a2ed3acebb8b
spec:
bar: null
cronSpec: 5 0 * * *
foo: default
image: my-awesome-cron-imagae
replicas: 1
# OpenAPI v2 形式发布合法性检查模式
CustomResourceDefinition 的结构化的、 启用了剪裁的 OpenAPI v3 合法性检查模式 会在 Kubernetes API 服务器上作为 OpenAPI v2 规约 的一部分发布出来。
kubectl 命令行工具会基于所发布的模式定义来执行 客户端的合法性检查( kubectl create
和 kubectl apply
),为定制资源的模式定义 提供解释( kubectl explain
)。 所发布的模式还可被用于其他目的,例如生成 客户端或者生成文档。
OpenAPI v3 合法性检查模式定义会被转换为 OpenAPI v2 模式定义,并出现在 OpenAPI v2 规范 的 definitions 和 paths 字段中。
在转换过程中会发生以下修改,目的是保持与 1.13 版本以前的 kubectl 工具兼容。 这些修改可以避免 kubectl 过于严格,以至于拒绝它无法理解的 OpenAPI 模式定义。 转换过程不会更改 CRD 中定义的合法性检查 模式定义,因此不会影响到 API 服务器中 的合法性检查。
- 以下字段会被移除,因为它们在 OpenAPI v2 中不支持(在将来版本中将使用 OpenAPI v3, 因而不会有 这些限制) 字段
allOf
、anyOf
、oneOf
和not
会被删除 - 如果设置了
nullable: true
,我们会丢弃type
、nullable
、items
和properties
OpenAPI v2 无法 表达 Nullable。为了避免 kubectl 拒绝正常的对象,这一转换是必要的。
# 额外的打印列
kubectl
工具依赖服务器端的输出格式化。你的集群的 API 服务器决定 kubectl get
命令要显示的列有哪 些。 你可以为 CustomResourceDefinition 定制这些要打印的列。 下面的例子添加了 Spec
、 Replicas
和 Age
列:
将此 CustomResourceDefinition 保存到 resourcedefinition.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
# 添加额外的打印
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
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
创建 CustomResourceDefinition:
[root@k8s-master-node1 ~]# kubectl apply -f resourcedefinition.yaml
使用创建好的crontab实例,查看当前的状态。
注意输出中的 NAME 、 SPEC 、 REPLICAS 和 AGE 列:
说明: NAME 列是隐含的,不需要在 CustomResourceDefinition 中定义。
[root@k8s-master-node1 ~]# kubectl get crontabs my-new-cron-object
NAME SPEC REPLICAS AGE
my-new-cron-object 5 0 * * * 1 38m
# 优先级
每个列都包含一个 priority
(优先级)字段。当前,优先级用来区分标准视图(Standard View)和宽视图 (Wide View)(使用 -o wide
标志)中显示的列:
- 优先级为
0
的列会在标准视图中显示。 - 优先级大于
0
的列只会在宽视图中显示。
# 类型
列的 type 字段可以是以下值之一 (比较 OpenAPI v3 数据类型):
integer
– 非浮点数字number
– 浮点数字string
– 字符串boolean
–true
或false
date
– 显示为以自此时间戳以来经过的时长
# 格式
列的 format 字段可以是以下值之一:
int32
int64
float
double
byte
date
date-time
password
列的 format 字段控制 kubectl 打印对应取值时采用的风格。
# 子资源
定制资源支持 /status
和 /scale
子资源。 通过在 CustomResourceDefinition 中定义 status
和 scale
, 可以有选择地启用这些子资源。
# Status 子资源
当启用了 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 子资源时,定制资源的 /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
文件:
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
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 对象创建完毕之后,你可以创建定制对象。
如果你将下面的 YAML 保存到 my-crontab.yaml 文件:
[root@k8s-master-node1 ~]# cat my-crontab.yaml
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-new-cron-object
spec:
image: my-awesome-cron-imagae
cronSpec: "* * * * */5"
replicas: 3
定制资源可以使用 kubectl scale 命令来扩缩其规模。 例如下面的命令将前面创建的定制资源的 .spec.replicas 设置为 5:
注意: 你可以使用 PodDisruptionBudget 来保护启用了 scale 子资源的定制资源。
[root@k8s-master-node1 ~]# kubectl apply -f my-crontab.yaml
crontab.stable.example.com/my-new-cron-object created
[root@k8s-master-node1 ~]# kubectl scale --replicas=5 crontabs.stable.example.com my-new-cron-object
crontab.stable.example.com/my-new-cron-object scaled
[root@k8s-master-node1 ~]# kubectl get crontabs.stable.example.com my-new-cron-object -o jsonpath='{.spec.replicas}'
5
# 分类
分类(Categories)是定制资源所归属的分组资源列表(例如, all
)。 你可以使用 kubectl get <分类名称>
来列举属于某分类的所有资源。
下面的示例在 CustomResourceDefinition 中将 all
添加到分类列表中, 并展示了如何使用 kubectl get all
来输出定制资源:
将下面的 CustomResourceDefinition 保存到 resourcedefinition.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
image:
type: string
replicas:
type: integer
status:
type: object
properties:
replicas:
type: integer
labelSelector:
type: string
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
# 添加一个分类归属
categories:
- crontab-all
创建了 CustomResourceDefinition 对象之后,你可以创建定制对象。
将下面的 YAML 保存到 my-crontab.yaml
中:
[root@k8s-master-node1 ~]# cat my-crontab.yaml
apiVersion: stable.example.com/v1
kind: CronTab
metadata:
name: my-new-cron-object
spec:
image: my-awesome-cron-imagae
cronSpec: "* * * * */5"
replicas: 3
[root@k8s-master-node1 ~]# kubectl apply -f my-crontab.yaml
crontab.stable.example.com/my-new-cron-object unchanged
'你可以在使用 kubectl get 时指定分类:'
[root@k8s-master-node1 ~]# kubectl get crontab-all
NAME AGE
my-new-cron-object 2m3s
# CRD的版本
# 概览
CustomResourceDefinition API 提供了用于引入和升级的工作流程到 CustomResourceDefinition 的新 版本。
创建 CustomResourceDefinition 时,会在 CustomResourceDefinition spec.versions 列表设置适当 的稳定级别和版本号。例如, v1beta1 表示第一个版本尚未稳定。 所有定制资源对象将首先用这个版本保存。
创建 CustomResourceDefinition 后,客户端可以开始使用 v1beta1 API,稍后可能需要添加新版本,例如 v1 。
添加新版本:
- 选择一种转化策略。由于定制资源对象需要能够两种版本都可用, 这意味着它们有时会以与存储版本不同的版 本来提供服务。为了能够做到这一点, 有时必须在它们存储的版本和提供的版本之间进行转换。如果转换涉及 模式变更, 并且需要自定义逻辑,则应该使用 Webhook 来完成。如果没有模式变更, 则可使用默认的
None
转换策略,为不同版本提供服务时只有apiVersion
字段 会被改变。 - 如果使用转换 Webhook,请创建并部署转换 Webhook。更多详细信息请参见 Webhook conversion。
- 更新 CustomResourceDefinition,将新版本设置为
served:true
,加入到spec.versions
列表。另 外,还要设置spec.conversion
字段 为所选的转换策略。如果使用转换 Webhook,请配置spec.conversion.webhookClientConfig
来调用 Webhook。
对于客户来说,在将对象升级到新的存储版本之前、期间和之后使用旧版本和新版本都是安全的。
删除旧版本:
- 确保所有客户端都已完全迁移到新版本。 可以查看 kube-apiserver 的日志以识别仍通过旧版本进行访问 的所有客户端。
- 在
spec.versions
列表中将旧版本的served
设置为false
。 如果仍有客户端意外地使用旧版本,他们 可能开始会报告采用旧版本尝试访 定制资源的错误消息。 如果发生这种情况,请将旧版本的served:true
恢 复,然后迁移余下的客户端 使用新版本,然后重复此步骤。 - 确保已完成将现有对象升级到新存储版本 的步骤。
- CustomResourceDefinition 的
spec.versions
列表中,确认新版本的stored
已被设置为true
。 - 确认旧版本不在 CustomResourceDefinition
status.storedVersions
中。
- CustomResourceDefinition 的
- 从 CustomResourceDefinition
spec.versions
列表中删除旧版本。 - 在转换 Webhooks 中放弃对旧版本的转换支持。
# 指定多个版本
CustomResourceDefinition API 的 versions
字段可用于支持你所开发的 定制资源的多个版本。版本可以 具有不同的模式,并且转换 Webhooks 可以在多个版本之间转换定制资源。 在适当的情况下,Webhook 转换应遵 循 Kubernetes API 约定。 尤其是,请查阅 API 变更文档 以了解一些有用的常见错误和建议。
说明: 在 apiextensions.k8s.io/v1beta1 版本中曾经有一个 version 字段, 名字不叫做 versions 。该 version 字 段已经被废弃,成为可选项。 不过如果该字段不是空,则必须与 versions 字段中的第一个条目匹配。
下面的示例显示了两个版本的 CustomResourceDefinition。 第一个例子中假设所有的版本使用相同的模式而它 们之间没有转换。 YAML 中的注释提供了更多背景信息。
apiextensions.k8s.io/v1
apiextensions.k8s.io/v1beta1
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
# name 必须匹配后面 spec 中的字段,且使用格式 <plural>.<group>
name: crontabs.stable.example.com
spec:
# 组名,用于 REST API: /apis/<group>/<version>
group: stable.example.com
# 此 CustomResourceDefinition 所支持的版本列表
versions:
- name: v1beta1
# 每个 version 可以通过 served 标志启用或禁止
served: true
# 有且只能有一个 version 必须被标记为存储版本
storage: true
# schema 是必需字段
schema:
# openAPIV3Schema 是用来检查定制对象的模式定义
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
# conversion 节是 Kubernetes 1.13+ 版本引入的,其默认值为无转换,即
# strategy 子字段设置为 None。
conversion:
# None 转换假定所有版本采用相同的模式定义,仅仅将定制资源的 apiVersion
# 设置为合适的值.
strategy: None
# 可以是 Namespaced 或 Cluster
scope: Namespaced
names:
# 名称的复数形式,用于 URL: /apis/<group>/<version>/<plural>
plural: crontabs
# 名称的单数形式,用于在命令行接口和显示时作为其别名
singular: crontab
# kind 通常是驼峰编码(CamelCased)的单数形式,用于资源清单中
kind: CronTab
# shortNames 允许你在命令行接口中使用更短的字符串来匹配你的资源
shortNames:
- ct
注意: 在创建之后,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
后的顺序排序, 版本优先级 CustomResourceDefinition 的版本 - 如果
beta
或alpha
之后还有另一个数字,那么也会针对这些数字 从大到小排序。 - 不符合上述格式的字符串按字母顺序排序,数字部分不经过特殊处理。 请注意,在下面的示例中, foo1 排在 foo10 之前。 这与遵循 Kubernetes 版本模式的条目的数字部分排序不同。
如果查看以下版本排序列表,这些规则就容易懂了:
- v10
- v2
- v1
- v11beta2
- v10beta3
- v3beta1
- v12alpha1
- v11alpha2
- foo1
- foo10
# 版本废弃
从 v1.19 开始,CustomResourceDefinition 可用来标明所定义的资源的特定版本 被废弃。当发起对已废弃的 版本的 API 请求时,会在 API 响应中以 HTTP 头部 的形式返回警告消息。 如果需要,可以对资源的每个废弃版 本定制该警告消息。
定制的警告消息应该标明废弃的 API 组、版本和类别(kind),并且应该标明 应该使用(如果有的话)哪个 API 组、版本和类别作为替代。
apiextensions.k8s.io/v1
apiextensions.k8s.io/v1beta1
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: crontabs.example.com
spec:
group: example.com
names:
plural: crontabs
singular: crontab
kind: CronTab
scope: Namespaced
versions:
- name: v1alpha1
served: true
storage: false
# 此属性标明此定制资源的 v1alpha1 版本已被弃用。
# 发给此版本的 API 请求会在服务器响应中收到警告消息头。
deprecated: true
# 此属性设置用来覆盖返回给发送 v1alpha1 API 请求的客户端的默认警告信息。
deprecationWarning: "example.com/v1alpha1 CronTab is deprecated; Please Update !!!"
schema:
openAPIV3Schema:
type: object
properties:
txt:
type: string
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
txt:
type: string
通过创建v1alpha1
版本的资源,触发告警。
[root@k8s-master-node1 ~]# cat test-crontab.yaml
apiVersion: example.com/v1alpha1
kind: CronTab
metadata:
name: tes
[root@k8s-master-node1 ~]# kubectl apply -f test-crontab.yaml
Warning: example.com/v1alpha1 CronTab is deprecated; Please Update !!!
crontab.example.com/test created