Kubernetes——自定义资源类型(CRD)
自定义资源类型(CRD)
Kubernetes 系统的扩展和增强既包括扩展 API Server 所支持的资源类型及相关声明式功能的实现,以及消除集群的单点以实现集群的高可用等,也包括如何将系统增强为一个完整意义上的 PaaS 平台,并以 DevOps 文化为驱动改善工作流程等。
Kubernetes API 默认提供的众多的功能性资源类型可用于容器编排以解决多数场景中的编排需求。但是,有些场景也需要借助于额外的或更高级别的编排抽象,例如引入集群外部的一些服务并以资源对象的形式进行管理,或者把 Kubernetes 的多个标准资源对象合并为一个单一的更高级别的资源抽象等等。而这类的 API 扩展抽象通常也应该兼容 Kubernetes 系统的基本特性,如支持 kubectl 管理工具、CRUD 及 watch 机制、标签、etcd 存储、认证、授权、RBAC 及审计,等等,从而使得用户可将精力集中于构建业务逻辑本身。
目前,扩展 Kubernetes API 的常用方式有三种:
- 使用 CRD(CustomResourceDefinitions)自定义资源类型。
- 开发自定义的 API Server 并聚合至主 API Server。
- 定制扩展 Kubernetes 源码。
其中,CRD 最易用但限制较多,自定义 API Server 更富有弹性但代码工作量偏大,而仅在必须添加新的核心类型才能确保专用的 Kubernetes 集群功能正常时才应该定制系统源码。
CRD 并非设计用来取代 Kubernetes 的原生资源类型,而是用于补充一种简单易用的更为灵活和更高级别的自定义 API 资源的方式。虽然目前在功能上仍存在不少的局限,但对于大多数的需求场景来说,CRD 的表现已经足够好,因此在满足需求的前提下是首选的 API 资源类型扩展方案。
一、创建 CRD 对象
CRD 自 Kubernetes 1.7 版开始引入,并自 1.8 版起完全取代其前身 TPR(ThirdPartyResources),其设计目标是通过 CRD 可以向 API 中增加新资源类型,无须修改 Kubernetes 源代码就能扩展它支持使用 API 资源类型。该功能大大提高了 Kubernetes 的可扩展能力。
CRD 本身也是一种资源类型,是一种无须编码就可以扩展原生 Kubernetes API 接口的形式,隶属于集群级别。实例化出特定的对象之后,它会在 API 上注册生成 GVR 类型 URL 端点,并能够作为一种资源类型被使用并实例化相应的对象。自定义资源类型之前,选定其使用的 API 群租名称、版本及新建的资源类型名称,根据这些信息即可创建自定义资源类型,并创建自定义类型的资源对象。
CRD 高级特性:
-
- 支持 subresource,比如 status 或者 scale。
- finalizer,在删除自定义资源的同时去做垃圾回收。
- controller,通过 client-go 中的 informer,实现更强大的功能。
- crd 的 validation 可对资源中的字段进行验证,设置默认值。
CustomResourceDefinition 字段定义如下:
[root@k8s-master01-test-2-26 ~]# kubectl explain customresourcedefinition
KIND: CustomResourceDefinition
VERSION: apiextensions.k8s.io/v1
DESCRIPTION:
CustomResourceDefinition represents a resource that should be exposed on
the API server. Its name MUST be in the format <.spec.name>.<.spec.group>.
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
metadata <Object>
Standard object's metadata More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
spec <Object> -required-
spec describes how the user wants the resources to appear
status <Object>
status indicates the actual state of the CustomResourceDefinition
[root@k8s-master01-test-2-26 ~]#
CustomResourceDefinition.spec 字段定义如下:
[root@k8s-master01-test-2-26 ~]# kubectl explain customresourcedefinition.spec
KIND: CustomResourceDefinition
VERSION: apiextensions.k8s.io/v1
RESOURCE: spec <Object>
DESCRIPTION:
spec describes how the user wants the resources to appear
CustomResourceDefinitionSpec describes how a user wants their resource to
appear
FIELDS:
conversion <Object>
conversion defines conversion settings for the CRD.
group <string> -required-
group is the API group of the defined custom resource. The custom resources
are served under `/apis/<group>/...`. Must match the name of the
CustomResourceDefinition (in the form `<names.plural>.<group>`).
names <Object> -required-
names specify the resource and kind names for the custom resource.
preserveUnknownFields <boolean>
preserveUnknownFields indicates that object fields which are not specified
in the OpenAPI schema should be preserved when persisting to storage.
apiVersion, kind, metadata and known fields inside metadata are always
preserved. This field is deprecated in favor of setting
`x-preserve-unknown-fields` to true in
`spec.versions[*].schema.openAPIV3Schema`. See
https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#pruning-versus-preserving-unknown-fields
for details.
scope <string> -required-
scope indicates whether the defined custom resource is cluster- or
namespace-scoped. Allowed values are `Cluster` and `Namespaced`.
versions <[]Object> -required-
versions is the list of all API versions of the defined custom resource.
Version names are used to compute the order in which served versions are
listed in API discovery. If the version string is "kube-like", it will sort
above non "kube-like" version strings, which are ordered lexicographically.
"Kube-like" versions start with a "v", then are followed by a number (the
major version), then optionally the string "alpha" or "beta" and another
number (the minor version). These are sorted first by GA > beta > alpha
(where GA is a version with no suffix such as beta or alpha), and then by
comparing major version, then minor version. An example sorted list of
versions: v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2,
foo1, foo10.
[root@k8s-master01-test-2-26 ~]#
customresourcedefinition.spce 常用字段定义如下:
- conversion <Object>: 定义 CRD 的不同版本之间的格式转换方式。有两个子选项: strategy 和 webhook。
- group <string> -required-: 定义了自定义资源所属的 API 组。自定义资源在`/apis/<group>/…`下提供。必须与 CustomResourceDefinition(格式为 `<names.plural>.<group>`)相同。
- names <Object> -required-: 指定自定义资源的资源和种类名称。
- preserveUnknownFields <boolean>: 表示未指定的对象字段。
- scope <string> -required-: 指示定义的自定义资源是 cluster or Namespace-scoped。允许的值为 'Cluster' 和 'Namespaced'。
- versions <[]Object> -required-: 定义自定义资源的所有API版本的列表。
先看看系统中有哪些 CRD 资源,然后我们再自己创建一个 CRD 资源:
[root@k8s-master01-test-2-26 ~]# kubectl get CustomResourceDefinition
NAME CREATED AT
accessors.storage.kubesphere.io 2022-07-01T02:20:20Z
alertmanagerconfigs.monitoring.coreos.com 2022-07-01T02:23:04Z
alertmanagers.monitoring.coreos.com 2022-07-01T02:23:08Z
applications.app.k8s.io 2022-07-01T02:19:04Z
applications.argoproj.io 2022-07-01T02:22:10Z
applications.gitops.kubesphere.io 2022-07-01T02:22:38Z
applicationsets.argoproj.io 2022-07-01T02:22:10Z
appprojects.argoproj.io 2022-07-01T02:22:11Z
argocdextensions.argoproj.io 2022-07-01T02:22:11Z
authorizationpolicies.security.istio.io 2022-07-01T02:21:41Z
bgpconfigurations.crd.projectcalico.org 2022-07-01T02:12:34Z
bgppeers.crd.projectcalico.org 2022-07-01T02:12:34Z
blockaffinities.crd.projectcalico.org 2022-07-01T02:12:35Z
clusterconfigurations.installer.kubesphere.io 2022-07-01T02:12:51Z
clusterdashboards.monitoring.kubesphere.io 2022-07-01T02:28:13Z
clusterinformations.crd.projectcalico.org 2022-07-01T02:12:35Z
clusters.cluster.kubesphere.io 2022-07-01T02:19:19Z
clustertemplates.devops.kubesphere.io 2022-07-01T02:22:36Z
configs.notification.kubesphere.io 2022-07-01T02:27:24Z
dashboards.monitoring.kubesphere.io 2022-07-01T02:28:13Z
destinationrules.networking.istio.io 2022-07-01T02:21:42Z
devopsprojects.devops.kubesphere.io 2022-07-01T02:22:37Z
envoyfilters.networking.istio.io 2022-07-01T02:21:42Z
exporters.events.kubesphere.io 2022-07-01T02:21:33Z
federatedrolebindings.iam.kubesphere.io 2022-07-01T02:19:28Z
federatedroles.iam.kubesphere.io 2022-07-01T02:19:31Z
federatedusers.iam.kubesphere.io 2022-07-01T02:19:33Z
felixconfigurations.crd.projectcalico.org 2022-07-01T02:12:35Z
filters.logging.kubesphere.io 2022-07-01T02:17:40Z
fluentbitconfigs.logging.kubesphere.io 2022-07-01T02:17:41Z
fluentbits.logging.kubesphere.io 2022-07-01T02:17:41Z
gateways.gateway.kubesphere.io 2022-07-01T02:19:23Z
gateways.networking.istio.io 2022-07-01T02:21:42Z
gitrepositories.devops.kubesphere.io 2022-07-01T02:22:37Z
globalnetworkpolicies.crd.projectcalico.org 2022-07-01T02:12:35Z
globalnetworksets.crd.projectcalico.org 2022-07-01T02:12:35Z
globalrolebindings.iam.kubesphere.io 2022-07-01T02:19:35Z
globalroles.iam.kubesphere.io 2022-07-01T02:19:38Z
groupbindings.iam.kubesphere.io 2022-07-01T02:19:41Z
groups.iam.kubesphere.io 2022-07-01T02:19:44Z
helmapplications.application.kubesphere.io 2022-07-01T02:19:07Z
helmapplicationversions.application.kubesphere.io 2022-07-01T02:19:09Z
helmcategories.application.kubesphere.io 2022-07-01T02:19:12Z
helmreleases.application.kubesphere.io 2022-07-01T02:19:14Z
helmrepos.application.kubesphere.io 2022-07-01T02:19:17Z
hostendpoints.crd.projectcalico.org 2022-07-01T02:12:35Z
inputs.logging.kubesphere.io 2022-07-01T02:17:41Z
ipamblocks.crd.projectcalico.org 2022-07-01T02:12:35Z
ipamblocks.network.kubesphere.io 2022-07-01T02:20:00Z
ipamconfigs.crd.projectcalico.org 2022-07-01T02:12:35Z
ipamhandles.crd.projectcalico.org 2022-07-01T02:12:36Z
ipamhandles.network.kubesphere.io 2022-07-01T02:20:03Z
ippools.crd.projectcalico.org 2022-07-01T02:12:36Z
ippools.network.kubesphere.io 2022-07-01T02:20:06Z
istiooperators.install.istio.io 2022-07-01T02:21:42Z
jaegers.jaegertracing.io 2022-07-01T02:23:06Z
kialis.kiali.io 2022-07-01T02:23:39Z
kubecontrollersconfigurations.crd.projectcalico.org 2022-07-01T02:12:36Z
loginrecords.iam.kubesphere.io 2022-07-01T02:19:46Z
namespacenetworkpolicies.network.kubesphere.io 2022-07-01T02:20:09Z
networkpolicies.crd.projectcalico.org 2022-07-01T02:12:36Z
networksets.crd.projectcalico.org 2022-07-01T02:12:36Z
nginxes.gateway.kubesphere.io 2022-07-01T02:19:25Z
notificationmanagers.notification.kubesphere.io 2022-07-01T02:27:24Z
outputs.logging.kubesphere.io 2022-07-01T02:17:41Z
parsers.logging.kubesphere.io 2022-07-01T02:17:41Z
peerauthentications.security.istio.io 2022-07-01T02:21:42Z
pipelineruns.devops.kubesphere.io 2022-07-01T02:22:37Z
pipelines.devops.kubesphere.io 2022-07-01T02:22:37Z
podmonitors.monitoring.coreos.com 2022-07-01T02:23:12Z
probes.monitoring.coreos.com 2022-07-01T02:23:27Z
prometheuses.monitoring.coreos.com 2022-07-01T02:24:56Z
prometheusrules.monitoring.coreos.com 2022-07-01T02:24:11Z
receivers.notification.kubesphere.io 2022-07-01T02:27:25Z
requestauthentications.security.istio.io 2022-07-01T02:21:42Z
resourcequotas.quota.kubesphere.io 2022-07-01T02:20:12Z
rolebases.iam.kubesphere.io 2022-07-01T02:19:49Z
rulers.events.kubesphere.io 2022-07-01T02:21:33Z
rules.auditing.kubesphere.io 2022-07-01T02:21:23Z
rules.events.kubesphere.io 2022-07-01T02:21:33Z
s2ibinaries.devops.kubesphere.io 2022-07-01T02:22:39Z
s2ibuilders.devops.kubesphere.io 2022-07-01T02:22:39Z
s2ibuildertemplates.devops.kubesphere.io 2022-07-01T02:22:39Z
s2iruns.devops.kubesphere.io 2022-07-01T02:22:40Z
serviceentries.networking.istio.io 2022-07-01T02:21:43Z
servicemonitors.monitoring.coreos.com 2022-07-01T02:24:17Z
servicepolicies.servicemesh.kubesphere.io 2022-07-01T02:20:14Z
sidecars.networking.istio.io 2022-07-01T02:21:43Z
strategies.servicemesh.kubesphere.io 2022-07-01T02:20:18Z
telemetries.telemetry.istio.io 2022-07-01T02:21:43Z
templates.devops.kubesphere.io 2022-07-01T02:22:37Z
thanosrulers.monitoring.coreos.com 2022-07-01T02:24:26Z
users.iam.kubesphere.io 2022-07-01T02:19:52Z
virtualservices.networking.istio.io 2022-07-01T02:21:43Z
volumesnapshotclasses.snapshot.storage.k8s.io 2022-07-01T02:15:14Z
volumesnapshotcontents.snapshot.storage.k8s.io 2022-07-01T02:15:14Z
volumesnapshots.snapshot.storage.k8s.io 2022-07-01T02:15:14Z
webhooks.auditing.kubesphere.io 2022-07-01T02:21:23Z
workloadentries.networking.istio.io 2022-07-01T02:21:43Z
workloadgroups.networking.istio.io 2022-07-01T02:21:43Z
workspacerolebindings.iam.kubesphere.io 2022-07-01T02:19:55Z
workspaceroles.iam.kubesphere.io 2022-07-01T02:19:58Z
workspaces.tenant.kubesphere.io 2022-07-01T02:20:23Z
workspacetemplates.tenant.kubesphere.io 2022-07-01T02:20:25Z
[root@k8s-master01-test-2-26 ~]#
下面的配置清单中定义了一个名为 user.auth.zuoyang.tech 的 CRD 资源对象,它的群组名称为为 auth.zuoyang.tech,仅支持一个版本级别 v1alpha1,复数形式为 usters,隶属于名称空间级别,因此,它的对象在 API 上的 URL 路径前缀为 /apis/auth.zuoyang.tech/v1alpha1/namespaces/NS_NAME/users/:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: users.auth.zuoyang.tech
spec:
group: auth.zuoyang.tech
names:
kind: User
plural: users
singular: user
shortNames:
- u
scope: Namespaced
versions:
- served: true
storage: true
name: v1alpha1
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
userID:
type: integer
minimum: 1
maximum: 65535
groups :
type: array
items:
type: string
email:
type: string
password:
type: string
format: password
required: ["userID","groups"]
配置清单中,spec 嵌套使用的字段都能够见名知意,这里需要特别说明的是,metadata.name 字段的值必须等同于 spec 字段中的 "<name.plural>.<group>" 合并起来的形式。
清单中的 CRD 资源创建于 Kubernetes 集群中之后,继而创建一个新的 CustomResourceDefinition 类型的对象,例如,使用下面的命令列出集群上的 CRD 对象时,命令结果将显示出如下资源名称及创建时间状态信息:
[root@k8s-master01-test-2-26 ~]# kubectl get crd |grep zuoyang
users.auth.zuoyang.tech 2022-07-07T07:48:05Z
[root@k8s-master01-test-2-26 ~]#
现在,在 API 群组 auth.zuoyang.tech/v1beta1 中,users 已经是一个名称空间级别的可用资源类型,用不可按需创建出任意数量的 users 类型的对象,下面就是一个资源清单示例,它定义了一个名为 admin 的 users 对象:
apiVersion: auth.zuoyang.tech/v1alpha1
kind: User
metadata:
name: admin
namespace: default
spec:
userID: 1
email: 1635230007@qq.com
groups:
- superusers
- adminstrators
password: zuoyang.tech
[root@k8s-master01-test-2-26 ~]# kubectl apply -f user-cr-demo.yaml
user.auth.zuoyang.tech/admin created
"kubectl get users" 可以查看自定义资源的状态信息:
[root@k8s-master01-test-2-26 ~]# kubectl get users -n default
NAME AGE
admin 2m39s
[root@k8s-master01-test-2-26 ~]# kubectl describe users
Name: admin
Namespace: default
Labels: <none>
Annotations: <none>
API Version: auth.zuoyang.tech/v1alpha1
Kind: User
Metadata:
Creation Timestamp: 2022-07-07T07:59:20Z
Generation: 1
Managed Fields:
API Version: auth.zuoyang.tech/v1alpha1
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
.:
f:email:
f:groups:
f:password:
f:userID:
Manager: kubectl-client-side-apply
Operation: Update
Time: 2022-07-07T07:59:20Z
Resource Version: 390747
UID: e18c3482-cf85-4898-9ad7-340d47d87572
Spec:
Email: 1635230007@qq.com
Groups:
superusers
adminstrators
Password: zuoyang.tech
User ID: 1
Events: <none>
[root@k8s-master01-test-2-26 ~]#
根据 API 对象 GVR 格式的 URL 规范,users 资源的对象 admin 的引用路径为 /apis/auth.zuoyang.tech/v1alpha1/namespaces/default/users/admin,这一点已通过 descreibe 命令予以证实。而要删除自定义的 users 对象 admin,只要使用通用格式的 kubeclt 命令即可,如 "kubectl delete users admin",或者使用陈述式对象配置命令 “kubectl delete -f <filename>”。
[root@k8s-master01-test-2-26 ~]# kubectl delete users admin
user.auth.zuoyang.tech "admin" deleted
[root@k8s-master01-test-2-26 ~]# kubectl get users -n default
No resources found in default namespace.
[root@k8s-master01-test-2-26 ~]#
二、定义资源格式验证
除了对操作请求进行身份验证和授权检查之外,对象配置的变动在存储 etcd 之前还需要经由准入控制器的核验,尤其是验证型(validation)控制器会检查传入的对象格式是否符合有效格式,包括是否设定了不符合定义的数据类型值,以及是否违反了字段的限制规则等。
Kubernetes 自 1.9 版本起支持为 CRD 定义验证(validation 字段)机制用以定义 CRD 可用的有效字段等,这在将设计的自定义资源公开时非常重要。自定义资源可使用 OpenAPI 模式声明验证规则,该模式是 JSON 模式的子集。需要注意的是,OpenAPI 架构并不能支持 JSON 架构的所有功能,而 CRD 验证也不能支持 OpenAPI 架构的所有功能,不过对大多数情况来说,它足够使用了。
下面是一个配置清单(就是上面那个yaml)。它分别定义了 userID、groups、email 和 password 字段的数据类型,并指定了 userID 字段的取值范围,以及 password 字段的数据格式,而且还通过 required 指定 userID 和 groups 是必选字段:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: users.auth.zuoyang.tech
spec:
group: auth.zuoyang.tech
names:
kind: User
plural: users
singular: user
shortNames:
- u
scope: Namespaced
versions:
- served: true
storage: true
name: v1alpha1
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
userID:
type: integer
minimum: 1
maximum: 65535
groups :
type: array
items:
type: string
email:
type: string
password:
type: string
format: password
required: ["userID","groups"]
限制了每个字段的数据类型之后,验证机制会检查创建或修改操作中提供的每个字段值等是否违反了格式要求,任何的核验失败都会导致写入操作被拒绝。
自 Kubernetes 1.11 版本开始,kubeclt 即使用服器侧对象信息打印机制,这意味着将由 API Server 决定 kubectl get 命令结果会显示哪些字段。在 CRD 资源中,可在 spec.additionalPrinterColumns 中嵌套定义在对象的详细信息中要打印的字段列表。
下面的配置清单片段定义了要为 users 类型的对象显示相关的四个字段,将其合并至前面定义的 CRD 对象 users 配置清单的 spec 字段中,重新应用到集群中并确保其正常生效:
[root@k8s-master01-test-2-26 ~]# kubectl explain customresourcedefinition.spec.versions.additionalPrinterColumns
KIND: CustomResourceDefinition
VERSION: apiextensions.k8s.io/v1
RESOURCE: additionalPrinterColumns <[]Object>
DESCRIPTION:
additionalPrinterColumns specifies additional columns returned in Table
output. See
https://kubernetes.io/docs/reference/using-api/api-concepts/#receiving-resources-as-tables
for details. If no columns are specified, a single column displaying the
age of the custom resource is used.
CustomResourceColumnDefinition specifies a column for server side printing.
FIELDS:
description <string>
description is a human readable description of this column.
format <string>
format is an optional OpenAPI type definition for this column. The 'name'
format is applied to the primary identifier column to assist in clients
identifying column is the resource name. See
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types
for details.
jsonPath <string> -required-
jsonPath is a simple JSON path (i.e. with array notation) which is
evaluated against each custom resource to produce the value for this
column.
name <string> -required-
name is a human readable name for the column.
priority <integer>
priority is an integer defining the relative importance of this column
compared to others. Lower numbers are considered higher priority. Columns
that may be omitted in limited space scenarios should be given a priority
greater than 0.
type <string> -required-
type is an OpenAPI type definition for this column. See
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types
for details.
[root@k8s-master01-test-2-26 ~]#
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: users.auth.zuoyang.tech
spec:
group: auth.zuoyang.tech
names:
kind: User
plural: users
singular: user
shortNames:
- u
scope: Namespaced
versions:
- served: true
storage: true
name: v1alpha1
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
userID:
type: integer
minimum: 1
maximum: 65535
groups :
type: array
items:
type: string
email:
type: string
password:
type: string
format: password
required: ["userID","groups"]
additionalPrinterColumns:
- name: userID
type: integer
description: The user ID.
jsonPath: .spec.userID
- name: groups
type: string
description: The goups of the user.
jsonPath: .spec.goups
- name: email
type: string
description: The email of the user.
jsonPath: .spec.email
- name: password
type: string
description: The password of the user account.
jsonPath: .spec.password
[root@k8s-master01-test-2-26 ~]# kubectl get crd users.auth.zuoyang.tech
NAME CREATED AT
users.auth.zuoyang.tech 2022-07-07T08:54:41Z
[root@k8s-master01-test-2-26 ~]# kubectl describe crd users.auth.zuoyang.tech
Name: users.auth.zuoyang.tech
Namespace:
Labels: <none>
Annotations: <none>
API Version: apiextensions.k8s.io/v1
Kind: CustomResourceDefinition
Metadata:
Creation Timestamp: 2022-07-07T08:54:41Z
Generation: 1
Managed Fields:
API Version: apiextensions.k8s.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
f:acceptedNames:
f:kind:
f:listKind:
f:plural:
f:shortNames:
f:singular:
f:conditions:
k:{"type":"Established"}:
.:
f:lastTransitionTime:
f:message:
f:reason:
f:status:
f:type:
k:{"type":"NamesAccepted"}:
.:
f:lastTransitionTime:
f:message:
f:reason:
f:status:
f:type:
Manager: kube-apiserver
Operation: Update
Subresource: status
Time: 2022-07-07T08:54:41Z
API Version: apiextensions.k8s.io/v1
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
f:conversion:
.:
f:strategy:
f:group:
f:names:
f:kind:
f:listKind:
f:plural:
f:shortNames:
f:singular:
f:scope:
f:versions:
Manager: kubectl-client-side-apply
Operation: Update
Time: 2022-07-07T08:54:41Z
Resource Version: 405679
UID: c98f0ba2-7fb4-4e0f-85f4-a55173b5aa64
Spec:
Conversion:
Strategy: None
Group: auth.zuoyang.tech
Names:
Kind: User
List Kind: UserList
Plural: users
Short Names:
u
Singular: user
Scope: Namespaced
Versions:
Additional Printer Columns:
Description: The user ID.
Json Path: .spec.userID
Name: userID
Type: integer
Description: The goups of the user.
Json Path: .spec.goups
Name: groups
Type: string
Description: The email of the user.
Json Path: .spec.email
Name: email
Type: string
Description: The password of the user account.
Json Path: .spec.password
Name: password
Type: string
Name: v1alpha1
Schema:
openAPIV3Schema:
Properties:
Spec:
Properties:
Email:
Type: string
Groups:
Items:
Type: string
Type: array
Password:
Format: password
Type: string
User ID:
Maximum: 65535
Minimum: 1
Type: integer
Required:
userID
groups
Type: object
Type: object
Served: true
Storage: true
Status:
Accepted Names:
Kind: User
List Kind: UserList
Plural: users
Short Names:
u
Singular: user
Conditions:
Last Transition Time: 2022-07-07T08:54:41Z
Message: no conflicts found
Reason: NoConflicts
Status: True
Type: NamesAccepted
Last Transition Time: 2022-07-07T08:54:41Z
Message: the initial names have been accepted
Reason: InitialNamesAccepted
Status: True
Type: Established
Stored Versions:
v1alpha1
Events: <none>
[root@k8s-master01-test-2-26 ~]#
三、子资源
前面自定义资源对象 admin 在其详细状态信息输出中没有类似核心资源的 status 字段,该字段是一种用于保存对象当前状态的子资源。
在 Kubernetes 系统的声明式 API 中,status 字段至关重要,它由 Kubernetes 系统自行维护,相关的控制器循环中持续与 API Server 进行通信,并负责确保 status 字段中的状态匹配 spec 字段中定义的期望状态。
在 CRD 中为自定义资源启用 status 字段的方式非常简单,只需要为其定义 spec.subresources.status 字段即可,其内嵌的字段等由系统自行维护,用户无须提供任何额外的配置。它的使用格式如下:
crd.spec.versions.subresources 字段定义如下:
[root@k8s-master01-test-2-26 ~]# kubectl explain crd.spec.versions.subresources
KIND: CustomResourceDefinition
VERSION: apiextensions.k8s.io/v1
RESOURCE: subresources <Object>
DESCRIPTION:
subresources specify what subresources this version of the defined custom
resource have.
CustomResourceSubresources defines the status and scale subresources for
CustomResources.
FIELDS:
scale <Object>
scale indicates the custom resource should serve a `/scale` subresource
that returns an `autoscaling/v1` Scale object.
status <map[string]>
status indicates the custom resource should serve a `/status` subresource.
When enabled: 1. requests to the custom resource primary endpoint ignore
changes to the `status` stanza of the object. 2. requests to the custom
resource `/status` subresource ignore changes to anything other than the
`status` stanza of the object.
[root@k8s-master01-test-2-26 ~]#
crd.spec.versions.subresources.scale 字段定义如下:
[root@k8s-master01-test-2-26 ~]# kubectl explain crd.spec.versions.subresources.scale
KIND: CustomResourceDefinition
VERSION: apiextensions.k8s.io/v1
RESOURCE: scale <Object>
DESCRIPTION:
scale indicates the custom resource should serve a `/scale` subresource
that returns an `autoscaling/v1` Scale object.
CustomResourceSubresourceScale defines how to serve the scale subresource
for CustomResources.
FIELDS:
labelSelectorPath <string>
labelSelectorPath defines the JSON path inside of a custom resource that
corresponds to Scale `status.selector`. Only JSON paths without the array
notation are allowed. Must be a JSON Path under `.status` or `.spec`. Must
be set to work with HorizontalPodAutoscaler. The field pointed by this JSON
path must be a string field (not a complex selector struct) which contains
a serialized label selector in string form. More info:
https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions#scale-subresource
If there is no value under the given path in the custom resource, the
`status.selector` value in the `/scale` subresource will default to the
empty string.
specReplicasPath <string> -required-
specReplicasPath defines the JSON path inside of a custom resource that
corresponds to Scale `spec.replicas`. Only JSON paths without the array
notation are allowed. Must be a JSON Path under `.spec`. If there is no
value under the given path in the custom resource, the `/scale` subresource
will return an error on GET.
statusReplicasPath <string> -required-
statusReplicasPath defines the JSON path inside of a custom resource that
corresponds to Scale `status.replicas`. Only JSON paths without the array
notation are allowed. Must be a JSON Path under `.status`. If there is no
value under the given path in the custom resource, the `status.replicas`
value in the `/scale` subresource will default to 0.
[root@k8s-master01-test-2-26 ~]#
crd.spec.versions.subresources.status字段定义如下:
KIND: CustomResourceDefinition
VERSION: apiextensions.k8s.io/v1
DESCRIPTION:
status indicates the custom resource should serve a `/status` subresource.
When enabled: 1. requests to the custom resource primary endpoint ignore
changes to the `status` stanza of the object. 2. requests to the custom
resource `/status` subresource ignore changes to anything other than the
`status` stanza of the object.
CustomResourceSubresourceStatus defines how to serve the status subresource
for CustomResources. Status is represented by the `.status` JSON path
inside of a CustomResource. When set, * exposes a /status subresource for
the custom resource * PUT requests to the /status subresource take a custom
resource object, and ignore changes to anything except the status stanza *
PUT/POST/PATCH requests to the custom resource ignore changes to the status
stanza
CustomResourceSubresourceStatus defines how to serve the status subresource
for CustomResources. Status is represented by the `.status` JSON path
inside of a CustomResource. When set, * exposes a /status subresource for
the custom resource * PUT requests to the /status subresource take a custom
resource object, and ignore changes to anything except the status stanza *
PUT/POST/PATCH requests to the custom resource ignore changes to the status
stanza
[root@k8s-master01-test-2-26 ~]#
将前面配置清单中 CRD 对象 users 的配置中,再改造下,完成活动对象的修改即可在其实例化出的对象上通过 /status 获取状态信息,例如,对象 admin 的状态引用路径为 /apis/auth.zuoyang.tech/v1alpha1/namespaces/default/users/admin/status。在没有相应资源控制器的情形下,活动对象状态的非计划内变动既不会实时反映到 status 中,也不会问 spec 定义的期望状态转换。
事实上,如果有相应的资源控制器维护自定义的资源类型时,还可以配置自定义资源对象时使用 scale 子资源和 status 子资源协同实现类似 Deployment 或 StatefulSet 等控制器一样对象规模的伸缩功能。例子如下:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: users.auth.zuoyang.tech
spec:
group: auth.zuoyang.tech
names:
kind: User
plural: users
singular: user
shortNames:
- u
scope: Namespaced
versions:
- served: true
storage: true
name: v1alpha1
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
userID:
type: integer
minimum: 1
maximum: 65535
groups :
type: array
items:
type: string
email:
type: string
password:
type: string
format: password
required: ["userID","groups"]
additionalPrinterColumns:
- name: userID
type: integer
description: The user ID.
jsonPath: .spec.userID
- name: groups
type: string
description: The goups of the user.
jsonPath: .spec.goups
- name: email
type: string
description: The email of the user.
jsonPath: .spec.email
- name: password
type: string
description: The password of the user account.
jsonPath: .spec.password
subresources:
status: {}
scale:
labelSelectorPath: .status.labelSelector
specReplicasPath: .spec.replicas
statusReplicasPath: .status.replicas
crd.spec.versions.subresources.scale 的各内嵌字段介绍如下:
- specReplicasPath <string> -required-: 引用定义在 spec 中用于表示期望的副本数量的字段 jsonPath 格式,如 .spec.relicas。
- statusReplicasPath <string> -required-: 引用保存在 status 中用于表示对象的当前副本数量的字段,jsonPath 格式,如 .status.replicas。
- labelSelectorPath <string>: 可选字段,但若要与 HPA 结合使用则是必选字段,用于引用 status 中用于表示使用的标签选择器字段,jsonPath 格式,如 .status.labelSelector。
为某 CRD 资源定义 scale 子资源之后,即可实例化出支持规模伸缩的自定义资源对象。但必须存在一个相应的资源控制器来持续维护相应的自定义资源对象,以确保其当前状态匹配期望的状态。满足条件后,类似于 Deployment 资源对象等,使用 "kubectl scale" 命令就能完成对自定义资源对象规模的手动伸缩。
四、使用资源类别
类别(categories)[英[ˈkætɪgəriz]美[ˈkætəˌgɔriz]] 是 Kubernetes 1.10 版本引入的一种分组组织自定义资源的方法,定义 CRD 对象时为其指定一个或多个类别,可以通过 kubectl get <category-name> 命令列出该类别中的所有自定义资源对象,all 就是一个常用的内建资源类别。
例如,为前面定义的 CRD 对象 users 的 spec.names 字段中额外内嵌如下配置,便能使得 users 资源类型下的所有对象都隶属于 all 类别:
[root@k8s-master01-test-2-26 ~]# kubectl explain crd.spec.names
KIND: CustomResourceDefinition
VERSION: apiextensions.k8s.io/v1
RESOURCE: names <Object>
DESCRIPTION:
names specify the resource and kind names for the custom resource.
CustomResourceDefinitionNames indicates the names to serve this
CustomResourceDefinition
FIELDS:
categories <[]string>
categories is a list of grouped resources this custom resource belongs to
(e.g. 'all'). This is published in API discovery documents, and used by
clients to support invocations like `kubectl get all`.
kind <string> -required-
kind is the serialized kind of the resource. It is normally CamelCase and
singular. Custom resource instances will use this value as the `kind`
attribute in API calls.
listKind <string>
listKind is the serialized kind of the list for this resource. Defaults to
"`kind`List".
plural <string> -required-
plural is the plural name of the resource to serve. The custom resources
are served under `/apis/<group>/<version>/.../<plural>`. Must match the
name of the CustomResourceDefinition (in the form
`<names.plural>.<group>`). Must be all lowercase.
shortNames <[]string>
shortNames are short names for the resource, exposed in API discovery
documents, and used by clients to support invocations like `kubectl get
<shortname>`. It must be all lowercase.
singular <string>
singular is the singular name of the resource. It must be all lowercase.
Defaults to lowercased `kind`.
[root@k8s-master01-test-2-26 ~]#
explain crd.spec.names.categories 内嵌字段定义如下:
[root@k8s-master01-test-2-26 ~]# kubectl explain crd.spec.names.categories
KIND: CustomResourceDefinition
VERSION: apiextensions.k8s.io/v1
FIELD: categories <[]string>
DESCRIPTION:
categories is a list of grouped resources this custom resource belongs to
(e.g. 'all'). This is published in API discovery documents, and used by
clients to support invocations like `kubectl get all`.
[root@k8s-master01-test-2-26 ~]#
五、多版本支持
Kubernetes 原生资源的一个引人瞩目的特性是它们能够在 API 版本之间自动和透明地迁移。资源的使用者可以使用混合 API 版本,并且都能够获得他们所期望的资源版本。而 Kubernetes 1.11 版本开始,CRD 支持多个版本,但它们之间的转换必须手动完成。
在 CRD 上使用多版本机制时,将 spec.version 字段替换为 spec.versions 字段,并将支持的各版本以对象列表的形式给出定义即可。不过,多版本并存时,仅其中一个版本且必须有一个版本应标记为存储(customresourcedefinition.spec.versions.storage 字段)版本。
下面的配置清单片断中定义了两个 API 版本,其中仅 v1alpha1 标记为了 storage:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: users.auth.zuoyang.tech
spec:
group: auth.zuoyang.tech
names:
kind: User
plural: users
singular: user
shortNames:
- u
scope: Namespaced
versions:
- served: true
storage: true
name: v1alpha1
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
userID:
type: integer
minimum: 1
maximum: 65535
groups :
type: array
items:
type: string
email:
type: string
password:
type: string
format: password
required: ["userID","groups"]
- served: true
storage: false
name: v1alpha2
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
userID:
type: integer
minimum: 1
maximum: 65535
groups :
type: array
items:
type: string
email:
type: string
password:
type: string
format: password
required: ["userID","groups"]
additionalPrinterColumns:
- name: userID
type: integer
description: The user ID.
jsonPath: .spec.userID
- name: groups
type: string
description: The goups of the user.
jsonPath: .spec.goups
- name: email
type: string
description: The email of the user.
jsonPath: .spec.email
- name: password
type: string
description: The password of the user account.
jsonPath: .spec.password
subresources:
status: {}
scale:
labelSelectorPath: .status.labelSelector
specReplicasPath: .spec.replicas
statusReplicasPath: .status.replicas
六、自定义控制器基础
仅借助于 CRD 完成资源自定义本身并不能为用户带来太多的价值,它只是资源类型的定义,只是提供了 JSON 格式的数据范式及存储相关数据的能力,至于如何执行数据相关的业务逻辑,时刻确保将 status 中的状态移向 spec 中的状态则是由封装于控制器中的代码来负责实现的。
相应的,为自定义资源类型提供业务逻辑代码的控制器需要由用户自行开发,并运行于 API Server 的客户端程序(通常是托管运行于 kubernetes 系统之上,类似于 Ingress 控制器),这就是所谓的自定义控制器(Custom Controller)。
换句话讲,某特定的 CRD 资源的相关对象发生变动时,如何确保它的当前状态不断地接近期望的状态并非 API Server 的功能,而是相关的专用控制器组件。
事实上,自定义控制器不仅仅能够用于管理 CRD 资源,用户也完全可以仅针对系统内建的核心类型对象开啊更高级的控制器,这些对象类型包括 Service、Deployment、ConfigMap。不过,对于每个 CRD 的自定义类别来说,至少应该存在一个相关的自定义控制器。
简单的来说,控制器负责持续监视资源变动,根据资源的 spec 及 status 中的信息执行某些操作逻辑,并将执行结果更新至资源的 status 中。自定义控制器同样担负着类似的职责,只不过一个特定的控制器通常仅负责一部分特定的资源类型,并执行专有管理逻辑。
Kubernetes 系统内建了许多控制器,如 NodeController、ServiceController 等,它们打包在一起并作为一个统一守护进程 kube-controller-manager。这些控制器基本上都遵循同一钟模式以完成资源管理操作,该模式通常可描述为三种特性:
-
- 声明式 API:用户自定义期望的状态而非要执行的特定操作,如使用 kubectl apply 命令将请求发送至 API Server 存储于 etcd 中,并由 API Server 做出处理响应。
- 异步:客户端的请求于 API Server 存储完成之后即返回成功信息,而无须等待控制器执行的和解循环结束。
- 水平处理(level-based):同一对象有多次变动事件待处理时,控制器仅需要处理其最新一次的变动。这种根据最新观察结果而非历史变化来和解 status 和 spec 的机制即为水平处理机制。
定义控制器实现自定义资源管理行为的最佳方法同样是遵循此类控制器模式进行程序开发,目前,大部分开发接口都是基于 Golang 语言来实现的,相关代码位于客户端库的 client-go/go/tools/cache 和 client-go/util/workqueue 目录中。
简单的来说,控制器包含两个重要组件,Informer/SharedInformer 和 Workqueue,前者负责监视资源对象当前状态的更改,并将事件发送至后者,而后由处理函数进行处理,如下图:
Informer 主要由 Listwatcher、ResourceEvenetHandler 和 ResyncPeriod 三类函数组件进行构造。
-
- Listwather: 是应用于特定名称空间中特定资源对象的列表函数(listFunc)和监视函数(watchFunc),并结合字段选择器为控制器精确限定关注的资源对象。
- ResourceEvenetHandler: 负责处理资源对象状态改变产生的相关通知,其分别使用 AddFunc、UpdateFunc、DeleteFunc 三个函数完成对象的创建、更新和删除操作。
- ResyncPeriod 定义控制器遍历缓存中的所有项目并触发运行 UpdateFunc 的频率,即和解循环的执行频度。