CRD 使用详解

2022-12-12 container k8s

在 K8S 1.7 开始支持 Custom Resource Define, CRD 自定义资源,这样可以动态注册到集群中,就与其它资源类似可以通过 kubectl 来管理该资源了。注意,此时的 CRD 仅仅是资源的定义而已,需要一个 Controller 去监听 CRD 的各种事件来添加自定义的业务逻辑。

示例

这里以 Cron 表达式为例,简单介绍使用方法,首先是一个仅操作数据的示例 cron.yaml 配置文件。

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crons.example.com
spec:
  group: example.com
  versions:          # 所有支持的版本
  - name: v1beta1    # 版本名称,比如 v1、v2beta1 等等
    served: true     # 是否开启通过 REST APIs 访问
    storage: true    # 必须将一个且只有一个版本标记为存储版本 ??????
    schema:          # 自定义对象的声明规范
      openAPIV3Schema:
        description: Define CronTab YAML Spec
        type: object
        properties:
          spec:
            type: object
            properties: # 类型校验
              cronSpec:
                type: string
              image:
                type: string
              replicas:
                type: integer
  scope: Namespaced     # 定义作用范围,可以是命名空间或者整个集群(cluster)
  names:
    kind: Cron          # 驼峰形式定义,会在资源清单中的Kind参数使用
    plural: crons       # 复数形式用于RestAPI路径
    singular: cron      # 单数,用于Client操作或显示的别名
    shortNames:         # 缩写形式
    - ct

其中的 metadata.name 必须要以 复数.组 的方式命名,以上述的示例为例,那么 RestAPI 就是 /apis/example.com/v1/crons

另外,在 schema 中参数会通过 OpenAPI v3 Schema 方式进行简单的参数校验,如果需要更复杂的校验则可以参考 CRD Validation 中的介绍。

创建资源

通过如下方式创建 crons.example.com 资源。

----- 创建资源并查看
# kubectl apply -f cron.yaml
customresourcedefinition.apiextensions.k8s.io/crons.example.com created
# kubectl get crd
NAME                CREATED AT
crons.example.com   2022-08-18T15:40:20Z

然后就可以据此定义 Cron 资源对象,如下是 cron-demo.yaml 文件。

apiVersion: "example.com/v1beta1"
kind: Cron
metadata:
  name: cron-demo
spec:
  cronSpec: "* * * * */5"
  image: your-image-here

接着可以创建、查看该对象。

----- 创建上述的对象
# kubectl apply -f cron-demo.yaml
kubectl apply -f cron-demo.yaml
----- 查看时可以简写为ct或者是复数crons
# kubectl get cron
NAME        AGE
cron-demo   41s
----- 或者查看原始的yaml文件,包含了关键的cronSpec和image参数
# kubectl get cron -o yaml
apiVersion: v1
items:
- apiVersion: example.com/v1beta1
  kind: Cron
  metadata:
... ...
  spec:
    cronSpec: '* * * * */5'
    image: your-image-here
kind: List
metadata:
  resourceVersion: ""

Controller

上述只是将自定义的资源创建了,也就是将资源清单数据保存到了 etcd 中,除此之外并没有其它的用处,接着需要定义一个 Controller 来处理,可以查看 Sample Controller 示例代码。

其实现最终会基于 client-go 这个库,基本原理架构可以参考 Under the Hood 中的介绍。

示例

这是官方提供的 Sample Controller 实例,可以参考 GitHub,可以通过如下方式简单试用。

----- 下载相关依赖包并编译二进制文件
# go mod vendor
# go build -o sample-controller .
----- 更新生成代码,会更新deepcopy和pkg/generated
# bash hack/update-codegen.sh
----- 创建自定义的CRD资源
# kubectl create -f artifacts/examples/crd-status-subresource.yaml
customresourcedefinition.apiextensions.k8s.io/foos.samplecontroller.k8s.io created
----- 在本地运行
# ./sample-controller -kubeconfig=$HOME/.kube/config
----- 创建自定义的资源对象并检查,也可以删除
# kubectl create -f artifacts/examples/example-foo.yaml
foo.samplecontroller.k8s.io/example-foo created
# kubectl get deployments
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
example-foo   0/1     1            0           45s
# kubectl delete deployment example-foo
deployment.apps "example-foo" deleted

这实际上是从 K8S 源码中提取出来的,所以有些生成代码的更新脚本需要进行修改才可以。

代码生成

通过 code-generator 提供了以下工具生成代码:

  • deepcopy-gen 生成深度拷贝方法,为每个 T 类型创建一个 func (t *T) DeepCopy() 方法。
  • client-gen 为自定义资源生成标准的操作方法,例如 get list create 等,也就是 Kerbernetes 的客户端。
  • informer-gen 为自定义资源生成 informer 模块,这是一种基于事件的接口,用来及时反馈数据库中自定义资源的变化。
  • lister-gen 为自定义资源的 get 和 list 方法提供只读缓存层。

通过这四种生成器可以打造和 Kerbernetes 上游一样的控制器能力,除此之外,还有其它的代码生成工具。

可以直接下载代码后在源码所在目录下执行如下命令 go install k8s.io/code-generator/cmd/deepcopy-gen 进行安装。另外,还提供了 generate-groups.shgenerate-internal-groups.sh 两个脚本方便调用。

----- 安装代码,由于cmd的命令不在根目录下,实际上只会下载代码
# go install k8s.io/code-generator@latest
----- 切换到源码目录下安装
# cd $GOPATH/pkg/mod/k8s.io/code-generator

code-generator 源码中已经提供了一些常用的脚本文件:

  • generate-groups.sh 通过命令行直接调用,可以用来调用不同的脚本,不过已经不建议使用了。
  • kube_codegen.sh 真正功能的实现函数,通常用于自定义脚本的编写,一般会通过 hack/update-codegen.sh 脚本更新以及 hack/verify-codegen.sh 检查是否最新。

详细的使用可以直接参考源码中的 help 信息,这里以上述的 Sample Controller 为例生成相应代码,可以通过 kube_codegen.sh 配置自己的脚本,不过对目录是有一定要求的。

注意,在使用时有个隐形的约束,假设包为 ROOT_PKG=k8s.io/sample-controller,那么对应的 Controller 源码应该保存在 $GOPATH/src/$ROOT_PKG 目录下。实际上,这也是没有 module 之前的约定,不过自从有了 module 之后对目录不再有要求了。

对于 sample-controller 来说已经提供了脚本,可以通过如下步骤测试。

$ mkdir -p $GOPATH/src/k8s.io/sample-controller && cd $GOPATH/src/k8s.io/sample-controller
$ git clone https://github.com/kubernetes/sample-controller.git .
$ bash hack/update-codegen.sh