在基于 Kubernetes 的 CI/CD 流程中,版本管理是非常重要的一环。
在实际搭建 HybridBlog 混合云 DevOps 平台过程中,我遇到了一个典型问题:
Git仓库中的文章已经删除,但是通过 Kubernetes 版本回滚后,线上网站中的文章又重新出现。
这个问题表面看起来像是 Kubernetes 回滚异常,实际上属于 源码版本与运行环境版本不一致问题。
本文记录问题产生原因、排查过程以及解决方案。
一、项目CI/CD流程
当前博客系统采用以下发布流程:
Gitea
|
| git push
↓
Jenkins
|
| docker build
↓
Harbor
|
| image push
↓
Kubernetes Deployment
|
↓
Hugo静态博客
具体过程:
- 开发者修改 Hugo Markdown 文件
- 提交代码到 Gitea
- Gitea Webhook 触发 Jenkins
- Jenkins 拉取代码并构建 Docker 镜像
- 镜像上传到 Harbor
- Jenkins 使用 kubectl 更新 Kubernetes Deployment
- 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
可以快速定位线上版本。