Contents

kube-operator

kube-operator开发示例

使用 Kubebuilder 开发 Kubernetes Operator 是目前 Go 语言生态中最主流、最高效的方式。它是 Kubernetes 官方推荐的 SDK,基于 controller-runtimecontroller-tools 构建,能自动生成 boilerplate 代码,让专注于业务逻辑。

1、核心概念

  1. CRD (Custom Resource Definition): 定义自定义资源(如 MyApp),扩展 Kubernetes API。
  2. Controller (控制器): 一个运行在集群内的循环进程,不断对比 期望状态 (Spec)实际状态 (Status),并执行操作使两者一致(Reconcile)。
  3. Operator: CRD + Controller 的组合,用于自动化运维复杂应用。

使用kubebuilder开发

1、初始化项目

# 创建项目目录
mkdir crab-op && cd crab-op

#初始化
kubebuilder init --domain=web.imau.cc  --repo=github.com/serialt/crab-operator  --owner=serialt 

#创建api
kubebuilder create api --group webapp --version v1 --kind Crab
 
# 下载依赖
go mod tidy

2、自定义CRD

// api/v1/crab_types.go
type GuestbookSpec struct {
	Replicas int32  `json:"replicas"` // 对应 YAML 的 `spec.replicas`
	Image    string `json:"image"`    // 对应 YAML 的 `spec.image`
	Port     int32  `json:"port"`     // 应用监听的端口
	SvcPort  int32  `json:"svcPort"`  // Service 暴露的端口
}

type GuestbookStatus struct {
    // +listType=map
	// +listMapKey=type
	// +optional
	Conditions        []metav1.Condition `json:"conditions,omitempty"`
	AvailableReplicas int32  
}

3、实现业务逻辑

import (
	"context"

	webappv1 "github.com/serialt/crab-operator/api/v1"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/intstr"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
	logf "sigs.k8s.io/controller-runtime/pkg/log"
)

// +kubebuilder:rbac:groups=webapp.web.imau.cc,resources=crabs,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=webapp.web.imau.cc,resources=crabs/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=webapp.web.imau.cc,resources=crabs/finalizers,verbs=update
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete


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

	crab := webappv1.Crab{}
	if err := r.Get(ctx, req.NamespacedName, &crab); err != nil {
		if apierrors.IsNotFound(err) {
			// 资源已被物理删除,无需处理
			return ctrl.Result{}, nil
		}
		return ctrl.Result{}, err
	}

	labels := map[string]string{
		"app": crab.Name,
	}

	deploy := &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      crab.Name,
			Namespace: crab.Namespace,
			Labels:    labels,
		}}
	if err := controllerutil.SetControllerReference(&crab, deploy, r.Scheme); err != nil {
		return ctrl.Result{}, err
	}

	result, err := controllerutil.CreateOrUpdate(ctx, r.Client, deploy, func() error {
		deploy.Spec = appsv1.DeploymentSpec{
			Replicas: &crab.Spec.Replicas,
			Selector: &metav1.LabelSelector{
				MatchLabels: labels,
			},
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: labels,
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:  crab.Name,
							Image: crab.Spec.Image,
							Ports: []corev1.ContainerPort{
								{
									ContainerPort: crab.Spec.Port,
								},
							},
						},
					},
				},
			},
		}

		// 关键操作:设置 OwnerReference
		// 确保删除 Guestbook CR 时,自动清理关联的 Deployment
		if err := controllerutil.SetControllerReference(&crab, deploy, r.Scheme); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		logger.Error(err, "Failed to create or update Deployment")
		crab.Status.AvailableReplicas = 0
		return ctrl.Result{}, err
	}
	logger.Info("Deployment操作结果", "result", result)

	// 构建svc
	svc := &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      crab.Name,
			Namespace: crab.Namespace,
		},
	}
	result, err = controllerutil.CreateOrUpdate(ctx, r.Client, svc, func() error {
		svc.Spec = corev1.ServiceSpec{
			Selector: labels,
			Ports: []corev1.ServicePort{
				{
					Port:       crab.Spec.SvcPort,
					TargetPort: intstr.FromInt(int(crab.Spec.Port)),
				},
			},
		}
		return controllerutil.SetControllerReference(&crab, svc, r.Scheme)
	})
	if err != nil {
		logger.Error(err, "Failed to create or update Service")
		return ctrl.Result{}, err
	}
	logger.Info("Service操作结果", "result", result)

	return ctrl.Result{}, nil
}



func (r *CrabReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&webappv1.Crab{}).
		Owns(&appsv1.Deployment{}).
		Owns(&corev1.Service{}).
		Complete(r)
}

4、部署本地测试

# 生成CRD YAML
[serialt@Krab web-op]🐳 make manifests

# 部署本地集群
[serialt@Krab web-op]🐳 make install
[serialt@Krab web-op]🐳 make run



# 测试的yaml
# config/samples/webapp_v1_crab.yaml
apiVersion: webapp.web.imau.cc/v1
kind: Crab
metadata:
  labels:
    app.kubernetes.io/name: crab-op
    app.kubernetes.io/managed-by: kustomize
  name: crab-app
  namespace: dev
spec:
  # TODO(user): Add fields here
  replicas: 3
  image: registry.cn-hangzhou.aliyuncs.com/serialt/nginx:1.28-alpine
  port: 80
  svcPort: 8080 

  
# 执行后可以看到3个pod
[serialt@Krab web-op]🐳 kubectl apply -f config/samples/webapp_v1_crab.yaml 

生产/集成测试

将控制器打包成镜像部署到集群。

1、构建并推送镜像:

# 设置镜像仓库
# export IMG=registry.cn-hangzhou.aliyuncs.com/serialt/crab-op:v0.0.1 

# 当前系统与节点系统相同
make docker-build docker-push IMG=registry.cn-hangzhou.aliyuncs.com/serialt/crab-op:v0.0.1 


# 构建多架构镜像
make docker-buildx PLATFORMS=linux/amd64 IMG=registry.cn-hangzhou.aliyuncs.com/serialt/crab-op:v0.0.1 

2、部署到集群:

make deploy IMG=registry.cn-hangzhou.aliyuncs.com/serialt/crab-op:v0.0.1 


# 修改replicas
kubectl replace -f config/samples/webapp_v1_crab.yaml