生产镜像应写成 仓库:版本@sha256:摘要。版本 tag 让人看懂计划部署哪个版本,digest 让 Docker 拉取确定的内容;仅使用 latest、stable 或可移动版本 tag,无法保证重建和回滚得到原来的镜像。
tag 是名称,digest 是内容身份
Docker 的镜像摘要说明(在新标签页打开)把 digest 定义为镜像内容的 SHA-256 标识。仓库维护者可以把同一个 tag 重新指向修复后的镜像,原 digest 不会随 tag 移动。
registry.example.com/app:1.8
registry.example.com/app:1.8@sha256:<64-character-digest>
第一行只指定 tag,拉取时会取得当时 tag 指向的内容。第二行同时指定 tag 和 digest,仓库必须返回该 digest;如果旧内容已经被删除,拉取会明确失败,不会静默换成新镜像。
固定 digest 后:
- 同一提交在测试、预发布和生产使用同一制品;
- 主机重建或扩容时不会拉到 tag 的新内容;
- 回滚记录能准确指出旧版本,而不是依赖一个可能移动的名称。
发布者身份、构建来源和已知漏洞仍需单独核验。digest 要与镜像签名、SLSA provenance、SBOM 和漏洞扫描结果共同使用。
多架构镜像可能有两层摘要
一个同时支持 amd64 和 arm64 的镜像,仓库顶层通常是 OCI image index 或 Docker manifest list。顶层摘要对应平台索引,每个平台 manifest 又有自己的摘要。
docker buildx imagetools inspect registry.example.com/app:1.8
输出中的顶层 Digest 适合写入需要跨架构部署的 Compose 配置。展开 Manifests 后,可以看到 linux/amd64、linux/arm64 等平台摘要。Docker 拉取顶层索引时,会根据目标节点的平台选择其中一个 manifest;顶层摘要和平台摘要因此不能混着记录。
固定顶层索引摘要,能够保证平台集合和每个平台指向的 manifest 不变;amd64 和 arm64 节点仍会运行各自平台的二进制层。以 OneDev 16.4.2 的多架构索引为例,只允许 amd64 时可以同时声明平台:
services:
app:
image: 1dev/server:16.4.2@sha256:ddb7e414e2e3038ab522ab4e517a95a5580cac0177ef5ee171fe37e4f209a34b
platform: linux/amd64
部署记录至少保存完整镜像引用、目标平台和运行节点架构。排查“相同配置但行为不同”时,先确认两个节点是否选择了不同平台 manifest。
在升级前解析并记录摘要
先拉取候选 tag,再查看仓库为本地镜像保存的引用:
image="registry.example.com/app:1.8"
docker pull "$image"
docker image inspect "$image" \
--format '{{range .RepoDigests}}{{println .}}{{end}}'
RepoDigests 使用 仓库@摘要 格式,同一个本地镜像可能因为推送、拉取或重新标记而列出多个仓库限定引用。应选择仓库名与候选引用一致的一项,再用 docker buildx imagetools inspect 核对它是顶层索引还是平台 manifest;不要默认使用输出第一行。
私有仓库需要先完成只读登录。CI 中不要从普通命令日志输出仓库密码;使用平台 secret 或标准输入传给 docker login --password-stdin。
把审核后的引用写入环境文件或部署配置:
APP_IMAGE=1dev/server:16.4.2@sha256:ddb7e414e2e3038ab522ab4e517a95a5580cac0177ef5ee171fe37e4f209a34b
PREVIOUS_APP_IMAGE=1dev/server:16.3.4@sha256:c6e8192063e7edd26b4bcde30407215d46e8d3b5583701c0d8249a4a2a897582
PREVIOUS_APP_IMAGE 不应靠发布时临时猜测。每次成功发布后,把实际运行引用写入受控发布记录;下一次发布开始前,该记录就是明确的回滚目标。
构建一次,让测试和生产使用同一制品
测试通过的镜像不要在生产重新构建。同一个 digest 按以下顺序进入各环境:
- CI 为源代码提交构建镜像;
- 扫描镜像并生成 SBOM、签名或来源证明;
- 把候选镜像的 digest 写入发布记录;
- 测试环境使用该 digest;
- 测试通过后,生产继续使用同一个 digest;
- 发布结果记录提交、镜像引用、配置版本和时间。
Docker 构建最佳实践(在新标签页打开)同样建议按 digest 固定基础镜像,以避免上游 tag 在未审查时改变构建输入。基础镜像也需要更新,因此依赖更新工具应提出新 digest,再由正常测试和评审决定是否采用。SLSA 构建来源规范(在新标签页打开)进一步把制品与构建者、输入和构建过程关联起来;这部分证据不能由 digest 代替。
从测试环境重新按同一 Dockerfile 构建“生产版”,即使代码提交相同,也可能因为基础镜像、系统包仓库或构建参数变化产生不同内容。这类流程无法证明生产运行的是已经测试的制品。
部署后核对配置引用和本地镜像
Compose 启动后先查看容器保存的镜像引用:
container_id="$(docker compose ps -q app)"
docker inspect "$container_id" \
--format 'configured={{.Config.Image}} image_id={{.Image}}'
configured 应包含计划中的 tag@digest。image_id 是本地镜像配置对象的 ID,不能直接代替仓库 manifest 或索引 digest。继续查看 RepoDigests:
image_id="$(docker inspect "$container_id" --format '{{.Image}}')"
docker image inspect "$image_id" \
--format '{{range .RepoDigests}}{{println .}}{{end}}'
输出可能包含多个仓库限定引用。核对与部署仓库同名的一项,同时保留 Compose 中声明的索引或平台摘要;多架构部署还要记录节点平台,不能要求本地 image ID、顶层索引摘要和平台 manifest 摘要三者相同。
部署记录应同时保存计划使用的镜像引用、Compose 展开后的 image、容器 Config.Image、本地镜像的 RepoDigest,以及服务健康状态和公开请求返回的版本标识。这些值共同证明配置、容器与对外服务指向同一次发布。
只看 docker compose ps 的 running,无法证明容器使用了计划镜像。只看应用版本字符串,也无法区分同版本号下的不同重建。
更新 digest 也要走完整发布流程
固定摘要后,docker compose pull 不会自动越过 digest 获取新内容。升级应提交一个可审计的引用变更:
-APP_IMAGE=1dev/server:16.3.4@sha256:c6e8192063e7edd26b4bcde30407215d46e8d3b5583701c0d8249a4a2a897582
+APP_IMAGE=1dev/server:16.4.2@sha256:ddb7e414e2e3038ab522ab4e517a95a5580cac0177ef5ee171fe37e4f209a34b
发布前确认目标摘要存在且平台匹配:
docker buildx imagetools inspect \
1dev/server:16.4.2@sha256:ddb7e414e2e3038ab522ab4e517a95a5580cac0177ef5ee171fe37e4f209a34b
docker compose --env-file .env config --quiet
docker compose --env-file .env pull
候选容器通过健康检查后再切流量。公开验证成功才更新“当前版本”记录;验证失败则恢复旧引用和旧入口。镜像回滚只有在数据库和外部状态仍与旧应用兼容时才安全,迁移顺序见数据库迁移应该在发布哪一步执行。
定期检查上游版本和安全状态
固定摘要后,上游更新不会自行进入生产。版本巡检需要同时查看新的安全发布、当前 digest 的漏洞扫描变化,以及签名、来源证明和 SBOM 是否仍然完整。镜像仓库若会按保留策略删除旧 manifest,还要从生产网络重新拉取回滚 digest,确认旧制品没有失效。
高危漏洞出现时,应生成并审核新摘要,经过最小必要测试后发布。直接把可变 tag 留在生产中,让主机在任意重启时自动取得更新,会把安全更新和未经验证的运行变更混在一起。
常见问题
tag 和 digest 有什么区别?
tag 是可读且可能移动的仓库引用;digest 是内容寻址摘要。生产配置同时保留二者,兼顾识别和确定性。
固定 digest 后还能自动收到更新吗?
不能,也不应该静默更新。更新任务发现新版本后,提交新摘要并完成测试、发布和回滚准备。
多架构镜像固定哪个摘要?
跨架构部署固定 OCI 索引摘要,节点再选择匹配平台的 manifest;单一架构也可以直接固定平台 manifest。发布记录还要保存声明摘要、目标平台和运行节点架构。
digest 能证明镜像安全吗?
不能。它只能证明拉取内容与指定内容一致。发布者身份、构建来源和漏洞状态需要签名、provenance、SBOM 与扫描结果。
只固定版本 tag 能可靠回滚吗?
不能假设 tag 永远不变。保留旧 digest、配置和数据库兼容边界,才有确定的回滚目标。