简介
本文基于operator-sdk,实现一个简单的operator,一方面学习和了解operator,另一方面通过该operator实现一个简单的功能:对于符合名称前缀的一系列命名空间,约束某些命名空间的deployment replica为0,仅允许白名单里的namespace有pod运行。
实现该自动化的动机是:在工作中由于集群资源有限,且开发中的项目版本众多,每个版本都在各自的命名空间运行,大量的pod可能未在使用。在交付发布前,需要优先保证待发布版本的命名空间能够有足够的资源运行pod,避免因为各种resource不足导致的调度失败。在没有该operator时,需要每次发布前挑选版本,然后逐个运行kubectl scale命令将命名空间内的deploy全部缩为0。
本文仓库地址:lwabish/namespace-scaler-operator: 按照参数配置,把在管理范围内且不在白名单内的namespace下的deployment全部scale为0 (github.com)
准备工作
-
安装operator-sdk:Installation | Operator SDK (operatorframework.io)
-
初始化项目:
operator-sdk init --domain wubw.fun --repo github.com/lwabish/namespace-scaler-operator
-
创建指定GVK(Group Version Kind)的crd和控制器,本文的cr kind为NSScaler:
operator-sdk create api --group operators --version v1alpha1 --kind NSScaler --resource --controller --namespaced=false
。- --namespaced为true则对应的cr是区分命名空间的。
- operator监视资源和命名空间的相关问题:https://sdk.operatorframework.io/docs/building-operators/golang/operator-scope/
- crd的namespaced问题:CRD Scope | Operator SDK (operatorframework.io)
-
修改cr的spec结构体
api/v1alpha1/nsscaler_types.go
,按需增加字段,然后make generate
用代码生成器生成深拷贝之类的代码。 -
生成crd yaml:
make manifests
控制器实现
controllers/nsscaler_controller.go
为控制器,需要我们实现协调循环函数Reconcile
触发
- 首先,我们的cr在创建、更新、删除时,控制器显然是需要调用Reconcile函数进行状态协调的。
- 其次,如果operator会创建附属于operator的子资源,那么这些子资源的状态发生变更时也应该调用Reconcile函数进行协调。如果是这种情况需要参考文档增加子资源类型。
父子关系
如果operator会创建子资源,那么需要在创建后标记出父子关系:controllerutil.SetControllerReference()
增删改查
控制器结构体组合了一个专门为控制器准备的k8s client,可以用该client从cache中高效地增删改查集群对象,作为完成逻辑的基础。
// 基本逻辑先创建实例,然后把指针传给r.Get/List/Update等方法,最后获取结果
func (r *NSScalerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
...
// 查
instance := &operatorsv1alpha1.NSScaler{}
err := r.Get(ctx, req.NamespacedName, instance)
// 批量查
nsList := &coreV1.NamespaceList{}
err = r.List(ctx, nsList, &client.ListOptions{})
// 改
err = r.Update(ctx, &deployment, &client.UpdateOptions{})
}
状态维护
instance.Status.Done = noPodObserved
err = r.Status().Update(context.TODO(), instance)
幂等性
幂等性十分关键。确保每次调用协调循环完成的任务都是独立的,不依赖于其他操作的状态。
协调结果
// 有错
return ctrl.Result{}, err
// 没错,需要再次协调
return ctrl.Result{Requeue: true}, nil
// 正常结束
return ctrl.Result{}, nil
// 延时再次协调
return ctrl.Result{RequeueAfter: nextRun.Sub(r.Now())}, nil
本地调试
-
make install
/make uninstall
安装卸载crd到kubeconfig中的集群。 -
make run
启动控制器或使用idea/goland等debug main.go 启动控制器。 -
kubectl apply -f config/samples/xxx.yaml
部署cr,观察控制器被触发后的效果。
集群部署
- 修改makefile中的
IMAGE_TAG_BASE
和IMG ?= $(IMAGE_TAG_BASE):$(VERSION)
, 并设置合适的版本号version
- 国内网络即使宿主机可以连接k8s.io,在多阶段构建时容器里也无法直连k8s.io下载依赖。因此需要设置go proxy。Dockerfile中增加
ENV GOPROXY="https://goproxy.cn,direct"
- build、push镜像:
make docker-build docker-push
- 准备rbac相关的权限yaml。偷懒可直接在
config/rbac/role_binding.yaml
中把sa关联cluster-admin。 make deploy
集群卸载
make undeploy
参考
Go Operator Tutorial | Operator SDK (operatorframework.io)