K8S Controller 使用详解

2023-03-28 container k8s

简介

在构建自定义的 Operator 时会涉及到如下几种组件:

  • client-go 底层用于与 K8S API 进行交互,支持资源的 CRUD 操作,包含了 ClientSet、DynamicClient、RESTClient 三种。
  • controller-runtime 封装了控制器的处理框架,底层会调用 client-go 库。
  • kubebuilder 可以很方便渲染出 Controller 整个框架,该框架使用的就是 controller-runtime 模块。
  • operator-sdk 同样基于 controller-runtime 实现,而且同时使用了 kubebuilder 来构建 Go 项目。

简单来说,当前 Controller 的编写包含了 OperatorSDK、Kubebuilder、ClientGo 三种方式,前两者提供了更加实用的封装,而 ClientGo 相对来说要简单很多,这里简单介绍 Kubebuilder 的使用方式。

Kubebuilder

Kubebuilder 由 K8S Special Interest Group, SIG API Machinery 拥有和维护,用于帮助开发者创建 CRD 并生成 Controller 脚手架,包含了如下的组件:

  • Client 封装了对资源的操作,修改会直接访问 APIServer ,而查询则会访问本地 Cache 信息。
  • Cache 负责生成 SharedInformer,会监听 GVK 下的 GVR 变化,然后触发 Controller 的 Reconcile 逻辑。
  • Manager 管理协调多个 Controller,提供共有依赖以及基础服务(例如保活),负责初始化 Controller、Cache、Client 的工作。
  • Finzlizers 用于处理资源的预删除逻辑,保障资源被删除后能够从 Cache 中读取到,清理相关的其它资源。
  • Builder 构造器,提供了一系列配置接口,可以通过链式条用进行组装,最终为 Reconciler 生成相应的 Controller 对象。

示例

可以直接从 GitHub Release 上下载对应的二进制文件。

----- 切换到默认的工作目录
$ cd $GOPATH/src/example.io/hello
----- 初始化项目,会同时下载依赖
$ go mod init example.io/hello
$ kubebuilder init --domain example.io

----- 创建API,会自动生成资源、Controller的定义
$ kubebuilder create api --group batch --version v1 --kind Hello
$ ls api/v1/hello_types.go
$ ls internal/controller/hello_controller.go

----- 安装CRD资源
$ make install
$ kubectl get crd | grep example.io
$ kubectl api-versions | grep example.io
$ kubectl api-resources --namespaced=true | grep example.io

----- 直接本地运行,下面介绍如何部署到K8S中
$ make run

----- 启动CustomResource,初始化项目时已经生成了示例
$ kubectl apply -f config/samples/batch_v1_hello.yaml
$ kubectl get hello
$ kubectl get hellos.batch.example.io hello-sample
$ kubectl delete hellos.batch.example.io hello-sample

也可以将 Controller 以 Deployment 的方式部署到 K8S 集群上。

----- 构建镜像并Push到DockerHub上,然后部署到K8S集群
make docker-build docker-push IMG=YourRepo/kubebuilder-example:latest
make deploy IMG=YourRepo/kubebuilder-example:latest
----- 在初始化项目时会自动创建一个Namespace配置
$ kubectl get deployment -n kubebuilder-example-system
$ kubectl get pod -n kubebuilder-example-system

增加业务逻辑

这里简单通过 Spec 将信息传递给资源,然后打印与 CRD 相关的信息,修改 api/v1/hello_types.go 文件。

type HelloSpec struct {
    Message string `json:"message,omitempty"`
}
type HelloStatus struct {
    Status string `json:"status"`
}

以及 internal/controller/hello_controller.go 文件,关键是 Reconcile 函数。

func (r *HelloReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    logger := log.FromContext(ctx)

    obj := &batchv1.Hello{}
    if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
        logger.Info("Unable to fetch object", "error", err)
    } else {
        logger.Info("Geeting from Kubebuilder", "message", obj.Spec.Message)
    }
    obj.Status.Status = "Running"
    if err := r.Status().Update(ctx, obj); err != nil {
        logger.Info("Unable to update status", "error", err)
    }
    return ctrl.Result{}, nil
}

修改完后类似如上的方式编译运行。

----- 本地运行
$ make run
----- 修改CR文件,增加参数
$ vim config/samples/batch_v1_hello.yaml
apiVersion: batch.example.io/v1
kind: Hello
metadata:
  name: hello-sample
spec:
  message: HelloWorld
----- 应用到K8S
$ kubectl apply -f config/samples/batch_v1_hello.yaml

其它

健康检查

内部集成了健康检查功能,可以通过 Manager.AddHealthzCheck()Manager.AddReadyzCheck() 注册监控检查逻辑,其端口和路径可以在如下的 Options 中配置,然后通过 Manager.Start() 启动时会同时包含一个 HTTP Server 提供监控检查的返回结果。

type Options struct {
	// HealthProbeBindAddress is the TCP address that the controller should bind to
	// for serving health probes
	HealthProbeBindAddress string

	// Readiness probe endpoint name, defaults to "readyz"
	ReadinessEndpointName string

	// Liveness probe endpoint name, defaults to "healthz"
	LivenessEndpointName string
}

监控指标

pkg/metricspkg/internal/controller/metrics 中内置了很多模块的监控指标,同样可以在 Options.MetricsBindAddress 中配置,包括通过 pkg/metrics 中的 Registry 自定义监控指标。

参考