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

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

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

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

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

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

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

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

一个例子

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

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

bash 复制代码
➜ kubectl get dnr -owide

NAME                   AGE
dagnoderunner-sample   86s

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

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

apiVersion 字段的内容也不是我们平常所熟悉的,apps/v1apiextensions.k8s.io/v1v1,而是 cloudnative101.net/v1beta1

bash 复制代码
➜ 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 吧,如下所示

bash 复制代码
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

bash 复制代码
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 资源。

bash 复制代码
➜ kubectl get crd

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

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

bash 复制代码
➜ 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 关键字段说明

field desc
metadata.name CRD 的名字,名称与 spec 中的字段匹配:<spec.names.plural>.<spec.group>
spec.scope CRD 可以是命名空间范围的,也可以是集群范围的,值为 Namespaced 或 Cluster
spec.group REST API 使用的组名称:/apis/<group>/<version>
spec.names.kind CamelCased 格式的单数类型
spec.names.plural REST API 使用的复数名称:/apis/<group>/<version>/<plural>
spec.names.singular kubectl 中使用的资源单数名称
spec.names.shortNames[*] kubectl 中使用的资源简称列表
spec.names.categories[*] 自定义资源所属的分组资源的列表
spec.versions[*].name REST 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 集群內执行。

bash 复制代码
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,当然会失败。

bash 复制代码
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 对象的定义

bash 复制代码
# starts a proxy to the Kubernetes API server
kubectl proxy --port=8080

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

bash 复制代码
➜ 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 实例列表

bash 复制代码
➜ 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

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

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

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

CR

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

CRDs + Controllers

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

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

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

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

References

相关推荐
wenyue11211 小时前
云原生开发框架
数据库·云原生
颜淡慕潇1 小时前
【K8S系列】深入解析 Kubernetes 中的 Deployment
后端·云原生·容器·kubernetes
蚂蚁在飞-7 小时前
云原生时代的轻量级反向代理Traefik
云原生·golang
Lorin 洛林8 小时前
Spring Cloud Netflix 系列:Eureka 经典实战案例和底层原理解析
云原生·eureka
运维&陈同学8 小时前
【zookeeper04】消息队列与微服务之zookeeper客户端访问
linux·后端·微服务·zookeeper·云原生·消息队列·云计算
petaexpress10 小时前
k8s微服务架构就是云原生吗?两者是什么关系
微服务·云原生·架构·kubernetes·k8s
tester Jeffky10 小时前
云原生革命:构建未来应用的无限可能
云原生
WeeJot嵌入式12 小时前
云原生:构建未来应用的基石
云原生
wenyue11211 天前
Enhancing K8s Gateway API with Easegress Without Changing a Single Line of Code
容器·kubernetes·gateway
.Ayang1 天前
微服务介绍
网络·安全·网络安全·微服务·云原生·架构·安全架构