很多团队在 Docker 早期都会先用一个标签:latest。它上手快,但只要开始进入多人协作、回滚、排障,问题就会立刻暴露出来。真正麻烦的不是标签写法本身,而是你已经无法准确回答“现在生产环境跑的到底是哪一个版本”。

latest 最大的问题是不够明确

如果你这样构建和发布:

docker build -t registry.example.com/blog-api:latest .
docker push registry.example.com/blog-api:latest

短期看没有问题,但后面会出现几种很常见的混乱:

  • 本地和服务器上的 latest 其实不是同一个镜像
  • 同事重新推了一次 latest,你却不知道内容变了什么
  • 服务出了问题,但你没法准确回滚到上一个稳定版本
  • 日志里只看得到 latest,排障时缺少版本锚点

latest 适合做一个默认别名,不适合做发布系统里的唯一标签。

最小可用的标签模型

一个对中小项目足够实用的做法是同时保留两类标签:

  • 一个可读版本号,例如日期加发布次数
  • 一个不可歧义的 Git SHA

例如:

docker build -t registry.example.com/blog-api:2026-04-09-1 .
docker tag registry.example.com/blog-api:2026-04-09-1 registry.example.com/blog-api:git-a1b2c3d
docker push registry.example.com/blog-api:2026-04-09-1
docker push registry.example.com/blog-api:git-a1b2c3d

这样做的好处很直接:

  • 人能看懂这是不是今天的发布
  • 机器能精确定位到具体提交
  • 回滚时可以明确指定旧标签

如果你还想保留 latest,也应该把它当成附加标签,而不是唯一真相。

发布时部署具体标签,不部署模糊标签

镜像打出来之后,部署阶段最重要的原则是:运行环境使用明确版本。

例如在 docker compose.yml 里:

services:
  blog:
    image: registry.example.com/blog-api:2026-04-09-1
    restart: unless-stopped

这比写成 latest 稳定得多,因为你能一眼看出线上到底跑的是哪次发布。

后面就算要升级,也应该是把配置里的标签从旧版本改到新版本,而不是让服务器自己去猜“最新”指的是什么。

镜像标签和发布顺序要一起设计

只改标签命名还不够,交付动作也要固定下来。一个常见且足够稳定的顺序可以是:

  1. 本地验证功能
  2. 构建镜像
  3. 打版本标签和 Git SHA 标签
  4. 推送到镜像仓库
  5. 在目标环境拉取明确版本
  6. 重启服务并检查日志

示例命令:

docker build -t registry.example.com/blog-api:2026-04-09-1 .
docker tag registry.example.com/blog-api:2026-04-09-1 registry.example.com/blog-api:git-a1b2c3d
docker push registry.example.com/blog-api:2026-04-09-1
docker push registry.example.com/blog-api:git-a1b2c3d
docker compose pull
docker compose up -d
docker compose logs --tail=100

重点不是命令多高级,而是这条链路每次都能复用。

回滚能力和发布能力一样重要

如果你的标签是明确的,回滚就会简单很多:

docker compose down
# 修改 compose 中的 image 标签为旧版本
docker compose up -d

如果你只有 latest,回滚就会变成一件很痛苦的事。因为你既不知道旧版本是什么,也不知道仓库里的 latest 有没有再次被覆盖。

对工程交付来说,能不能快速回退,和能不能快速发布同样关键。

常见误区

  • 以为镜像仓库里只有一个服务,所以 latest 就够用
  • 只在构建时打标签,部署配置里仍然写 latest
  • 发布后只看 docker ps,不核对镜像标签和日志
  • 没有把 Git 提交和镜像版本建立对应关系

这些问题在单人项目早期不明显,但只要开始持续发布,就会不断放大。

留下一个更稳的发布习惯

镜像标签不是装饰,它是交付链路里的定位器。只要你把“版本号标签 + Git SHA 标签 + 明确部署标签”这套最小模型建立起来,Docker 发布就会从“能跑一次”变成“能持续交付、能追踪、能回滚”的工程流程。后面不管接 GitHub Actions 还是更完整的 CI/CD,这个基础都不会浪费。