Kubernetes Operator开发实战:CRD自定义资源与控制器调谐模式构建

Kubernetes Operator将运维知识编码为控制器程序,通过CRD(Custom Resource Definition)扩展Kubernetes API,用自定义资源管理复杂有状态应用。Operator模式的核心是Reconcile循环:持续观察资源实际状态与期望状态的差异,执行操作使其趋于一致。本文使用Go语言和controller-runtime框架构建一个数据库备份Operator。

CRD自定义资源定义与API声明

使用kubebuilder脚手架初始化项目,创建BackupSchedule资源:

kubebuilder init --domain ops.io --repo github.com/example/backup-operator
kubebuilder create api --group ops --version v1alpha1 --kind BackupSchedule

编辑API类型定义,声明BackupSchedule的期望状态:

// api/v1alpha1/backupschedule_types.go
package v1alpha1

import (
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type BackupSpec struct {
    // 目标数据库连接信息
    DatabaseHost string `json:"databaseHost"`
    DatabasePort int    `json:"databasePort"`
    DatabaseName string `json:"databaseName"`
    DatabaseUser string `json:"databaseUser"`
    // S3存储配置
    S3Bucket string `json:"s3Bucket"`
    S3Prefix string `json:"s3Prefix"`
    // Cron表达式
    Schedule string `json:"schedule"`
    // 保留备份数量
    Retention int `json:"retention"`
}

type BackupStatus struct {
    LastBackupTime   *metav1.Time `json:"lastBackupTime,omitempty"`
    LastBackupStatus string       `json:"lastBackupStatus,omitempty"`
    NextBackupTime   *metav1.Time `json:"nextBackupTime,omitempty"`
}

type BackupSchedule struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`
    Spec   BackupSpec   `json:"spec,omitempty"`
    Status BackupStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
type BackupScheduleList struct {
    metav1.TypeMeta `json:",inline"`
    metav1.ListMeta `json:"metadata,omitempty"`
    Items           []BackupSchedule `json:"items"`
}

生成CRD清单并安装到集群:

make manifests
make install

Reconcile控制器调谐循环实现

Reconcile函数是Operator的核心逻辑,每次相关资源变更时触发。备份Operator的Reconcile逻辑:检查是否到达备份时间,创建Job执行备份,更新Status字段。

// controllers/backupschedule_controller.go
func (r *BackupScheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    log := log.FromContext(ctx)

    // 1. 获取BackupSchedule资源
    var bs opsv1alpha1.BackupSchedule
    if err := r.Get(ctx, req.NamespacedName, &bs); err != nil {
        return ctrl.Result{}, client.IgnoreNotFound(err)
    }

    // 2. 解析Cron表达式判断是否需要执行
    sched, err := cron.ParseStandard(bs.Spec.Schedule)
    if err != nil {
        log.Error(err, "invalid cron schedule")
        return ctrl.Result{}, err
    }

    now := time.Now()
    var lastTime time.Time
    if bs.Status.LastBackupTime != nil {
        lastTime = bs.Status.LastBackupTime.Time
    } else {
        lastTime = bs.CreationTimestamp.Time
    }

    nextTime := sched.Next(lastTime)
    if now.Before(nextTime) {
        // 未到执行时间,设置Requeue
        return ctrl.Result{RequeueAfter: nextTime.Sub(now)}, nil
    }

    // 3. 创建备份Job
    jobName := fmt.Sprintf("backup-%s-%d", bs.Name, now.Unix())
    job := r.buildBackupJob(&bs, jobName)
    if err := r.Create(ctx, job); err != nil && !errors.IsAlreadyExists(err) {
        log.Error(err, "failed to create backup job")
        return ctrl.Result{RequeueAfter: 30 * time.Second}, err
    }

    // 4. 更新Status
    bs.Status.LastBackupTime = &metav1.Time{Time: now}
    bs.Status.LastBackupStatus = "JobCreated"
    bs.Status.NextBackupTime = &metav1.Time{Time: sched.Next(now)}
    if err := r.Status().Update(ctx, &bs); err != nil {
        return ctrl.Result{Requeue: true}, err
    }

    return ctrl.Result{RequeueAfter: sched.Next(now).Sub(now)}, nil
}

Reconcile函数返回ctrl.Result的RequeueAfter字段控制下次触发时间。该值应基于Cron计算的下次执行时间,而非固定间隔,避免时间漂移。

构建备份Job与资源管理逻辑

备份Job构建函数封装容器模板,使用mysqldump和aws cli完成数据库导出和S3上传:

func (r *BackupScheduleReconciler) buildBackupJob(bs *opsv1alpha1.BackupSchedule, name string) *batchv1.Job {
    return &batchv1.Job{
        ObjectMeta: metav1.ObjectMeta{
            Name:      name,
            Namespace: bs.Namespace,
        },
        Spec: batchv1.JobSpec{
            TTLSecondsAfterFinished: ptr.To(int32(3600)),
            Template: corev1.PodTemplateSpec{
                Spec: corev1.PodSpec{
                    RestartPolicy: corev1.RestartPolicyOnFailure,
                    Containers: []corev1.Container{{
                        Name:  "backup",
                        Image: "mysql-client:8.0",
                        Command: []string{"/bin/sh", "-c"},
                        Args: []string{fmt.Sprintf(
                            "mysqldump -h%s -P%d -u%s -p$MYSQL_PASS %s | gzip | aws s3 cp - s3://%s/%s/%s.sql.gz",
                            bs.Spec.DatabaseHost, bs.Spec.DatabasePort,
                            bs.Spec.DatabaseUser, bs.Spec.DatabaseName,
                            bs.Spec.S3Bucket, bs.Spec.S3Prefix, name,
                        )},
                        Env: []corev1.EnvVar{
                            {Name: "MYSQL_PASS", ValueFrom: &corev1.EnvVarSource{
                                SecretKeyRef: &corev1.SecretKeySelector{
                                    LocalObjectReference: corev1.LocalObjectReference{Name: "db-secret"},
                                    Key:                  "password",
                                },
                            }},
                        },
                    }},
                },
            },
        },
    }
}

TTLSecondsAfterFinished设为3600秒,Job完成后1小时自动清理。通过SetControllerReference建立OwnerReference,BackupSchedule删除时级联清理关联Job。

测试部署与线上运维注意事项

本地测试使用envtest启动临时控制平面:

make test

部署到集群前,构建镜像并推送:

make docker-build docker-push IMG=registry.example.com/backup-operator:v0.1.0
make deploy IMG=registry.example.com/backup-operator:v0.1.0

创建BackupSchedule实例验证:

apiVersion: ops.io/v1alpha1
kind: BackupSchedule
metadata:
  name: mysql-daily-backup
  namespace: default
spec:
  databaseHost: mysql.default.svc.cluster.local
  databasePort: 3306
  databaseName: myapp
  databaseUser: root
  s3Bucket: my-backups
  s3Prefix: mysql
  schedule: "0 2 * * *"
  retention: 7

线上运维需关注三点:Reconcile幂等性保证——同一资源多次调谐不应产生副作用,通过Job名称包含时间戳避免重复创建;Finalizer处理——资源删除时需等待关联资源清理完成;Status子资源更新冲突——并发更新Status可能产生OptimisticLockError,重新Get后重试即可。监控方面,controller-runtime暴露的metrics(controller_runtime_reconcile_total、controller_runtime_reconcile_errors_total)应接入Prometheus,对错误率突增做告警。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/kubernetesoperator-kai-fa-shi-zhan-crd-zi-ding-yi-zi-yuan/

(0)
小编小编
上一篇 3小时前
下一篇 3小时前

相关推荐