Kubernetes CRD详解
概述
CRD(Custom Resource Definition) 本身是一种Kubernetes内置的资源类型,即自定义资源的定义,用于描述用户定义的资源是什么样子,CRD的相关概念:
CRD是 v1.7 +新增的无需改变代码 就可以扩展 Kubernetes API 的机制,用来管理自定义对象。它实际上是ThirdPartyResources(TPR) 的升级版本,而 TPR 已经在 v1.8 中删除
从Kubernetes 的用户角度来看,所有东西都叫资源Resource,就是Yaml里的字段Kind的内容,例如Service、Deployment等
除了常见内置资源之外,Kubernetes允许用户自定义资源Custom Resource,而CRD表示自定义资源的定义
当你创建新的CustomResourceDefinition(CRD)时,Kubernetes API服务器会为你所指定的每个版本生成一个新的 RESTful 资源路径
基于CRD对象所创建的自定义资源可以是名字空间作用域的,也可以是集群作用域的, 取决于CRD对象spec.scope字段的设置
定义CRD对象的操作会使用你所设定的名字和模式定义(Schema)创建一个新的定制资源, Kubernetes API负责为你的定制资源提供存储和访问服务。 CRD对象的名称必须是合法的 DNS子域名
DNS子域名
很多资源类型需要可以用作DNS子域名的名称,名称必须满足如下规则:
不能超过253个字符
只能包含小写字母、数字,以及 '-' 和 '.'
必须以字母数字开头
必须以字母数字结尾
定制资源
定制资源(Custom Resource) 是对Kubernetes API的扩展
定制资源和定制控制器
资源(Resource)是 Kubernetes API 中的一个端点, 其中存储的是某个类别的API对象的一个集合,例如内置的Pod资源包含一组Pod对象
定制资源(Custom Resource)是对Kubernetes API的扩展, 定制资源所代表的是对特定Kubernetes安装的一种定制,不过很多Kubernetes 核心功能现在都用定制资源来实现,这使得Kubernetes更加模块化
定制资源可以通过动态注册的方式在运行中的集群内或出现或消失,集群管理员可以独立于集群更新定制资源
一旦某定制资源被安装,用户可以使用kubectl来创建和访问其中的对象, 就像他们为Pod这种内置资源所做的一样
定制控制器
就定制资源本身而言,它只能用来存取结构化的数据,当你将定制资源与定制控制器(Custom Controller) 结合时, 定制资源就能够提供真正的声明式API(Declarative API)
Operator模式就是将定制资源与定制控制器相结合的
Operator介绍
Kubernetes CRD Operator = kubernetes CRD + custom controller
Operator是由CoreOS开发的,用来扩展 Kubernetes API,特定的应用程序控制器,它用来创建、配置和管理复杂的有状态应用,如数据库、缓存和监控系统
Operator基于Kubernetes的资源和控制器概念之上构建,但同时又包含了应用程序特定的领域知识,创建Operator的关键是CRD(自定义资源)的设计
Operator是Kubernetes的扩展软件, 它利用定制资源管理应用及其组件。 Operator遵循Kubernetes的理念,特别是在控制器方面
Operator模式会封装你编写的(Kubernetes 本身提供功能以外的)任务自动化代码
Kubernetes的Operator模式概念允许你在不修改 Kubernetes自身代码的情况下, 通过为一个或多个自定义资源关联控制器来扩展集群的能力
Operator是Kubernetes API的客户端。Operator旨在为其管理的资源提供强大的自动化功能,因此它还需要一些额外的支持性代码
Operator Framework
Operator Framework同样也是CoreOS开源的一个用于快速开发Operator的工具包,该框架包含两个主要的部分:
Operator SDK——无需了解复杂的Kubernetes API特性,即可让你根据你自己的专业知识构建一个Operator应用
Operator Lifecycle Manager OLM——帮助你安装、更新和管理跨集群的运行中的所有Operator(以及他们的相关服务)
Operator安装
Operator SDK提供以下工作流来开发一个新的Operator
使用SDK创建一个新的Operator项目
通过添加自定义资源(CRD)定义新的资源API
指定使用SDK API来watch的资源
定义Operator的协调(reconcile)逻辑
使用Operator SDK构建并生成Operator部署清单文件
安装Operator SDK
[root@k8s01 ~]# wget https://github.com/operator-framework/operator-sdk/releases/download/v1.23.0/operator-sdk_linux_amd64
#添加可执行权限
[root@k8s01 ~]# chmod +x operator-sdk_linux_amd64
#添加软链
[root@k8s01 ~]# ln -s /opt/k8s/crd/Operator/operator-sdk_linux_amd64 /usr/local/bin/operator-sdk
Operator简单使用
[root@k8s01 ~]# operator-sdk init --domain=example.com --repo=github.com/example-inc/memcached-operator
#步骤二: 创建 API(operator-sdk create api --group cache --version v1 --kind Memcached --resource=true --controller=true)
[root@k8s01 ~]# operator-sdk create api --group cache --version v1 --kind Memcached --resource=true --controller=true
#步骤三: 构建镜像-需要本地存在 docker 环境(make docker-build IMG=liumiaocn/memcache:v1)
[root@k8s01 ~]# make docker-build IMG=liumiaocn/memcache:v1
#步骤四: 运行Operator-需要环境具备 K8s/K3s(make install && make deploy IMG=liumiaocn/memcache:v1)
[root@k8s01 ~]# make install && make deploy IMG=liumiaocn/memcache:v1
#步骤五:创建自定义资源
[root@k8s01 ~]# kubectl apply -f config/samples/cache_v1_memcached.yaml
#步骤六:删除CR和相关资源(kubectl delete -f config/samples/cache_v1_memcached.yaml)
[root@k8s01 ~]# kubectl delete -f config/samples/cache_v1_memcached.yaml
Kubernetes API聚合层
使用聚合层(Aggregation Layer),用户可以通过附加的API扩展 Kubernetes, 而不局限于 Kubernetes 核心API提供的功能。 这里的附加API可以是现成的解决方案,比如 metrics server, 或者你自己开发的API
聚合层不同于 定制资源(Custom Resources)。 后者的目的是让kube-apiserver能够识别新的对象类别(Kind)
聚合层在kube-apiserver进程内运行,在扩展资源注册之前,聚合层不做任何事情
要注册API,你可以添加一个APIService 对象,用它来"申领" Kubernetes API中的URL路径。 自此以后聚合层将把发给该 API 路径的所有内容(/apis/myextension.mycompany.io/v1/…) 转发到已注册的 APIService
声明式APIs
典型的在声明式API中:
API包含相对而言为数不多的、尺寸较小的对象(资源)
对象定义了应用或者基础设施的配置信息
对象更新操作频率较低
通常需要人来读取或写入对象
对象的主要操作是CRUD风格的(创建、读取、更新和删除)
不需要跨对象的事务支持:API对象代表的是期望状态而非确切实际状态
命令式 API(Imperative API)与声明式有所不同,以下迹象表明你的 API 可能不是声明式的:
客户端发出"做这个操作"的指令,之后在该操作结束时获得同步响应
客户端发出"做这个操作"的指令,并获得一个操作ID,之后需要检查一个Operation(操作) 对象来判断请求是否成功完成
你会将你的API类比为远程过程调用(Remote Procedure Call,RPCs)
直接存储大量数据;例如每个对象几 kB,或者存储上千个对象
需要较高的访问带宽(长期保持每秒数十个请求)
存储有应用来处理的最终用户数据(如图片、个人标识信息(PII)等)或者其他大规模数据
在对象上执行的常规操作并非CRUD风格
添加定制资源
Kubernetes提供了两种方式供你向集群中添加定制资源:
CRD相对简单,创建CRD可以不必编程
API聚合需要编程,但支持对API行为进行更多的控制,例如数据如何存储以及在不同API版本间如何转换等
Kubernetes提供这两种选项以满足不同用户的需求,这样就既不会牺牲易用性也不会牺牲灵活性
聚合API指的是一些下位的API服务器,运行在主API服务器后面;主API服务器以代理的方式工作。这种组织形式称作API聚合(API Aggregation,AA),对用户而言看起来仅仅是Kubernetes API被扩展了
CRD允许用户创建新的资源类别同时又不必添加新的API服务器, 使用CRD时你并不需要理解API聚合
CRD | 聚合API |
无需编程,用户可选择任何语言来实现CRD控制器 | 需要编程,并构建可执行文件和镜像 |
无需额外运行服务,CRD由API服务器处理 | 需要额外创建服务,且该服务可能失效 |
一旦CRD被创建,不需要持续提供支持,Kubernetes主控节点升级过程中自动会带入缺陷修复 | 可能需要周期性地从上游提取缺陷修复并更新聚合 API服务器 |
无需处理API的多个版本,例如当你控制资源的客户端时,你可以更新它使之与API同步 | 你需要处理API的多个版本,例如在开发打算与很多人共享的扩展时 |
仅支持CRUD操作,例如 "logs" 或 "exec"的操作是不支持的 | 支持 CRUD 之外的操作 |
访问定制资源
Kubernetes客户端库可用来访问定制资源,并非所有客户端库都支持定制资源。Go和Python客户端库是支持的
当你添加了新的定制资源后,可以用如下方式之一访问它们
kubectl
Kubernetes动态客户端(DynamicClient),可以对任意Kubernetes资源进行RESTFful操作,包括CRD自定义资源
编写的REST客户端
使用Kubernetes 客户端生成工具所生成的客户端,生成客户端的工作有些难度,不过某些项目可能会随着 CRD或聚合API一起提供一个客户端
CRD示例
创建CRD(定制资源)
当你创建新的CustomResourceDefinition(CRD)时,Kubernetes API服务器会为你所指定的每个版本生成一个新的RESTful资源路径。 基于CRD对象所创建的自定义资源可以是名字空间作用域的,也可以是集群作用域的, 取决于CRD对象spec.scope字段的设置
[root@k8s01 ~]# cat > resourcedefinition.yaml << EOF
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
#名字必需与下面的spec字段匹配,并且格式为'<名称的复数形式>.<组名>'
name: crontabs.stable.example.com
spec:
#组名称,用于REST API: /apis/<组>/<版本>
group: stable.example.com
#列举此CustomResourceDefinition所支持的版本
versions:
- name: v1
#每个版本都可以通过served标志来独立启用或禁止
served: true
#其中一个且只有一个版本必需被标记为存储版本
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
cronSpec:
type: string
image:
type: string
replicas:
type: integer
#可以是Namespaced或Cluster
scope: Namespaced
names:
#名称的复数形式,用于URL:/apis/<组>/<版本>/<名称的复数形式>
plural: crontabs
#名称的单数形式,作为命令行使用时和显示时的别名
singular: crontab
#kind通常是单数形式的驼峰命名(CamelCased)形式。你的资源清单会使用这一形式
kind: CronTab
#shortNames允许你在命令行使用较短的字符串来匹配资源
shortNames:
- ct
EOF
执行创建
[root@k8s01 ~]# kubectl apply -f resourcedefinition.yaml
customresourcedefinition.apiextensions.k8s.io/crontabs.stable.example.com created
这样一个新的受名字空间约束的RESTful API 端点会被创建在
/apis/stable.example.com/v1/namespaces/
对象的kind将是来自你上面创建时 所用的spec中指定的 CronTab
[root@k8s01 ~]# kubectl get --raw /apis/stable.example.com/v1/
{"kind":"APIResourceList","apiVersion":"v1","groupVersion":"stable.example.com/v1","resources":[{"name":"crontabs","singularName":"crontab","namespaced":true,"kind":"CronTab","verbs":["delete","deletecollection","get","list","patch","create","update","watch"],"shortNames":["ct"],"storageVersionHash":"EdX2FP+L0js="}]}
[root@k8s01 ~]# kubectl get --raw /apis/stable.example.com/v1/|python -m json.tool
{
"apiVersion": "v1",
"groupVersion": "stable.example.com/v1",
"kind": "APIResourceList",
"resources": [
{
"kind": "CronTab",
"name": "crontabs",
"namespaced": true,
"shortNames": [
"ct"
],
"singularName": "crontab",
"storageVersionHash": "EdX2FP+L0js=",
"verbs": [
"delete",
"deletecollection",
"get",
"list",
"patch",
"create",
"update",
"watch"
]
}
]
}
创建定制对象(定制控制器)
在创建了CustomResourceDefinition对象之后,你可以创建定制对象(Custom Objects),定制对象可以包含定制字段,这些字段可以包含任意的JSON数据,在下面的例子中在类别为CronTab的定制对象中,设置了cronSpec和image定制字段,类别CronTab来自你在上面所创建的CRD的规约
[root@k8s01 ~]# cat >my-crontab.yaml<<EOF
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image
EOF
执行创建命令
[root@k8s01 ~]# kubectl apply -f my-crontab.yaml
crontab.stable.example.com/my-new-cron-object created
你就可以使用kubectl来管理你的CronTab对象了例如:
[root@k8s01 ~]# kubectl get ct -o yaml
apiVersion: v1
items:
- apiVersion: stable.example.com/v1
kind: CronTab
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"stable.example.com/v1","kind":"CronTab","metadata":{"annotations":{},"name":"my-new-cron-object","namespace":"default"},"spec":{"cronSpec":"* * * * */5","image":"my-awesome-cron-image"}}
creationTimestamp: "2023-06-25T07:09:27Z"
generation: 1
managedFields:
- apiVersion: stable.example.com/v1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
f:spec:
.: {}
f:cronSpec: {}
f:image: {}
manager: kubectl-client-side-apply
operation: Update
time: "2023-06-25T07:09:27Z"
name: my-new-cron-object
namespace: default
resourceVersion: "51213757"
selfLink: /apis/stable.example.com/v1/namespaces/default/crontabs/my-new-cron-object
uid: 5094d58b-ddd1-445e-a2a3-8a43bf47eb3f
spec:
cronSpec: '* * * * */5'
image: my-awesome-cron-image
kind: List
metadata:
resourceVersion: ""
selfLink: ""
删除CustomResourceDefinition
当你删除某CustomResourceDefinition时,服务器会卸载其RESTful API端点,并删除服务器上存储的所有定制对象
[root@k8s01 ~]# kubectl delete -f resourcedefinition.yaml
customresourcedefinition.apiextensions.k8s.io "crontabs.stable.example.com" deleted
[root@k8s01 ~]# kubectl get crontabs
Error from server (NotFound): Unable to list "stable.example.com/v1, Resource=crontabs": the server could not find the requested resource (get crontabs.stable.example.com)