Kubernetes CRD开发汇总
1. Kubernetes CRD开发
1.1 kubernetes 自定义资源(CRD)
在研究 Service Mesh 的过程中,发现 Istio 很多参数都通过 kubernetes CRD 来管理,例如 VirtualService 和 DestinationRule,这种方式使部署在 k8s 集群上的服务的管理方式更趋向一致。
kubernetes 的资源管理方式和声明式 API 的良好设计使得在这个平台上的功能扩展变得异常容易。例如 CoreOS 推出的 Operator 框架就是一个很好的例子。
这篇文章通过一个简短的示例来演示如何创建自定义资源。
1.1.1 创建 CRD(CustomResourceDefinition)
这里以创建一个简单的弹性伸缩配置的 CRD 为例。将下面的内容保存在 scaling_crd.yaml
文件中。
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:# name must match the spec fields below, and be in the form: <plural>.<group>name: scalings.control.example.com
spec:# group name to use for REST API: /apis/<group>/<version>group: control.example.com# list of versions supported by this CustomResourceDefinitionversions:- name: v1# Each version can be enabled/disabled by Served flag.served: true# One and only one version must be marked as the storage version.storage: true# either Namespaced or Clusterscope: Namespacednames:# plural name to be used in the URL: /apis/<group>/<version>/<plural>plural: scalings# singular name to be used as an alias on the CLI and for displaysingular: scaling# kind is normally the CamelCased singular type. Your resource manifests use this.kind: Scaling# shortNames allow shorter string to match your resource on the CLIshortNames:- sc
通过 kubectl 创建这个 CRD:
kubectl apply -f scaling_crd.yaml
1.1.2 创建自定义资源的对象
我们编写一个 test.yaml
文件来创建一个自定义的 Scaling
对象。
apiVersion: "control.example.io/v1"
kind: Scaling
metadata:name: test
spec:targetDeployment: testminReplicas: 1maxReplicas: 5metricType: CPUstep: 1scaleUp: 80scaleDown: 40
通过 kubectl 创建:
kubectl apply -f test.yaml
提示:
scaling.control.example.io/test created
你可以通过 kubectl 查看已经创建的名为 test
的 Scaling 对象。
kubectl get scalings.control.example.io test -o yaml
会输出类似如下的结果:
apiVersion: control.example.io/v1
kind: Scaling
metadata:annotations:kubectl.kubernetes.io/last-applied-configuration: |{"apiVersion":"control.example.io/v1","kind":"Scaling","metadata":{"annotations":{},"name":"test","namespace":"default"},"spec":{"maxReplicas":5,"metricType":"CPU","minReplicas":1,"scaleDown":40,"scaleUp":80,"step":1,"targetDeployment":"test"}}creationTimestamp: "2019-01-09T12:22:36Z"generation: 1name: testnamespace: defaultresourceVersion: "1316610"selfLink: /apis/control.example.io/v1/namespaces/default/scalings/testuid: 28717b37-5ac2-11e9-89f8-080027a9fd96
spec:maxReplicas: 5metricType: CPUminReplicas: 1scaleDown: 40scaleUp: 80step: 1targetDeployment: test
我们可以像操作 k8s 内置的 Deployment 资源一样操作我们创建的 Scaling 资源,同样可以对它进行更新和删除的操作。
1.1.3 参数校验
上面的 CRD 配置中我们并没有指定这个资源的 Spec,也就是说用户可以使用任意的 Spec 创建这个 Scaling 资源,这并不符合我们的要求。我们希望在用户创建 Scaling 对象时,可以像 k8s 的原生资源一样进行参数校验,如果出错的情况下,就不会去创建或更新这个对象,而是给用户错误提示。
k8s 目前提供了两种方式来实现参数校验,OpenAPI v3 schema
和 validatingadmissionwebhook
。
这里主要使用比较简单的 OpenAPI v3 schema
来实现。validatingadmissionwebhook
需要用户自己提供一个检查服务,通过创建 ValidatingWebhookConfiguration
让 APIServer 将指定的操作请求转发给这个检查服务,检查服务返回 true 或者 false,决定参数校验是否成功。
我们将之前的 CRD 配置文件 scaling_crd.yaml
做一下修改,增加参数校验的部分:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:name: scalings.control.example.io
spec:group: control.example.ioversions:- name: v1served: truestorage: truescope: Namespacednames:plural: scalingssingular: scalingkind: Scalingvalidation:openAPIV3Schema:properties:spec:required:- targetDeployment- minReplicas- maxReplicas- metricType- step- scaleUp- scaleDownproperties:targetDeployment:type: stringminReplicas:type: integerminimum: 0maxReplicas:type: integerminimum: 0metricType:type: stringenum:- CPU- MEMORY- REQUESTSstep:type: integerminimum: 1scaleUp:type: integerscaleDown:type: integerminimum: 0
可以看到 spec 中增加了 validation 字段,其中定义了对各个参数的检验要求。
required
表示数组中的参数必须要设置。type string
和type integer
表示限制参数类型。minimum: 0
表示数字最小值为 0。enum
表示参数只能在指定的值中。
具体支持哪些校验方法可以通过 https://github.com/OAI/OpenAPI-Specification 查看。
更新 CRD 资源:
kubectl apply -f scaling_crd.yaml
再次修改 test.yaml
测试我们的参数校验是否生效,将 targetDeployment: test
这一行删除。
更新 Name 为 test 的 Scaling 对象。
kubectl apply -f test.yaml
可以看到错误提示输出如下:
validation failure list:
spec.targetDeployment in body is required
至此,不符合我们要求的 Scaling 对象将不被允许创建。
1.2 kubernetes 自定义控制器
kubernetes 的 controller-manager 通过 APIServer 实时监控内部资源的变化情况,通过各种操作将系统维持在一个我们预期的状态上。比如当我们将 Deployment 的副本数增加时,controller-manager 会监听到此变化,主动创建新的 Pod。
对于通过 CRD 创建的资源,也可以创建一个自定义的 controller 来管理。
1.2.1 目的
在上文中我们创建了自己的 Scaling 资源,如果我们想要通过监听该资源的变化来实现实时的弹性伸缩,就需要自己写一个控制器,通过 APIserver watch 该资源的变化。
当我们创建了一个 Scaling 对象,自定义控制器都能获得其参数,之后执行相关的检查,根据结果决定是否需要扩容或缩容相关的实例。
1.2.2 实现
client-go 这个 repo 封装了对 k8s 内置资源的一些常用操作,包括了 clients/listers/informer 等对象和函数,可以 通过 Watch 或者 Get List 获取对应的 Object,并且通过 Cache,可以有效避免对 APIServer 频繁请求的压力。
但是对于我们自己创建的 CRD,没有办法直接使用这些代码。
通过 code-generator 这个 repo,我们可以提供自己的 CRD 相关的结构体,轻松的生成 client-go 中类似的代码,方便我们编写自己的控制器。
1.2.3 在自己的项目中使用 code-generator
这里主要参考了 sample-controller 这个项目。
1.2.3.1 创建自定义 CRD 结构体
假设我们有一个 test
repo,在根目录创建一个 pkg
目录,用于存放我们自定义资源的 Spec 结构体。
这里我们要知道自己创建的自定义资源的相关内容:
- API Group: 我们使用的是
control.example.com
。 - Version: 我们用的是
v1
,但是可以同时存在多个版本。 - 资源名称: 这里是
Scaling
。
接着创建如下的目录结构:
mkdir -p pkg/apis/control/v1
在 pkg/apis/control
目录下创建一个 register.go 文件。内容如下:
package controlconst (GroupName = "control.example.com"
)
创建 pkg/apis/control/v1/types.go
文件,内容如下:
package v1import (metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Objecttype Scaling struct {metav1.TypeMeta `json:",inline"`metav1.ObjectMeta `json:"metadata,omitempty"`Spec ScalingSpec `json:"spec"`
}type ScalingSpec struct {TargetDeployment string `json:"targetDeployment"`MinReplicas int `json:"minReplicas"`MaxReplicas int `json:"maxReplicas"`MetricType string `json:"metricType"`Step int `json:"step"`ScaleUp int `json:"scaleUp"`ScaleDown int `json:"scaleDown"`
}// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Objecttype ScalingList struct {metav1.TypeMeta `json:",inline"`metav1.ListMeta `json:"metadata,omitempty"`Items []Scaling `json:"items"`
}
这个文件中我们定义了 Scaling
这个自定义资源的结构体。
其中,类似 // +<tag_name>[=value]
这样格式的注释,可以控制代码生成器的一些行为。
- +genclient: 为这个 package 创建 client。
- +genclient:noStatus: 当创建 client 时,不存储 status。
- +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object: 为结构体生成 deepcopy 的代码,实现了 runtime.Object 的 Interface。
创建 doc 文件,pkg/apis/control/v1/doc.go
:
// +k8s:deepcopy-gen=package
// +groupName=control.example.compackage v1
最后 client 对于自定义资源结构还需要一些接口,例如 AddToScheme
和 Resource
,这些函数负责将结构体注册到 schemes 中去。
为此创建 pkg/apis/control/v1/register.go
文件:
package v1import ("test/pkg/apis/control"metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/apimachinery/pkg/runtime""k8s.io/apimachinery/pkg/runtime/schema"
)var SchemeGroupVersion = schema.GroupVersion{Group: control.GroupName,Version: "v1",
}func Resource(resource string) schema.GroupResource {return SchemeGroupVersion.WithResource(resource).GroupResource()
}var (// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.SchemeBuilder runtime.SchemeBuilderlocalSchemeBuilder = &SchemeBuilderAddToScheme = localSchemeBuilder.AddToScheme
)func init() {// We only register manually written functions here. The registration of the// generated functions takes place in the generated files. The separation// makes the code compile even when the generated files are missing.localSchemeBuilder.Register(addKnownTypes)
}// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {scheme.AddKnownTypes(SchemeGroupVersion,&Scaling{},&ScalingList{},)metav1.AddToGroupVersion(scheme, SchemeGroupVersion)return nil
}
至此,初期的准备工作已近完成,可以通过代码生成器来自动帮助我们生成相关的 client, informer, lister 的代码。
1.2.3.2 生成代码
通常我们通过创建一个 hack/update-codegen.sh
脚本来固化生成代码的步骤。
$GOPATH/src/k8s.io/code-generator/generate-groups.sh all \
test/pkg/client \
test/pkg/apis \
control:v1
可以看到,执行这个脚本,需要使用 code-generator 中的的脚本,所以需要先通过 go get 将 code-generator 这个 repo 的内容下载到本地,并且编译出相关的二进制文件(client-gen, informer-gen, lister-gen)。
执行完成后,可以看到 pkg
目录下多了一个 client
目录,其中就包含了 informer 和 lister 相关的代码。
并且在 pkg/apis/control/v1
目录下,会多一个 zz_generated.deepcopy.go
文件,用于 deepcopy 相关的处理。
1.2.3.3 创建自定义控制器代码
这里只创建一个 main.go
文件用于简单示例,通过我们刚刚自动生成的代码,每隔一段时间,自动通过 lister 获取所有的 Scaling 对象。
package mainimport ("fmt""log""os""time""k8s.io/apimachinery/pkg/labels""k8s.io/client-go/tools/clientcmd"clientset "test/pkg/client/clientset/versioned"informers "test/pkg/client/informers/externalversions"
)func main() {client, err := newCustomKubeClient()if err != nil {log.Fatalf("new kube client error: %v", err)}factory := informers.NewSharedInformerFactory(client, 30*time.Second)informer := factory.Control().V1().Scalings()lister := informer.Lister()stopCh := make(chan struct{})factory.Start(stopCh)for {ret, err := lister.List(labels.Everything())if err != nil {log.Printf("list error: %v", err)} else {for _, scaling := range ret {log.Println(scaling)}}time.Sleep(5 * time.Second)}
}func newCustomKubeClient() (clientset.Interface, error) {kubeConfigPath := os.Getenv("HOME") + "/.kube/config"config, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath)if err != nil {return nil, fmt.Errorf("failed to create out-cluster kube cli configuration: %v", err)}cli, err := clientset.NewForConfig(config)if err != nil {return nil, fmt.Errorf("failed to create custom kube client: %v", err)}return cli, nil
}
编译并执行此代码,每隔 5 秒钟,会在标准输出中输出我们创建的所有 Scaling 对象的具体内容。
需要注意的是,这里生成的 kube client 只能用于操作我们自己的 Scaling 对象。如果需要操作 Deployment 这一类的内置的资源,仍然需要使用 client-go
中的代码,因为不同的 clientset.Interface
实现的接口也是不同的。
上述的方法也是最顶层的实现方式,下面介绍两种可以快速搭建CRD开发的工具:一种是kubebuilder,另一种是operader-sdk,该工具目前正在与kubebuilder融合,其中kubebuilder是一个官方提供的快速实现Operator的工具包,可以快速生成k8s的CRD、Controller、Webhook,我们只需要实现业务逻辑。
kubebuilder封装了controller-runtime和controller-tools工具,通过controller-gen来生成代码,提供脚手架工具初始化 CRDs 工程,自动生成 boilerplate 代码和配置;提供代码库封装底层的 K8s client-go;简化了用户创建Operator的步骤:
- 创建工作目录,初始化项目
- 创建API,填充字段
- 定义 CRD
- 编写 Controller 逻辑
- 验证测试
- 发布到集群中
1.3 kubebuilder 开发自定义资源(CRD)
1.3.1 创建脚手架工程
kubebuilder init --domain edas.io1
这一步创建了一个 Go module 工程,引入了必要的依赖,创建了一些模板文件。
1.3.2 创建 API
kubebuilder create api --group apps --version v1alpha1 --kind Application1
这一步创建了对应的 CRD 和 Controller 模板文件,经过 1、2 两步,现有的工程结构如图 2 所示:
1.3.3 定义 CRD
在上图中对应的文件edasapplication_types.go定义 Spec 和 Status。
1.3.4 编写 Controller 逻辑
在上图中对应的文件edasapplication_controller.go实现 Reconcile 逻辑。
1.3.5 测试发布
本地测试完之后使用 Kubebuilder 的 Makefile 构建镜像,部署我们的 CRDs 和 Controller 即可。
1.4 operator-sdk 开发自定义资源(CRD)
该 SDK 提供了一个工作流程,用于使用 Go、 Ansible 或 Helm来开发operators。
下面的工作流用于创建新的 Go operator:
- 创建新的 operator project,使用 SDK Command Line Interface(CLI)。
- 定义新的resource APIs,通过添加Custom Resource Definitions(CRD)。
- 定义 Controllers 观察和协调资源。
- 编写协调逻辑,使用 SDK 和 controller-runtime APIs。
- 使用 SDK CLI 构建和生成 operator deployment manifests。
下面的工作流用于创建新的Ansible operator:
- 创建新的 operator project,使用SDK Command Line Interface(CLI)。
- 编写协调逻辑,为自己的对象,使用ansible playbooks 和 roles。
- 使用 SDK CLI 构建和生成 operator deployment manifests。
- 可选添加额外的 CRD’s,使用 SDK CLI,重复步骤2、3。
下面的工作流用于创建新的Helm operator:
- 创建新的 operator project,使用 SDK Command Line Interface(CLI)。
- 创建新的 (或添加已有的) Helm chart,用于 operator’s 协调逻辑使用。
- 使用SDK CLI 构建和生成operator deployment manifests。
- 可选添加额外的CRD’s,使用SDK CLI,重复步骤 2 和 3。
下面就以Go来创建一个operators开发示例
1.4.1 创建脚手架工程
$ operator-sdk new app-operator
$ cd app-operator
1.4.2 创建API
$ operator-sdk add api --api-version=app.example.com/v1alpha1 --kind=AppService
1.4.3 创建控制器
$ operator-sdk add controller --api-version=app.example.com/v1alpha1 --kind=AppService
1.4.4 编译并PUSH镜像
$ operator-sdk build quay.io/example/app-operator
$ docker push quay.io/example/app-operator
1.4.5 测试发布
$ kubectl create -f deploy/
1.5 总结
通过上述介绍来看Kubernetes 中CRD 的开发方式有多种,其中第一种不借用工具的开发方式其实使用的方法是调用client-go 和 code-generate两个工具库中的方法实现CRD资源的管理,涉及的知识点也相对底层,如果阅读了k8s kube-apiserver源码的人更加容易理解这种开发方式;通过kubebuilder和operator-sdk这两种工具的开发方式其实都是将client-go、controller-runtime和controller-tools代码进行了再封装,封装后的库为controller-gen,其目的是简化用户在不理解kube-apiserver等实现的基础上开发CRD的流程。
Kubernetes CRD开发汇总相关推荐
- Kubernetes CRD开发模式及源码实现深入剖析-Kubernetes商业环境实战
专注于大数据及容器云核心技术解密,可提供全栈的大数据+云原生平台咨询方案,请持续关注本套博客.如有任何学术交流,可随时联系.留言请关注<数据云技术社区>公众号. 1 CRD资源扩展 CRD ...
- Kubernetes CRD开发工具Operator-SDK简介
原文连接:https://blog.csdn.net/weixin_33918114/article/details/92211707 概览 原文来自:https://github.com/opera ...
- 深入了解Kubernetes CRD开发工具kubebuilder
原文连接:https://blog.csdn.net/u012986012/article/details/120271091 普通开发流程 如果不借助任何Operator脚手架,我们是如何实现Ope ...
- Kubernetes CRD开发实践
背景 Kubernetes的最大亮点之一必定是它的声明式API设计,所谓的声明式就是告诉Kubernetes你要什么,而不是告诉它怎么做命令.我们日常使用Kubernetes做编排工作的时候,经常会接 ...
- 【云原生】Kubernetes CRD 详解(Custom Resource Definition)
文章目录 一.概述 二.定制资源 1)定制资源 和 定制控制器 2)定制控制器 3)Operator 介绍 1.Operator Framework 2.Operator 安装 3.安装 Operat ...
- Kubernetes CRD (CustomResourceDefinition) 自定义资源类型
目录 1.CRD (CustomResourceDefinition) 介绍 1.1 client-go 组件 1.2 Custom Controller 组件 2.环境.软件准备 3.Kuberne ...
- 资源放送丨《 Kubernetes Operator 开发范式》PPT视频
回顾:之前,墨天轮邀请到PingCAP工程师 吴叶磊 分享了直播< Kubernetes Operator 开发范式>,在这里我们共享一下PPT和视频,供大家参考学习. Kubernete ...
- iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总
iOS开发系列--通讯录.蓝牙.内购.GameCenter.iCloud.Passbook系统服务开发汇总 --系统应用与系统服务 iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如 ...
- iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总,icloudpassbook
iOS开发系列--通讯录.蓝牙.内购.GameCenter.iCloud.Passbook系统服务开发汇总,icloudpassbook --系统应用与系统服务 iOS开发过程中有时候难免会使用iOS ...
最新文章
- wait跟sleep的区别
- kaldi 源码分析(十) - gmm-init-mono.c分析
- ASP.NET Core2基于RabbitMQ对Web前端实现推送功能
- ECCV 2020 | 微软亚洲研究院精选论文摘录
- java启动参数_Java启动参数的思考
- oracle内存参数越大越好吗,什么是Oracle内存参数调优技术?
- anndroid 之 Intent(意图)使用示例
- 不用wp-pagenav,wordpress自带分页代码调用
- java持久层用文件_JAVA中用三种方法将字符串持久化到文件中
- VS2013 community卸载后不能重装的问题
- 【NOIP2017】【Luogu3951】小凯的疑惑
- [转载]CMMI之功能点估算法:EI、EQ和EO
- Unity 内置渲染管线、SRP、URP、HDRP区别
- 【win10】设置电脑固定IP,解除固定IP
- LINUX gdk/X11正确获取DPI/Resolution的函数
- 复习jquery菜鸟教程
- 九宫格动态密码--快速入门
- Swift语言难度大吗?适不适合零基础学习?
- 姓名转拼音的几种格式
- 2021-04-06-MSF之永恒之蓝
热门文章
- oracle11gr2 active data guard,Oracle11gR2 Aactive DataGuard(手动)装配部署及维护文档(三)之升级及rman...
- 数据中心基础设施运维是什么?
- 谷歌开始用AI技术帮助数据中心散热节省成本
- 虚拟机备份oracle异常,客户端连接虚拟机Oracle服务器异常
- AI:人工智能的多模态融合模型的简介、发展以及未来趋势
- keras-yolo3:python库之keras-yolo3的简介、安装、使用方法详细攻略
- 成功解决Redirection is not supported
- ML之NBLoR:利用NB(朴素贝叶斯)、LoR(逻辑斯蒂回归)算法(+TfidfVectorizer)对Rotten Tomatoes影评数据集进行文本情感分析—五分类预测
- HighNewTech:70后、80后、90后、95后职场人大数据调查(有趣的五个结论)——源于猎聘网
- HighNewTech之QAB:重新温读张首晟教授2018年8月演讲PPT《量子计算, 人工智能与区块链》