↓
  1. Posts/

Kubernetes CRD 101: 什么是 CRD、CR,这些到底在说啥

·2886 字·6 分钟·
云原生 Kubernetes Kubernetes CRD 101
Cloud Native 101
作者
Cloud Native 101
与「Cloud Native 101」同行,让云原生技术触手可及!
Table of Contents

CRD

Kubernetes 中的 API 对象都叫做资源(Resource),就是 Yaml 里 Kind 字段所描述的东西。

我相信大家对 Kubernetes 中的 API 对象都有所了解,每个 API 对象都有它自己的能力,分的非常细,概念也很多。当平台出现了问题,对一些新手来说,他们往往会不知所措,不知道从什么地方开始排查,非常的迷茫。

比如最近发生在我司的一件趣事。

我司内网 开发环境 的服务,对集群外提供访问是通过 Ingress 这个资源,测试环境 为了和 生产环境 保持一致,上了 Service Mesh(Istio),用的是 VirtualService 对外提供服务。

QA同学日常在使用 Kubernetes 的过程中,经常接触到的都是 Deployment、Service、Ingress 这些原生内置的资源对象,那天 测试环境 因为某些原因出了问题,导致服务不能够正常访问,她以为是服务的 Ingress 资源没安装引起,闹出了不少笑话。

那么问题来了,VirtualService 到底是个什么资源呢?它又是怎么集成到 Kubernetes 集群內的呢?

这正是本篇文章要分享的主题。

一个例子
#

在回答上面这个问题之前,我们通过下面这个命令再来看一个 Kubernetes 集群內的资源。

乍一看,你是不是完全不知道 dagnoderunner-sample 有什么作用?

➜ kubectl get dnr -owide

NAME                   AGE
dagnoderunner-sample   86s

再来看下这个资源的 Spec 情况,我们发现 Kind 里描述的是一个叫 DagNodeRunner 的资源对象。

它的 Spec 也很简单,只有 foo 和 baz 两个字段,其他啥都没有。

apiVersion 字段的内容也不是我们平常所熟悉的,apps/v1、apiextensions.k8s.io/v1 和 v1,而是 cloudnative101.net/v1beta1。

➜ kubectl get DagNodeRunner dagnoderunner-sample -oyaml|kubectl neat

apiVersion: cloudnative101.net/v1beta1
kind: DagNodeRunner
metadata:
  name: dagnoderunner-sample
  namespace: default
spec:
  foo: bar
  baz: true

那么 DagNodeRunner 到底是个什么东西呢?它其实就是本篇文章分享的其中一个主题:CR。

什么是 CR
#

CR 的全称是 Custom Resource。

顾名思义,它是 Kubernetes 世界里的一种自定义资源,包括前面提到的 VirtualService 也是一种自定义资源,也就是说除了常见的 Deployment 之类的内置资源以外,Kubernetes 可以允许用户自定义资源。

那么前面举例的 DagNodeRunner 这个 CR 它有什么作用呢?说出来你可能不信,其实我也不知道有啥用。但是,这并不是重点,大家有没有好奇这个自定义资源是怎么生成的?

我们先以瓢画葫,来写个自定义资源的 Spec 吧,如下所示

cat <<EOF | kubectl create -f -
apiVersion: batch.cloudnative101.net/v1
kind: CronJob
metadata:
  name: cronjob-sample
  namespace: default
spec:
  schedule: "*/1 * * * *"
  startingDeadlineSeconds: 60
EOF

我们将以上脚本放在 Kubernetes 集群內执行,很遗憾,执行失败,并没有成功生成上面自定义的资源 CronJob。

error: unable to recognize "STDIN": no matches for kind "CronJob" in version "batch.cloudnative101.net/v1"

系统给出的提示已经非常明显了,上面的资源,集群并不能识别出来,所以导致创建自定义资源失败,很合理,不然啥都能创,岂不是要乱套了,可以想象的到 Kubernetes 的扩展能力会变的非常难管理。

那么 dagnoderunner-sample 这个自定义资源实例,到底是怎么建出来的呢,其实这个和 gRPC 对外提供服务前要先定义 proto 协议一个道理。

在 Kubernetes 世界里,用户的自定义资源想要在集群內运行,那么集群內,必须先要有这个自定义资源的定义,这里要引入本篇文章分享的另外一个主题,也就是说,要先有 CRD。

什么是 CRD
#

CRD 的全称是 Custom Resource Definition。

顾名思义,它指的就是自定义资源的定义,简单来说,它是描述我们定义的资源长什么样子。

CustomResourceDefinition 它是 Kubernetes 内置的原生的一个资源类型,它允许用户在 Kubernetes 中添加一个跟 Deployment 或者 Ingress 类似的、新的 API 资源类型,即:自定义资源。

我们可以通过 kubectl get crd 命令查看集群内定义的 CRD 资源。

➜ kubectl get crd

NAME                                 CREATED AT
dagnoderunners.cloudnative101.net   2021-11-06T04:09:55Z

我们通过命令再来看看这个 CRD 长什么样子

➜ kubectl get crd dagnoderunners.cloudnative101.net -oyaml|kubectl neat

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: dagnoderunners.cloudnative101.net
spec:
  conversion:
    strategy: None
  group: cloudnative101.net
  names:
    categories:
    - cloudnative-labs
    kind: DagNodeRunner
    listKind: DagNodeRunnerList
    plural: dagnoderunners
    shortNames:
    - dnr
    singular: dagnoderunner
  scope: Namespaced
  versions:
  - name: v1beta1
    schema:
      openAPIV3Schema:
        description: DagNodeRunner is the Schema for the dagnoderunners API
        properties:
          apiVersion:
            description: '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'
            type: string
          kind:
            description: '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'
            type: string
          metadata:
            type: object
          spec:
            description: DagNodeRunnerSpec defines the desired state of DagNodeRunner
            properties:
              baz:
                type: boolean
              foo:
                minimum: 1
                type: string
            required:
            - foo
            type: object
        type: object
    served: true
    storage: true

分析完以上 Spec,我们现在已经非常清楚上面例子提到的 dagnoderunner-sample 它为什么能够跑在集群內,而 CronJob 这个资源不能顺利生成的原因了。

只有我们在 Kubernetes 集群內执行了 CRD (可以理解为向集群注册了一种新的资源) 后,它才会允许用户将自定义的 API 资源类型添加到集群中,这样就可以像使用其他 Kubernetes 内置资源类型一样,使用 kubectl 来创建和访问它了。

CRD 关键字段说明
#

fielddesc
metadata.nameCRD 的名字,名称与 spec 中的字段匹配:<spec.names.plural>.<spec.group>
spec.scopeCRD 可以是命名空间范围的,也可以是集群范围的,值为 Namespaced 或 Cluster
spec.groupREST API 使用的组名称:/apis/<group>/<version>
spec.names.kindCamelCased 格式的单数类型
spec.names.pluralREST API 使用的复数名称:/apis/<group>/<version>/<plural>
spec.names.singularkubectl 中使用的资源单数名称
spec.names.shortNames[*]kubectl 中使用的资源简称列表
spec.names.categories[*]自定义资源所属的分组资源的列表
spec.versions[*].nameREST API 使用的版本号:/apis/<group>/<version>
spec.versions[*].served每个版本都可以通过 served 标志来独立启用或禁止
spec.versions[*].storage其中一个且只有一个版本必需被标记为存储版本
spec.versions[*].schema.openAPIV3Schema用于验证自定义对象的 schema

API Group

它是相关 API 功能的集合,每个 Group 拥有一或多个 Versions,用于接口的演进。

Kinds

每个 GV 都包含多个 API 对象,称为 Kinds,在不同的 Versions 之间同一个 Kind 定义可能不同。

在 Kubernetes 世界里,同一种 API 对象可以有多个版本,这正是 Kubernetes 进行 API 版本化管理的重要手段。

自定义资源的验证
#

我们再来做另外一个测试,将下面的脚本放在 Kubernetes 集群內执行。

cat <<EOF | kubectl create -f -
apiVersion: cloudnative101.net/v1beta1
kind: DagNodeRunner
metadata:
  name: dagnoderunner-sample
spec:
  foo: bar
  baz: test
EOF

发现并不能执行成功,因为 baz 在 Spec 里定义的是 boolean 类型,这里我们用了 string,当然会失败。

error: error validating "STDIN": error validating data: ValidationError(DagNodeRunner.spec.baz): invalid type for io.cloudnative-labs.v1beta1.DagNodeRunner.spec.baz: got "string", expected "boolean"; if you choose to ignore these errors, turn validation off with --validate=false

CRD 它可以通过在 spec.versions[*].schema.openAPIV3Schema 定义规则,来验证用户提交的自定义实例是否符合资源的描述,只有符合标准了,CR 实例才可以在集群內运行。

自定义资源的 RESTful API
#

我们可以通过启动 proxy server,来看下新的 API 对象的定义

# starts a proxy to the Kubernetes API server
kubectl proxy --port=8080

# 1. 查看自定义资源的定义
#

➜ curl http://localhost:8080/apis/cloudnative101.net/v1beta1
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "cloudnative101.net/v1beta1",
  "resources": [
    {
      "name": "dagnoderunners",
      "singularName": "dagnoderunner",
      "namespaced": true,
      "kind": "DagNodeRunner",
      "verbs": [
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "create",
        "update",
        "watch"
      ],
      "shortNames": [
        "dnr"
      ],
      "storageVersionHash": "dlMUrzpeS6U="
    }
  ]
}

# 2. 查看自定义资源 DagNodeRunner 实例列表
#

➜ curl http://localhost:8080/apis/cloudnative101.net/v1beta1/dagnoderunners|jq

{
  "apiVersion": "cloudnative101.net/v1beta1",
  "items": [
    {
      "apiVersion": "cloudnative101.net/v1beta1",
      "kind": "DagNodeRunner",
      "metadata": {
        "creationTimestamp": "2021-11-06T13:30:50Z",
        "generation": 1,
        "managedFields": [
          {
            "apiVersion": "cloudnative101.net/v1beta1",
            "fieldsType": "FieldsV1",
            "fieldsV1": {
              "f:spec": {
                ".": {},
                "f:baz": {},
                "f:foo": {}
              }
            },
            "manager": "kubectl",
            "operation": "Update",
            "time": "2021-11-06T13:30:50Z"
          }
        ],
        "name": "dagnoderunner-sample",
        "namespace": "default",
        "resourceVersion": "259910",
        "uid": "eaafdd0f-d6e6-489c-a49f-e6ffaa6264c9"
      },
      "spec": {
        "baz": true,
        "foo": "bar"
      }
    }
  ],
  "kind": "DagNodeRunnerList",
  "metadata": {
    "continue": "",
    "resourceVersion": "266515"
  }
}

总结
#

CRD
#

CRD 是 Kubernetes 的一种内置原生的一个资源类型,是 Custom Resource Definition 的缩写,它是自定义资源的定义,用来描述自定义资源长什么样子。

它是用来向 Kubernetes 集群注册一种新资源,用于拓展 Kubernetes 集群的能力。

有了 CRD,我们可以自定义底层基础设施的抽象,能够基于公司的产品自造概念(换句话来说可以根据业务需求来定制我们的资源类型),利用 Kubernetes 已有的资源和能力,通过乐高积木的模式,定义出更高层次的抽象。

CR
#

CR 就更好理解了,它实际上就是 CRD 的一个实例,是符合 CRD 中字段格式定义的一个资源描述。

CRDs + Controllers
#

我们都知道 Kubernetes 的扩展能力很强大,但是光有 CRD 并没有啥用,它背后还需要有控制器(Custom Controller)的支撑,才能体现出 CRD 的价值,Custom Controller 会去监听 CR 的 CRUD 事件来实现自定义业务逻辑。

在 Kubernetes 的世界里,可以说是 CRDs + Controllers = Everything。

后面我会给大家分享一下,利用 Kubebuilder 如何从零开始开发 CRDs、Controllers 和 Admission Webhooks 的例子,我们下一篇分享见。

希望这篇文章对你有所帮助,比心。。。

References
#

Official Account
关注公众号 「Cloud Native 101」,设为「星标」,不迷路!

Related

Kubernetes Patterns 101: 初识初始化容器,探索容器设计的精髓
··2989 字·6 分钟
云原生 Kubernetes Init Container Kubernetes Patterns 101
Kubernetes Patterns 101: 边车模式详解,解锁容器设计的新视角
·2794 字·6 分钟
云原生 Kubernetes Sidecar Docker Kubernetes Patterns 101
SSH 101: 一文掌握使用 SSH 的正确姿势
·2370 字·5 分钟
云原生 ssh 工程效率