Operator本身不自动备份数据库,而是通过CRD定义备份策略、协调Job执行mysqldump或go-restic等工具,并将状态写入Status字段,实现声明式、可追踪、可重试的K8s原生备份。

Operator 本身不自动备份数据库——它只负责“触发”和“协调”备份动作,真正执行备份的是你集成的工具(如 mysqldump、pg_dump 或云快照 API),而 Golang 的作用是把这件事变成声明式、可追踪、可重试的 Kubernetes 原生行为。
定义带备份策略的自定义资源(CRD)
不能让运维手动跑 mysqldump 脚本,得先让 Kubernetes “认识”什么是“要备份的数据库”。你需要一个 CRD,比如 DatabaseBackup,它不是描述备份结果,而是描述“谁、何时、怎么备”:
-
spec.databaseRef指向目标MySQLCluster或PersistentVolumeClaim,必须用ownerReference绑定生命周期,避免孤立备份 -
spec.schedule是 Cron 表达式(如"0 2 * * *"),但 Operator 不自己做定时调度——它监听这个字段变化,再创建对应的CronJob -
spec.storage指定输出位置:s3://my-bucket/backups/或pvc://backup-pv,注意路径末尾不能有斜杠,否则go-restic会报invalid repository URI - 不要在 CRD 中硬编码密码;用
spec.secretKeyRef引用Secret的 key,由控制器运行时注入
在 Reconcile 中生成并管理备份 Job
Operator 的 Reconcile 函数不是直接执行 mysqldump,而是确保集群中存在一个正确配置的 Job。常见错误是每次 Reconcile 都删掉旧 Job 重建,导致正在运行的备份被中断:
- 先用
r.Get(ctx, jobNamespacedName, &job)尝试获取已有 Job;如果存在且job.Status.Succeeded > 0,说明上次成功了,可以跳过 - 如果 Job 处于
Failed状态,检查job.Status.Failed是否 ≥ 3,超过则不再重试,改写status.conditions标记为BackupFailed - 生成 Job 时,
spec.template.spec.containers[0].command必须显式指定完整路径,例如["/bin/sh", "-c", "mysqldump -h $DB_HOST -u $DB_USER --password=$DB_PASSWORD myapp > /backup/$(date -u +%Y%m%dT%H%M%SZ).sql"],不能依赖$PATH - 挂载
Secret时,用envFrom: [{secretRef: {name: "db-creds"}}],别用env: [{name: DB_PASSWORD, valueFrom: {...}}]—— 后者在 Pod 日志里可能泄露明文
用 go-restic 实现加密、增量、对象存储直传
当底层存储不支持快照(比如 NFS 或本地 PV),或你要备份的是跨多个 PVC 的混合数据时,go-restic 比 shell 调用更可控,因为它能流式处理、避免落盘、支持内存加密:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 初始化 repo 必须在
initContainer中完成:restic init --repo s3:https://minio.example.com:9000/backups --password-file /tmp/password,否则主容器启动时 repo 不存在会 panic - 主容器中用
io.Pipe()连接tar -cf - /data和restic.Append(),避免生成临时.tar文件占满emptyDir空间 -
restic.Backup调用前,务必设置restic.SetPasswordFile("/tmp/password");如果密码来自 Secret 挂载,注意文件权限必须是0400,否则 restic 拒绝读取 - 不要在
Reconcile中调用restic.Forget清理旧快照——这会阻塞整个协调循环;应单独起一个低频CronJob来做 retention
备份状态必须写入 CR Status 字段,且不可省略
很多 Operator 开发者忽略 Status 子资源更新,导致 kubectl get databasebackup 看不到进度,也无法做条件等待(如 CI/CD 流水线等备份完成再部署):
- 每次 Reconcile 结束前,必须调用
r.Status().Update(ctx, &backup),且只更新status字段,不要碰spec -
status.lastRunTime和status.lastSuccessfulTime必须用metav1.Time{Time: time.Now()},不能用字符串时间戳,否则无法被kubectl wait解析 - 如果 Job 创建失败(如 RBAC 权限不足),
status.conditions应设为{type: "Ready", status: "False", reason: "JobCreateFailed", message: "no permissions to create jobs.batch"},而不是只打日志 - 切忌在
Reconcile中 sleep 等待 Job 完成——Kubernetes 期望控制器是事件驱动的,Job 成功后会触发新的Job事件,你的控制器应监听Job类型并反向更新DatabaseBackup状态
最易被忽略的一点:Operator 的 ServiceAccount 必须同时有 jobs.batch 的 create 权限,以及对目标 Secret 和 PersistentVolumeClaim 的 get 权限——缺任一都会静默失败,且错误日志只显示 "failed to get secret",不提示缺 RBAC。

















