K8S 之 CRD 介绍

255 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天

什么是CRD

CRD的全称为 CustomResourceDefinitions,即自定义资源。k8s拥有一些内置的资源,比如说Pod,Deployment,ReplicaSet等等,而CRD则提供了一种方式,使用户可以自定义新的资源,以扩展k8s的功能。

使用CRD可以在不修改k8s源代码的基础上方便的扩展k8s的功能,比如腾讯云TKE使用CRD:logcollectors.ccs.cloud.tencent.com以添加日志收集服务,而Istio也大量使用到了CRD。

值得一提的是,另一种扩展k8s的方式是apiservice,通过API:metrics.k8s.io自定义HPA是其最典型的应用。

可以使用kubectl api-resources命令查看集群中已定义的资源:

[root@node k8s]# kubectl api-resources 
NAME                              SHORTNAMES   APIGROUP                       NAMESPACED   KIND
configmaps                        cm                                          true         ConfigMap
endpoints                         ep                                          true         Endpoints
events                            ev                                          true         Event
namespaces                        ns                                          false        Namespace
persistentvolumes                 pv                                          false        PersistentVolume
pods                              po                                          true         Pod
podtemplates                                                                  true         PodTemplate
storageclasses                    sc           storage.k8s.io                 false        StorageClass
...

从如上输出中可以略窥一二,CRD至少包括如下属性:

  • NAME:CRD的复数名称

  • SHORTNAMES:cli中使用的资源简称

  • APIGROUP:API所使用的组名称

  • NAMESPACED:是否具有namespace属性

  • KIND:资源文件需要,用以识别资源

另外,CRD提供了定义资源的方式,不过想要让其具有实际意义还需控制器的配合。k8s的kube-controller-manager组件提供了多种内置控制器,比如说:cronjobdaemonsetdeploymentnamespace等等,它们监听资源的创建/更新/删除,且做出相应的动作。而对于CRD来说,也可以编写相应的控制器来完成对应的功能。

CRD使用

在k8s中CRD本身也是资源,大于1.7.0版本的集群可以使用apiextensions.k8s.io/v1beta1API访问CRD,大于1.16.0版本则可以使用apiextensions.k8s.io/v1API

创建CRD

CRD资源文件示例:

# crd-test.yml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # 名称必须符合如下格式:<plural>.<group>
  name: crontabs.staight.k8s.io
spec:
  # 组名,表示使用该API: /apis/<group>/<version>
  group: staight.k8s.io
  # version列表,表示该CRD支持的版本
  versions:
    - name: v1
      # 开启/关闭该API
      served: true
      # 有且只能有一个版本要将storage设置为true
      storage: true
  # Namespaced/Cluster,表示该CRD是命令空间属性还是集群属性
  scope: Namespaced
  names:
    # API中使用的名称:/apis/<group>/<version>/<plural>
    plural: crontabs
    # 单数名称,cli中使用
    singular: crontab
    # 往往是首字母大写的单数名称,资源文件中需要用到
    kind: CronTab
    # cli中的简称
    shortNames:
    - ct
  # 阻止无法识别的字段,集群版本1.15以上才可使用
  preserveUnknownFields: false
  # 创建资源文件时需验证的字段
  validation:
    openAPIV3Schema:
      type: object
      properties:
        spec:
          type: object
          properties:
            cronSpec:
              type: string
            image:
              type: string
            replicas:
              type: integer

然后创建该CRD:

[root@node k8s]# kubectl create -f crd-test.yml 
customresourcedefinition.apiextensions.k8s.io/crontabs.staight.k8s.io created

接着就能查到该CRD:


[root@node k8s]# kubectl get crd crontabs.staight.k8s.io
NAME                      CREATED AT
crontabs.staight.k8s.io   2019-10-08T10:21:09Z

CRD创建完成。可以通过URL:https://ip:port/apis/staight.k8s.io/v1/namespaces/default/crontabs 访问到crontab资源。

创建自定义对象

在创建CRD之后,即可创建其资源的对象了。资源文件示例:

# crontab.yml
apiVersion: "staight.k8s.io/v1"
kind: CronTab
metadata:
  name: new-crontab
spec:
  cronSpec: "* * * * *"
  image: new-image

注意spec中的字段应符合CRD的要求,创建它:

[root@node k8s]# kubectl create -f crontab.yml 
crontab.staight.k8s.io/new-crontab created

接着即可看到该对象:


[root@node k8s]# kubectl get crontab
NAME          AGE
new-crontab   28s

小结一下:

  • CRD用来自定义资源,是扩展k8s最常用的方式。

  • 只创建CRD并没有实际意义,想要CRD工作还需创建控制器,监听资源变动并做出相应动作。

Operator

只创建CRD并没有实际意义,想要CRD工作还需创建控制器,监听资源变动并做出相应动作。 这里就需要介绍下 Operator。 这个名字最早由 CoreOS(coreos.com/operators/) 在 2016 年提出来,我们来看看他们给出的定义:

An operator is a method of packaging, deploying and managing a Kubernetes application. 
A Kubernetes application is an application that is both deployed on Kubernetes and managed using the Kubernetes APIs and kubectl tooling.
To be able to make the most of Kubernetes, you need a set of cohensive APIs to extend in order to service and manage your applications that run on Kubernetes. 
You can think of Operators as the runtime that manages this type of application on Kubernetes.

简单概括一下,所谓的 Kubernetes Operator 其实就是借助 Kubernetes 的控制器模式,配合一些自定义的 API,完成对某一类应用的操作,比如资源创建、变更、删除等操作。这里对 Kubernetes 的控制器模式做个简要说明。Kubernetes 通过声明式 API 来定义对象,各个控制器负责实时查看对应对象的状态,确保达到定义的期望状态。这就是 Kubernetes 的控制器模式。

image.png kube-controller-manager 就是由这样一组控制器组成的。我们以 StatefulSet 为例来简单说明下控制器的具体逻辑。假设我们声明了一个 StatefulSet,并将其副本数设置为 3。kube-controller-manager 中以 goroutine 方式运行的 StatefulSet 控制器在观察 kube-apiserver 的时候,发现了这个新创建的对象,它会先创建一个 index 为 0 的 Pod ,并实时观察这个 Pod 的状态,待其状态变为 Running 后,再创建 index 为 1 的 Pod。后续该控制器会一直观察并维护这些 Pod 的状态,保证 StatefulSet 的有效副本数始终为 3。所以我们在声明完成 CRD 之后,也需要创建一个控制器,即 Operator,来完成对应的控制逻辑。在了解了 Operator 的概念和控制器模式后,我们来看看 Operator 是如何工作的。

Operator 工作的时候采用上述的控制器模式,会持续地观察 Kubernetes 中的自定义对象,即 CRCustom Resource)。我们通过 CRD 来定义一个对象,CR 则是 CRD 实例化的对象。

image.png

Operator 会持续跟踪这些 CR 的变化事件,比如 ADDUPDATEDELETE,然后采取一系列操作,使其达到期望的状态。上述的流程其实还是有些复杂的,尤其是对运维同学有一定的门槛。好在社区提供了一些脚手架,可以方便我们快速地构建自己的 Operator