五分钟轻松熟悉一个k8s Operator应用制作
liuian 2025-05-09 20:03 27 浏览
简介: operator 是一种 kubernetes 的扩展形式,可以帮助用户以 Kubernetes 的声明式 API 风格自定义来管理应用及服务,operator已经成为分布式应用在k8s集群部署的事实标准了,在云原生时代系统想迁移到k8s集群上编写operator应用是必不可少的能力,这里介绍用 CoreOS 的 operator framework 工具如何快速构建一个 operator 应用。
再说 operator 之前让我们先来了解下什么是 CRD (自定义资源描述,Custom Resource Definition)?
CRD
在 Kubernetes 中我们使用的 Deployment, DamenSet,StatefulSet, Service,Ingress, ConfigMap, Secret 这些都是资源,而对这些资源的创建、更新、删除的动作都会被称为为事件(Event),Kubernetes 的 Controller Manager 负责事件监听,并触发相应的动作来满足期望(Spec),这种方式也就是声明式,即用户只需要关心应用程序的最终状态。当我们在使用中发现现有的这些资源不能满足我们的需求的时候,Kubernetes 提供了自定义资源(Custom Resource)和 opertor 为应用程序提供基于 kuberntes 扩展。
CRD 则是对自定义资源的描述(Custom Resource Definition),也就是介绍这个资源有什么属性呀,这些属性的类型是什么,结构是怎样的这类。
我们看一个postgres-operator的CRD:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: postgresqls.acid.zalan.do
labels:
app.kubernetes.io/name: postgres-operator
annotations:
"helm.sh/hook": crd-install
spec:
group: acid.zalan.do
names:
kind: postgresql
listKind: postgresqlList
plural: postgresqls
singular: postgresql
shortNames:
- pg
additionalPrinterColumns:
- name: Team
type: string
description: Team responsible for Postgres CLuster
JSONPath: .spec.teamId
- name: Version
type: string
description: PostgreSQL version
JSONPath: .spec.postgresql.version
- name: Pods
type: integer
description: Number of Pods per Postgres cluster
JSONPath: .spec.numberOfInstances
- name: Volume
type: string
description: Size of the bound volume
JSONPath: .spec.volume.size
...
从上面的 CRD 文件可以看到 CRD 主要包括apiVersion、kind、metadata和spec四个部分。其中最关键的是apiVersion和kind,apiVersion表示资源所属组织和版本,apiVersion一般由APIGourp和Version组成,这里的 APIGourp 是apiextensions.k8s.io,Version 是v1beta1,相关信息可以通过kubectl api-resoures查看。kind 表示资源类型,这里是CustomResourceDefinition,表示是一个自定义的资源描述。
operator是什么
CoreOS 在 2016 年底提出了 Operator 的概念,当时的一段官方定义如下:
An Operator represents human operational knowledge in software, to reliably manage an application.
operator 是一种 kubernetes 的扩展形式,利用自定义资源对象(Custom Resource)来管理应用和组件,允许用户以 Kubernetes 的声明式 API 风格来管理应用及服务。operator 定义了一组在 Kubernetes 集群中打包和部署复杂业务应用的方法,operator主要是为解决特定应用或服务关于如何运行、部署及出现问题时如何处理提供的一种特定的自定义方式。比如:
- 按需部署应用服务(总不能用一大堆configmap来管理吧,也会很混乱~w(Д)w)
- 实现应用状态的备份和还原,完成版本升级,比如
- 数据库 schema 或额外的配置设置的改动
- 为分布式应用进行master选举,例如etcd,或者master-slave架构的mysql集群。
operator SDK
operator SDK —— operator framework,是 CoreOS 公司开发和维护的用于快速创建 operator 的工具,可以帮助我们快速构建 operator 应用,类似的工具还有:
- KUDO (Kubernetes 通用声明式 Operator)
- kubebuilder,kubernetes SIG 在维护的一个项目
- Metacontroller,可与 Webhook 结合使用,以实现自己的功能。
如果希望查看 Operator 生态,可以上 operatorhub.io ,也可以将自己创建的应用发布上去。
operator 安装
安装 operator sdk:
export RELEASE_VERSION=v0.13.0
curl -LO https://github.com/operator-framework/operator-sdk/releases/download/${RELEASE_VERSION}/operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu
chmod +x operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu && sudo mkdir -p /usr/local/bin/ && sudo cp operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu /usr/local/bin/operator-sdk && rm operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu
基于模板创建项目
用operator sdk 创建项目模板,这里用官方提供的一个sample-controller的模板:
operator-sdk new <controller-name> --repo github.com/kubernetes/sample-controller
项目结构目录创建完成,如下:
$ operator-sdk new test-controller --repo github.com/kubernetes/sample-controller
$ tree
.
├── build
│ ├── bin
│ │ ├── entrypoint
│ │ └── user_setup
│ └── Dockerfile
├── cmd
│ └── manager
│ └── main.go
├── deploy
│ ├── operator.yaml
│ ├── role_binding.yaml
│ ├── role.yaml
│ └── service_account.yaml
├── go.mod
├── go.sum
├── pkg
│ ├── apis
│ │ └── apis.go
│ └── controller
│ └── controller.go
├── tools.go
└── version
└── version.go
创建CRD
创建CRD:
operator-sdk add api --api-version=<api的版本> --kind=<类型名称>
创建CRD后,多出来了文件夹:
$ operator-sdk add api --api-version=test.k8s.realibox.com/v1 --kind=Realibox
INFO[0000] Generating api version test.k8s.realibox.com/v1 for kind Realibox.
INFO[0000] Created pkg/apis/test/group.go
INFO[0002] Created pkg/apis/test/v1/realibox_types.go
INFO[0002] Created pkg/apis/addtoscheme_test_v1.go
INFO[0002] Created pkg/apis/test/v1/register.go
INFO[0002] Created pkg/apis/test/v1/doc.go
INFO[0002] Created deploy/crds/test.k8s.realibox.com_v1_realibox_cr.yaml
INFO[0004] Created deploy/crds/test.k8s.realibox.com_realiboxes_crd.yaml
INFO[0004] Running deepcopy code-generation for Custom Resource group versions: [test:[v1], ]
INFO[0014] Code-generation complete.
INFO[0014] Running CRD generation for Custom Resource group versions: [test:[v1], ]
INFO[0014] Created deploy/crds/test.k8s.realibox.com_realiboxes_crd.yaml
INFO[0014] CRD generation complete.
INFO[0014] API generation complete.
$ tree
...
├── pkg
│ ├── apis
│ │ ├── addtoscheme_test_v1.go
│ │ ├── apis.go
│ │ └── test
│ │ ├── group.go
│ │ └── v1
│ │ ├── doc.go
│ │ ├── realibox_types.go
│ │ ├── register.go
│ │ └── zz_generated.deepcopy.go
│ └── controller
│ └── controller.go
...
test 文件夹下面放的就是 CRD,我们通过pkg/apis/test/v1/*_types.go文件定义我们的CRD结构,主要是Spec和Status:
vim pkg/apis/test/v1/realibox_types.go
...
// RealiboxSpec defines the desired state of Realibox
type RealiboxSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
}
type RealiboxStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
}
...
这里我们只改Spec字段,将RealiboxSpec结构体改为:
type RealiboxSpec struct {
Domain string `json:"domain,omitempty"`
OSS string `json:"oss,omitempty"`
Size string `json:"size,omitempty"`
}
更新CRD文件:
operator-sdk generate k8s
operator-sdk generate crds
CRD本质是一种k8s的资源,因此要使用crd,需要在K8s集群上创建CRD:
kubectl apply -f deploy/crds/test.k8s.realibox.com_realiboxes_crd.yaml
查看集群CRD:
$ kubectl get crd
NAME CREATED AT
clusterauthtokens.cluster.cattle.io 2020-08-29T06:41:42Z
clusteruserattributes.cluster.cattle.io 2020-08-29T06:41:42Z
realiboxes.test.k8s.realibox.com 2020-08-29T07:57:44Z
编写controller
创建好 CRD 后,我们可以编写 controller 了,先创建一个 controller 监听和核对新创建的realibox资源类型:
命令行说明:
operator-sdk add controller --api-version=<api的版本> --kind=<类型名称>
运行结果:
$ operator-sdk add controller --api-version=test.k8s.realibox.com/v1 --kind=Realibox
$ tree
...
├── pkg
│ ├── apis
│ │ ├── addtoscheme_test_v1.go
│ │ ├── apis.go
│ │ └── test
│ │ ├── group.go
│ │ └── v1
│ │ ├── doc.go
│ │ ├── realibox_types.go
│ │ ├── register.go
│ │ └── zz_generated.deepcopy.go
│ └── controller
│ ├── add_realibox.go
│ ├── controller.go
│ └── realibox
│ └── realibox_controller.go
...
在pkg/controller目录下生成了controller代码,在
pkg/controller/realibox/realibox_controller.go编写代码逻辑即可,在这里,我将CR信息在创建pod之前打印到日志里:
...
func (r *ReconcileRealibox) Reconcile(request reconcile.Request) (reconcile.Result, error) {
...
reqLogger.Info(fmt.Sprintf("Domain: %v created, oss info:%v, size: %v",instance.Spec.Domain,instance.Spec.OSS, instance.Spec.Size))
// Define a new Pod object
pod := newPodForCR(instance)
...
}
...
下面就可以运行 controller 了。
注:如果希望对集群进行更多的复杂操作,可以使用client-go来操作 Kubernetes 的资源,client-go是一个对 Kubernetes API 进行封装的库,由 Kubernetes 官方提供,还是十分好用的。
运行 controller
运行controller有两种方法,可以在本地直接运行controller,也可以打包到k8s运行。
本地运行controller
在本地运行controller直接go run就可以了:
export WATCH_NAMESPACE=default
go run cmd/manager/main.go
注意:不管是在本地运行还是远程运行都需要先在集群中创建CRD
运行好后我们可以编写一个CR资源,提交到k8s集群中:
apiVersion: test.k8s.realibox.com/v1
kind: Realibox
metadata:
name: example-realibox
spec:
domain: "realibox.com"
oss: "aliyun.com"
size: "3Gb"
通过kubectl提交到集群,可以看到controller程序已经收到CR注册内容打印出来了:
...
{"level":"info","ts":1598689291.273161,"logger":"controller_realibox","msg":"Domain: realibox.com created, oss info:aliyun.com, size: 3Gb","Request.Namespace":"default","Request.Name":"example-realibox"}
{"level":"info","ts":1598689291.2731829,"logger":"controller_realibox","msg":"Skip reconcile: Pod already exists","Request.Namespace":"default","Request.Name":"example-realibox","Pod.Namespace":"default","Pod.Name":"example-realibox-pod"}
...
打包提交到k8s运行
如果我们controller完成,我们可以将其打包放到k8s上运行:
打包镜像
这里用的阿里云镜像仓库:
operator-sdk build registry.cn-shenzhen.aliyuncs.com/shikanon/realibox-operator-test:v0.1 --image-builder docker
将打包好镜像上传镜像仓库:
docker login --username=shikanon@foxmail.com registry.cn-shenzhen.aliyuncs.com
docker push registry.cn-shenzhen.aliyuncs.com/shikanon/realibox-operator-test:v0.1
更改deploy/operator.yaml里面的镜像名称:
...
spec:
serviceAccountName: test-controller
containers:
- name: test-controller
# Replace this with the built image name
image: registry.cn-shenzhen.aliyuncs.com/shikanon/realibox-operator-test:v0.1
command:
- test-controller
imagePullPolicy: Always
...
将deploy下的YAML文件提交到集群中
kubectl apply -f deploy/service_account.yaml
kubectl apply -f deploy/role.yaml
kubectl apply -f deploy/role_binding.yaml
kubectl apply -f deploy/operator.yaml
查看状态,确保全部成功:
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/test-controller-75bf886d9c-whjdn 1/1 Running 0 23s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 123m
service/test-controller-metrics ClusterIP 10.43.100.251 <none> 8383/TCP,8686/TCP 12s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/test-controller 1/1 1 1 23s
NAME DESIRED CURRENT READY AGE
replicaset.apps/test-controller-75bf886d9c 1 1 1 23s
提交CR
这和单机运行是一样的,编写一个CR提交到集群,然后可以通过controller的pod日志查看到变化:
$ kubectl logs -f test-controller-75bf886d9c-whjdn
...
{"level":"info","ts":1598690827.2699623,"logger":"controller_realibox","msg":"Reconciling Realibox","Request.Namespace":"default","Request.Name":"example-realibox"}
{"level":"info","ts":1598690827.270006,"logger":"controller_realibox","msg":"Domain: realibox.com created, oss info:aliyun.com, size: 3Gb","Request.Namespace":"default","Request.Name":"example-realibox"}
{"level":"info","ts":1598690827.2700245,"logger":"controller_realibox","msg":"Skip reconcile: Pod already exists","Request.Namespace":"default","Request.Name":"example-realibox","Pod.Namespace":"default","Pod.Name":"example-realibox-pod"}
...
参考文献
- https://kubernetes.io/zh/docs/concepts/extend-kubernetes/operator/
- https://developer.aliyun.com/article/772224
- https://liqiang.io/post/kubernetes-all-about-crd-part06-kubebuilder-and-operator-sdk-d6e0858e
- https://www.shikanon.com/2020/%E8%BF%90%E7%BB%B4/%E5%9F%BA%E4%BA%8Eoperator-sdk%E6%A1%86%E6%9E%B6%E7%BC%96%E5%86%99CRD/
- https://coreos.com/blog/introducing-operators.html
- https://cloud.google.com/blog/products/containers-kubernetes/best-practices-for-building-kubernetes-operators-and-stateful-apps
- https://mp.weixin.qq.com/s/aXdbBJlUAQp6YPvZU3isSQ
相关推荐
- python入门到脱坑函数—定义函数_如何定义函数python
-
Python函数定义:从入门到精通一、函数的基本概念函数是组织好的、可重复使用的代码块,用于执行特定任务。在Python中,函数可以提高代码的模块性和重复利用率。二、定义函数的基本语法def函数名(...
- javascript函数的call、apply和bind的原理及作用详解
-
javascript函数的call、apply和bind本质是用来实现继承的,专业点说法就是改变函数体内部this的指向,当一个对象没有某个功能时,就可以用这3个来从有相关功能的对象里借用过来...
- JS中 call()、apply()、bind() 的用法
-
其实是一个很简单的东西,认真看十分钟就从一脸懵B到完全理解!先看明白下面:例1obj.objAge;//17obj.myFun()//小张年龄undefined例2shows(...
- Pandas每日函数学习之apply函数_apply函数python
-
apply函数是Pandas中的一个非常强大的工具,它允许你对DataFrame或Series中的数据应用一个函数,可以是自定义的函数,也可以是内置的函数。apply可以作用于DataF...
- Win10搜索不习惯 换个设定就好了_window10搜索用不了怎么办
-
Windows10的搜索功能是真的方便,这点用惯了Windows10的小伙伴应该都知道,不过它有个小问题,就是Windows10虽然会自动联网搜索,但默认使用微软自家的Bing搜索引擎和Edge...
- 面试秘籍:call、bind、apply的区别,面试官为什么总爱问这三位?
-
引言你有没有发现,每次JavaScript面试,面试官总爱问你call、bind和apply的区别?好像这三个方法成了通关密码,掌握了它们,就能顺利过关。其实不难理解,面试官问这些问题,不...
- 记住这8招,帮你掌握“追拍“摄影技法—摄影早自习第422日
-
杨海英同学提问:请问叶梓老师,我练习追拍时,总也不能把运动的人物拍清晰,速度一般掌握在1/40-1/60,请问您如何把追拍拍的清晰?这跟不同的运动形式有关系吗?请您给讲讲要点,谢谢您!摄影:Damia...
- [Sony] 有点残酷的测试A7RII PK FS7
-
都是好机!手中利器!主要是最近天天研究fs5,想知道fs5与a7rii后期匹配问题,苦等朋友的fs5月底到货,于是先拿手里现有的fs7小测一下,十九八九也能看到fs5的影子,另外也了解一下fs5k标配...
- AndroidStudio_Android使用OkHttp发起Http请求
-
这个okHttp的使用,其实网络上有很多的案例的,但是,如果以前没用过,copy别人的直接用的话,可以发现要么导包导不进来,要么,人家给的代码也不完整,这里自己整理一下.1.引入OkHttp的jar...
- ESL-通过事件控制FreeSWITCH_es事务控制
-
通过事件提供的最底层控制机制,允许我们有效地利用工具箱,适时选择使用其中的单个工具。FreeSWITCH是一个核心交换与混合矩阵,它周围有几十个模块提供各种功能特性。我们完全控制了所有的即时信息,这些...
- 【调试】perf和火焰图_perf生成火焰图
-
简介perf是linux上的性能分析工具,perf可以对event进行统计得到event的发生次数,或者对event进行采样,得到每次event发生时的相关数据(cpu、进程id、运行栈等),利用这些...
- 文本检索控件也玩安卓?dtSearch Engine发布Android测试版
-
dtSearchEngineforLinux(原生64-bit/32-bitC++和JavaAPIs)和dtSearchEngineforWin&.NET(原生64-bi...
- 网站后台莫名增加N个管理员,记一次SQL注入攻击
-
网站没流量,但却经常被SQL注入光顾。最近,网站真的很奇怪,网站后台不光莫名多了很多“管理员”,所有的Wordpres插件还会被自动暂停,导致一些插件支持的页面,如WooCommerce无法正常访问、...
- 多元回归树分析Multivariate Regression Trees,MRT
-
多元回归树(MultivariateRegressionTrees,MRT)是单元回归树的拓展,是一种对一系列连续型变量递归划分成多个类群的聚类方法,是在决策树(decision-trees)基础...
- JMETER性能测试_JMETER性能测试指标
-
jmeter为性能测试提供了一下特色:jmeter可以对测试静态资源(例如js、html等)以及动态资源(例如php、jsp、ajax等等)进行性能测试jmeter可以挖掘出系统最大能处...
- 一周热门
-
-
【验证码逆向专栏】vaptcha 手势验证码逆向分析
-
Python实现人事自动打卡,再也不会被批评
-
Psutil + Flask + Pyecharts + Bootstrap 开发动态可视化系统监控
-
一个解决支持HTML/CSS/JS网页转PDF(高质量)的终极解决方案
-
再见Swagger UI 国人开源了一款超好用的 API 文档生成框架,真香
-
网页转成pdf文件的经验分享 网页转成pdf文件的经验分享怎么弄
-
C++ std::vector 简介
-
飞牛OS入门安装遇到问题,如何解决?
-
系统C盘清理:微信PC端文件清理,扩大C盘可用空间步骤
-
10款高性能NAS丨双十一必看,轻松搞定虚拟机、Docker、软路由
-
- 最近发表
-
- python入门到脱坑函数—定义函数_如何定义函数python
- javascript函数的call、apply和bind的原理及作用详解
- JS中 call()、apply()、bind() 的用法
- Pandas每日函数学习之apply函数_apply函数python
- Win10搜索不习惯 换个设定就好了_window10搜索用不了怎么办
- 面试秘籍:call、bind、apply的区别,面试官为什么总爱问这三位?
- 记住这8招,帮你掌握“追拍“摄影技法—摄影早自习第422日
- [Sony] 有点残酷的测试A7RII PK FS7
- AndroidStudio_Android使用OkHttp发起Http请求
- ESL-通过事件控制FreeSWITCH_es事务控制
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)