Kubernetes 开发【4】—— 基于kubebuilder开发简单的Operator-灵析社区

没晒干的咸鱼

开发背景

operator模式 in K8S

Kubernetes 为自动化而生。无需任何修改,你即可以从 Kubernetes 核心中获得许多内置的自动化功能。 你可以使用 Kubernetes 自动化部署和运行工作负载, 甚至 可以自动化 Kubernetes 自身。

Kubernetes 的 Operator 模式概念允许你在不修改 Kubernetes 自身代码的情况下, 通过为一个或多个自定义资源关联控制器来扩展集群的能力。 Operator 是 Kubernetes API 的客户端, 充当自定义资源的控制器。


operator 的 作用及其构成

使用 Operator 可以自动化的事情包括:

.按需部署应用

.获取/还原应用状态的备份

.处理应用代码的升级以及相关改动。例如,数据库 schema 或额外的配置设置

.发布一个 service,要求不支持 Kubernetes API 的应用也能发现它

模拟整个或部分集群中的故障以测试其稳定性

在没有内部成员选举程序的情况下,为分布式应用选择首领角色

以上释义来自官方文档;我以组件化的表达方式去描述 operator,它大概由三部分组成,包括了:

CRD

webhook

controller

crd 是指我们定义了什么样的一种资源,定义它需要什么字段(schema),这些字段的值描述了我们对于这个资源的预期(终态);

webhook 是指我们在描述这个资源时的校验规则(显式),以及它必须包含什么字段(隐式);

controller 里实现了list&watch自定义资源,去不断触发 Reconsile 函数来响应,这个过程叫做调谐循环(Reconsile Loop)。

在本文中,我们将使用 kubebuilder 脚手架 撸一个简单的 redisCluster 出来。


operator 实现

简易流程

创建一个 New Project 并创建 go.mod。

初始化工程。

kubebuilder init --domain xxx.com

创建api,其中定义了 CR 的 GVK。

kubebuilder create api --group myapp --version v1 --kind Redis 

生成了如下的目录结构:

├── Dockerfile
├── Makefile
├── PROJECT
├── README.md
├── api
├── bin
├── config
├── controllers
├── go.mod
├── go.sum
├── hack
└── main.go

首先到 /api/v1/redis_types.go 文件下对 CRD 的 api 添加字段,也就是我们最后 apply 的 yaml 中包含的字段。

// RedisSpec defines the desired state of Redis
type RedisSpec struct {
	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
	// Important: Run "make" to regenerate code after modifying this file
 
	// Foo is an example field of Redis. Edit redis_types.go to remove/update
	//Foo string `json:"foo,omitempty"`
 
	// +kubebuilder:validation:Maximum:=40000
	// +kubebuilder:validation:Minimum:=80
	Port int `json:"port,omitempty"` //我们添加了一个Port字段
}

添加了一个 Port 字段,最终我们会创建一个 ContainerPort 为其值的 redis pod,其中,我们可以通过添加注释的方式去限制Port字段的范围为 80~40000,还可以通过正则表达式来校验,参考文档:

CRD Validation - The Kubebuilder Book

在 /config/samples/myapp_v1_redis.yaml 中,我们可以添加刚才的字段以及属性值,以应用测试用。

apiVersion: myapp.xxx.com/v1
kind: Redis
metadata:
  name: myredis
spec:
  port: 1011

至此,我们完成了对 CRD 的基本定义,然后执行

make install

安装 crd 到集群里,这里其实是通过 kubectl 和 kustomize 去 apply 的,所以会安装 CRD 到我本机的~/.kube/config中配置的集群里去。

接下里就要实现 controller ,包括其中最核心的 Reconcile 调谐函数,目录在 /controller/redis_controller.go。

func (r *RedisReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	_ = log.FromContext(ctx)
 
	// TODO(user): your logic here
	redis := &myappv1.Redis{}
	if err := r.Get(ctx, req.NamespacedName, redis); err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("得到对象", redis)
		// TODO client-go创建pod
	}
	return ctrl.Result{}, nil
}

以上代码实现了一段最简单的控制器逻辑,当然我们也可以继续扩充,使用 client-go 以及获取到的 redis 对象来创建对应的 redis pod。

编写完成后,我们可以通过执行 make run 本地启动 controller,并通过执行 kubectl apply redis.yaml 来测试控制器。

当测试完成后,我们需要实际应用 controller 到我们的集群中去时,执行

make docker-build docker-push IMG=registryurl

推送镜像,然后执行

make deploy IMG=registryurl

将 controller 部署到集群中去。

至此,我们就完成了最简单的 operator 开发。

关键功能(持续更新)

参考 deployment 和 pod 之前的关系,在实际的应用过程中,我们可能还要实现以下功能:

状态显示

事件支持

watch 子资源

集联删除

webhook校验yaml字段有效性

副本伸缩处理

这部分后续将缓慢更新。

状态显示

指的是 kubectl get 我们的 crd 时,命令行返回结果的字段展示;以及 kubectl describe 对应资源时yaml里面的字段

找到 redis CRD 对象的 schema,其位于 /api/v1/redis_types.go

// Redis is the Schema for the redis API
type Redis struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
 
	Spec   RedisSpec   `json:"spec,omitempty"`
	Status RedisStatus `json:"status,omitempty"`
}

其中已经为我们定义好了 type struct RedisStatus ,这里面的字段都是用于展示资源列表中资源的状态字段,默认为空。

我们可以在结构体内添加我们预期的状态字段:

// RedisStatus defines the observed state of Redis
type RedisStatus struct {
	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
	// Important: Run "make" to regenerate code after modifying this file
    RedisNum int `json:"num"`
}

当 Reconcile 函数中触发了副本的伸缩,num 字段需要更新时,我们可以做如下处理:


redis.Status.RedisNum = 预期值
err := r.Status().Update(ctx, redis)

至此,我们可以通过 kubectl describe 对应资源获得其中的 num 字段以及值。

另外,我们需要将这个字段在 kubectl get 的返回结果也展示出来,通过在 Redis 结构体上添加注释即可:

//+kubebuilder:printcolumn:name="Num",type=integer,JSONPath=`.status.num`
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

// Redis is the Schema for the redis API
type Redis struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`
 
	Spec   RedisSpec   `json:"spec,omitempty"`
	Status RedisStatus `json:"status,omitempty"`
}

具体的添加规则以及范例参考,可以转到:

Generating CRDs - The Kubebuilder Book


阅读量:2089

点赞量:0

收藏量:0