← 中文资源

简体中文版本

按摘要而非标签锁定 Docker 基础镜像

把可变标签解析成不可变摘要并写进 Dockerfile,验证每次重建都从完全相同的字节开始。

阅读英文原文

目标

node:22-alpine 这样的标签是一个会移动的指针,上游随时可以在你睡觉时用新的包重发这个标签。按 digest 锁定则冻结了构建当时消费的确切镜像字节,让“没改一行代码的重建”从抽奖变成确定的空操作。本食谱完成标签到摘要的解析、写入与双重构建验证,并用定时任务把标签漂移变成显式告警。

步骤要点

  1. 用 docker manifest inspect 解析标签背后的当前摘要,得到形如 sha256:4f47c… 的不可变身份。
  2. 把摘要写进 Dockerfile 的 FROM 行,同时保留人类可读的标签与解析日期注释,让升级成为刻意行为。
  3. 连续构建两次并对比 RootFS.Layers 的哈希,序列一致即证明两次构建的基础镜像字节完全相同。
  4. 想升级基础镜像时,先解析新摘要并用独立构建参数在隔离环境验证,CI 通过后才移动锁定的摘要。
  5. 加一个定时 CI 任务对比锁定摘要与标签当前摘要,不一致就发 warning,把静默漂移变成可评审的 PR。

排错提示

锁定后构建报 manifest unknown,是多架构索引与单平台目标不匹配,用 jq 按平台匹配解析出对应架构摘要或在与构建目标一致的 runner 上拉取;manifest inspect 需要认证时先 docker login,只读 CI 场景可用 curl 取 registry token;团队觉得摘要难读,就让标签与摘要并存加日期注释,把执行交给 CI 漂移检查而不是肉眼比对 64 位十六进制。

适用边界

摘要锁定冻结的是已知好字节,也冻结了安全修复的到达,必须配合定时漂移检查让升级保持刻意;可复现性只覆盖基础层,你自己的层仍随包源浮动,锁文件同样要钉死。

代码示例

# Before: floating, breaks overnight
FROM node:22-alpine

# After: immutable, with provenance comment
# base: node:22-alpine resolved 2026-10-22
FROM node:22-alpine@sha256:4f47cbe7a1c2...

RUN npm ci --omit=dev
USER node
CMD ["node", "dist/server.js"]

# Verify twice-built layer identity
# docker inspect app:v1 --format "{{.RootFS.Layers}}" | sha256sum
# docker inspect app:v2 --format "{{.RootFS.Layers}}" | sha256sum
# -> identical