在基于 Kubernetes 的 CI/CD 流程中,版本管理是非常重要的一环。

在实际搭建 HybridBlog 混合云 DevOps 平台过程中,我遇到了一个典型问题:

Git仓库中的文章已经删除,但是通过 Kubernetes 版本回滚后,线上网站中的文章又重新出现。

这个问题表面看起来像是 Kubernetes 回滚异常,实际上属于 源码版本与运行环境版本不一致问题

本文记录问题产生原因、排查过程以及解决方案。


一、项目CI/CD流程

当前博客系统采用以下发布流程:

Gitea
 |
 | git push
 ↓
 Jenkins
 |
 | docker build
 ↓
 Harbor
 |
 | image push
 ↓
 Kubernetes Deployment
 |
 ↓
 Hugo静态博客

具体过程:

  1. 开发者修改 Hugo Markdown 文件
  2. 提交代码到 Gitea
  3. Gitea Webhook 触发 Jenkins
  4. Jenkins 拉取代码并构建 Docker 镜像
  5. 镜像上传到 Harbor
  6. Jenkins 使用 kubectl 更新 Kubernetes Deployment
  7. Pod 使用新镜像提供网站服务

正常情况下:

Git代码

Docker镜像

Kubernetes运行版本

三者保持一致。


二、问题复现过程

1. 初始版本

Git仓库:

content/posts/

├── 1_Docker.md

├── 2_Kubernetes.md

└── 3_Harbor.md

提交代码:

commit A

Jenkins构建:

hybridblog:30

Kubernetes运行:

Pod

image:
 hybridblog:30

此时:

Git
 |
 |
 镜像
 |
 |
 Pod

版本一致

2. 删除文章并重新发布

随后删除文章:

bash

rm content/posts/3_Harbor.md

提交:

git add .

git commit -m "delete Harbor article"

git push

触发CI/CD。

新的构建:

commit B

↓

hybridblog:31

↓

Kubernetes更新

此时:

Git:

没有3_Harbor.md

线上:

没有3_Harbor.md

状态正常。


3. 执行Kubernetes回滚后出现问题

模拟紧急情况下为了恢复旧版本:

执行:

kubectl rollout undo deployment hybridblog -n hybridblog

Kubernetes执行:

hybridblog:31

↓

hybridblog:30

Pod重新运行旧镜像。

此时:

Git:

commit B

文章已经删除

但是 Kubernetes:

hybridblog:30

文章仍然存在

最终:

Gitea:

没有文章


Harbor:

保存旧镜像


Kubernetes:

运行旧镜像


网站:

文章重新出现

产生了版本漂移。


三、问题本质

这个问题不是 Kubernetes 故障,而是:

源码版本与运行版本漂移

也叫:

  • Deployment Drift
  • Release Drift
  • Git与运行环境状态不一致

原因:

Kubernetes 回滚的是:

Deployment历史版本

而不是:

Git提交记录

例如:

Deployment记录:

Revision 1

image:
hybridblog:30


Revision 2

image:
hybridblog:31

执行:

kubectl rollout undo

实际上只是:

31 → 30

Kubernetes并不会:

访问Gitea

恢复commit A

所以:

Kubernetes状态
≠
Git仓库状态

四、如何解决

通过Git恢复代码(推荐)

对于 Hugo 静态博客,文章源文件来自 Git。

因此恢复文章应该操作 Git。

查看历史:

git log --oneline

找到目标版本:

abc123 old version

恢复文件:

git checkout abc123 -- content/posts/3_Harbor.md

重新提交:

git add .

git commit -m "restore article"

git push

随后:

Git

↓

Jenkins

↓

Harbor

↓

Kubernetes

重新生成一致版本。


镜像版本管理优化,不使用latest标签

不推荐:

hybridblog:latest

原因:

latest会不断覆盖:

latest

↓

新代码

↓

旧代码无法追踪

应该使用唯一版本:

例如:

hybridblog:30

hybridblog:31

hybridblog:32

当前项目采用:

IMAGE_TAG="${BUILD_NUMBER}"

通过 Jenkins 构建编号生成镜像版本。

例如:

Jenkins Build:

41


Harbor:

hybridblog:41


Kubernetes:

hybridblog:41

可以快速定位线上版本。