3. 镜像版本管理:版本号策略、标签管理、元数据存储设计
镜像版本管理这事儿,说白了就是给每个镜像一个“身份证”。
我见过太多团队,镜像名就叫 app:latest,结果上线时根本不知道跑的是哪天的代码。嗯,这里要重点聊聊怎么避免这种混乱。
3.1 版本号策略:别再用 latest 了
我个人习惯用语义化版本号,格式是 主版本.次版本.修订号。比如 v2.1.3。
- 主版本:不兼容的 API 修改。比如从 v1 升到 v2,旧接口全废了。
- 次版本:向下兼容的功能新增。比如加了新接口,旧的不动。
- 修订号:向下兼容的问题修正。比如修了个 bug,功能没变。
我在项目中遇到过一个问题:开发同学随手打了个 v2.1.3,但代码根本没合入主干。结果回滚时发现这个版本根本不存在。所以,我建议版本号必须和 Git 标签一一对应。
核心原则:每个镜像版本,都能追溯到唯一的 Git commit。
举个例子,我们的 CI 流程会这样打标签:
# 从 Git 标签生成镜像版本
GIT_TAG=$(git describe --tags --abbrev=0)
IMAGE_TAG="${GIT_TAG}-build-${BUILD_NUMBER}"
docker build -t myapp:${IMAGE_TAG} .
这样每个镜像版本都是唯一的,不会出现覆盖。
3.2 标签管理:给镜像贴个“便利贴”
标签不只是版本号。我习惯用多维度标签来管理镜像。
| 标签类型 | 示例 | 用途 |
|---|---|---|
| 版本标签 | v2.1.3 |
精确版本,用于回滚 |
| 环境标签 | prod, staging |
标识部署环境 |
| 构建标签 | build-1234 |
关联 CI 构建编号 |
| 时间标签 | 2024-01-15 |
按日期检索 |
你想想看,如果只用一个标签,回滚时你根本不知道哪个版本是稳定的。我曾经因为只用了 latest,回滚时拉了个刚构建的测试镜像,线上直接挂了半小时。
我的习惯:每个镜像至少打三个标签——版本号、环境名、构建时间。这样无论从哪个维度查,都能快速定位。
3.3 元数据存储设计:镜像的“档案袋”
光有标签还不够。镜像的元数据才是回滚时的救命稻草。
元数据应该存什么?我建议至少包含以下字段:
- 镜像 ID:SHA256 摘要,唯一标识
- 创建时间:精确到秒
- Git commit:对应的代码版本
- 构建参数:环境变量、基础镜像版本
- 测试结果:单元测试、集成测试是否通过
- 部署记录:在哪些环境部署过,结果如何
我个人喜欢用 JSON 格式存储元数据,方便程序解析。比如:
{
"image_id": "sha256:abc123...",
"created_at": "2024-01-15T10:30:00Z",
"git_commit": "a1b2c3d4",
"build_number": 1234,
"base_image": "alpine:3.18",
"tests_passed": true,
"deployments": [
{"env": "staging", "status": "success"},
{"env": "prod", "status": "pending"}
]
}
这些元数据存在哪里?我建议用专门的元数据服务,比如 Harbor 或 OCI Artifacts。别只依赖 Docker Registry 的标签,因为标签可以被覆盖。
避坑指南:我曾经把元数据存在 Git 仓库里,结果镜像删了,元数据还在,回滚时根本找不到对应的镜像。后来我改用 Registry 自带的注解功能,镜像和元数据绑定在一起,删镜像时元数据也一起删,干净利落。
3.4 核心逻辑:版本回滚的“决策树”
下面这张图,是我自己总结的镜像回滚决策流程。说白了,就是回答三个问题:
- 目标版本是否存在?
- 元数据是否完整?
- 回滚是否安全?
这张图的核心逻辑是:版本不存在或元数据不完整,坚决不自动回滚。我吃过这个亏,有一次元数据丢了,回滚后才发现镜像里有个安全漏洞,又得重新回滚一次。
3.5 实战建议:从小处着手
如果你现在还在用 latest,别慌。我建议分三步走:
- 先加版本号:从今天开始,每个构建都打上语义化版本号。
- 再补元数据:用 Registry 的注解功能,把 Git commit 和构建参数写进去。
- 最后自动化:把版本管理和元数据写入 CI 流程,让机器替人干活。
一句话总结:版本号是镜像的“名字”,标签是“外号”,元数据是“档案”。三者缺一不可,才能做到回滚时心中有数。