应直接调用 kubernetes rest api 的 rollback 方法,使用 clientset.appsv1().deployments(ns).rollback() 并传入正确的 *appsv1.rollbackconfig(含 name、revision、updatedannotations),通过 replicaset annotation 获取真实 revision,回滚后轮询 deployment status.conditions 确认就绪。

直接走 Kubernetes REST API 的 Rollback 方法,别碰 kubectl rollout undo 或手动改 Deployment.Spec.Image——前者不可审计,后者生成新 revision,根本不是回滚。
用 clientset.AppsV1().Deployments(ns).Rollback() 而不是 Update/Patch
Go 客户端的 Rollback() 函数专为回滚设计,它调用的是 /apis/apps/v1/namespaces/{ns}/deployments/{name}/rollback 这个 endpoint,底层做了 revision 校验和幂等性处理。常见错误是误用 Update() 去改 Image 字段,这会触发新 rollout,revision 号加一,历史版本被挤出(如果设置了 revisionHistoryLimit),导致你想回的版本已不可用。
-
Rollback()的第二个参数必须是*appsv1.RollbackConfig,不是*appsv1.Deployment - 传错结构体类型会导致 422 错误:
Invalid value: "xxx": rollbackConfig.name must match the name of the target resource -
RollbackConfig只需填三个字段:Name(Deployment 名)、Revision(int64 类型,不是字符串)、UpdatedAnnotations(一般留空或设为map[string]string{})
revision 编号得从 ReplicaSet annotation 里查,不能硬写或递增推算
Kubernetes 不提供“列出 deployment 所有 revision”的 API。每个历史 ReplicaSet 都带 deployment.kubernetes.io/revision annotation,且通过 ownerReferences 指向所属 Deployment。漏掉这步,就容易传入一个不存在的 Revision 值,导致回滚失败,或者误选已被 GC 清理掉的旧 RS。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 查询逻辑:先用
clientset.AppsV1().ReplicaSets(ns).List()拿到所有 RS,过滤ownerReferences匹配目标 Deployment 的那些 - 再遍历它们的
Annotations提取deployment.kubernetes.io/revision,转成int64 - 注意排序:annotation 值是字符串,需转换后按数值降序排,才能拿到“上一个可用 revision”
回滚后必须轮询 Deployment status.conditions,不能只信 HTTP 200
Rollback() 调用返回成功,只代表请求已发给 apiserver,并不表示 Pod 已切回、就绪探针已通过、流量已恢复。K8s 是异步 reconcile 的,你得自己等实际状态落地。
- 重点看
Deployment.Status.Conditions中类型为Progressing和Available的 condition -
Progressing.Status == "True"且Reason == "NewReplicaSetAvailable"表示 rollout 完成;Available.Status == "True"表示新(实为旧)RS 已就绪 - 轮询间隔建议 2–5 秒,超时设为 300 秒;避免用固定 sleep,应结合
time.AfterFunc或context.WithTimeout - 别只检查 HTTP
/healthz:它可能返回 200,但 Pod 还在 terminating,旧版本流量尚未完全接管
本地二进制部署场景下,用软链 + flock 实现原子切换
没上 K8s 时,回滚本质是文件系统级切换。靠 os.Remove("current") + os.Symlink("v1.2.2", "current") 看似简单,但并发部署时可能链接指向中间态(比如删了但还没建好),导致服务启动失败。
- 加文件锁:
flock -x /var/lock/myapp-deploy.lock(用os/exec调用)或 Go 原生syscall.Flock - 切换前验证目标目录存在且含可执行文件:
os.Stat(filepath.Join("releases", target, "app")) - 重启服务前,先
systemctl is-active --quiet myapp确认旧进程已停,避免端口冲突 - 所有路径(
/opt/myapp/releases/、/opt/myapp/current)必须绝对固定,配置文件路径也得相对current解析,否则回滚后配置错乱
真正难的不是写 rollback 代码,而是把 revision 来源、状态等待、锁机制、路径一致性这些点串成闭环——少一个,自动化回滚就变成半自动甩锅流程。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










