先看现象。发布出问题后把 Deployment 镜像从 app:1.9.2 改回 app:1.9.1,滚动完成后接口返回的版本号仍是 1.9.2,docker inspect 显示两个 tag 指向同一个 digest。

原因不止一个,得分开看。构建流水线复用了 tag,1.9.1 被重新推送覆盖。还有一种是imagePullPolicy 为 IfNotPresent,节点上缓存了同 tag 镜像。

还有一种情况,回滚只改 tag 没触发滚动,Pod 仍是旧模板。

另外,仓库开了不可变 tag 但推送时被强制覆盖。

先比对两个 tag 的 digest 是否相同

docker manifest inspect repo/app:1.9.1 | grep digest && docker manifest inspect repo/app:1.9.2 | grep digest

再往下查看 Pod 实际使用的镜像 ID

kubectl get pod -o jsonpath='{.items[*].status.containerStatuses[*].imageID}'

然后改为按 digest 部署并强制拉取

kubectl set image deploy/app app=repo/app@sha256:abc123 && kubectl rollout restart deploy/app

等前面都确认过,再在流水线里加 digest 记录与校验

docker buildx imagetools inspect repo/app:1.9.1 --format '{{json .Manifest.Digest}}'

同事提醒我看一下最近一次发布,时间点确实对得上。

命令输出我都留了记录,免得改完记不清原来的值。

恢复的判定其实很直接。回滚后 Pod 的 imageID 与目标 digest 一致,接口版本号返回 1.9.1,节点上不再残留同 tag 的新镜像。

tag 是可变的,digest 才是身份,回滚要按 digest 部署,并在流水线里禁止同 tag 重复推送。这类问题最好一次改到位,反复小改反而容易把原因搅混。

作者 9823189