关于 docker-build-strategies
用于编写和审查 Dockerfile 的 Skill,涵盖多阶段构建、缓存、安全与镜像体积,并提供语言专项参考、示例文件和构建验证脚本。
主要功能
- 01多阶段构建模式
- 02BuildKit 层与缓存指导
- 03密钥和 SSH 挂载实践
- 04.dockerignore 与非 root 运行指导
- 05镜像体积及构建验证
适用场景
- 01为应用创建 Dockerfile
- 02审查或优化现有镜像构建
- 03添加 .dockerignore 并保护构建凭据
- 04检查镜像体积和运行用户配置
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ docker/ skills --skill docker-build-strategies安装后在 Claude Code 中输入/docker-build-strategies确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
skill.yamlagents/openai.yamlassets/Dockerfile.goDockerfile.nodejsDockerfile.pythondockerignore-examplechecks/verification.mdreferences/layer-caching.mdmulti-stage-builds.mdscripts/verify-build.sh详细介绍
Docker 构建策略
概述
本技能提供编写和审查生产级 Dockerfile 的规则和模式。适用于主要关注镜像构建质量的任务:多阶段构建、缓存行为、以非 root 用户运行、构建上下文清理以及运行时镜像大小。
何时使用此技能
在以下情况启用此技能:
- 为任何语言或框架创建新的 Dockerfile
- 优化现有 Dockerfile 的大小、速度或安全性
- 审查 Dockerfile 是否符合最佳实践
- 为项目添加
.dockerignore文件
何时不使用此技能
在以下情况不要使用此技能:
- 项目尚无 Docker 配置,主要需求是搭建初始脚手架
- 主要任务是在
compose.yaml中连接各项服务 - 主要任务是调试 Compose 启动顺序、网络或开发环境覆盖配置
核心指南
多阶段构建
当项目包含构建步骤,或构建时依赖与运行时依赖不同时,使用多阶段构建。将构建时依赖与运行时镜像分开。
- 为每个阶段显式命名(
FROM ... AS build、FROM ... AS runtime)。 - 为运行时阶段选择合适的最小基础镜像:
distroless、alpine或slim变体。 - 使用
COPY --from=build将最终构建产物复制到运行时阶段。 - 从前一阶段复制内容或添加静态文件时使用
COPY --link,以便让 COPY 不依赖之前的层,从而提高缓存复用率。
有关特定语言的模式(Go、Node、Python、Java),请参阅 references/multi-stage-builds.md。
层缓存
按照从最不常变化到最常变化的顺序排列 Dockerfile 指令。
- 在复制应用源代码之前,先放置依赖清单(
package.json、go.mod、requirements.txt)并执行安装步骤。使用绑定挂载将清单挂载到安装步骤中,而不是通过COPY复制,这样清单就不会进入镜像层:RUN --mount=type=bind,source=package.json,target=package.json --mount=type=bind,source=package-lock.json,target=package-lock.json npm ci。对于只读取清单的安装命令(npm ci、pip install -r、go mod download),这样做是安全的;如果某个步骤还需要将清单写回镜像,则改用COPY。 - 使用 BuildKit 缓存挂载来缓存包管理器的缓存:
- Go:
RUN --mount=type=cache,target=/go/pkg/mod go build ... - Node:
RUN --mount=type=cache,target=/root/.npm npm ci - Python:
RUN --mount=type=cache,target=/root/.cache/pip pip install ... - apt:
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=cache,target=/var/lib/apt,sharing=locked apt-get update && apt-get install -y ...— 无需执行rm -rf /var/lib/apt/lists/*,因为缓存位于镜像层之外。apt 需要独占访问其缓存目录,因此必须使用sharing=locked。 - apk(Alpine — 根据 Alpine wiki,此文档未经 Docker 验证;
references/layer-caching.md提供了源链接):RUN --mount=type=cache,target=/etc/apk/cache,sharing=locked apk add ...— 去掉--no-cache,这样下载的软件包会存入挂载的缓存目录,而不是被丢弃。
- Go:
- 将基础镜像标签固定到特定版本或摘要 — 生产环境中绝不要使用
latest。 - 使用
&&合并相关的RUN命令,以减少层数;但为保留缓存粒度,应将逻辑上不同的步骤分开。
有关缓存失效规则和缓存挂载模式的详细信息,请参阅 references/layer-caching.md。
构建密钥与 SSH 访问
切勿将凭据烘焙进镜像。使用 BuildKit 密钥和 SSH 挂载,使凭据仅在需要它们的特定 RUN 步骤期间可用,并且绝不持久保存在任何层或 docker history 输出中。
- 切勿通过
ARG或ENV传递凭据。它们最终都会进入镜像层,并且可通过docker history检查。 - 切勿将凭据文件
COPY到构建上下文中:.npmrc、.pypirc、.netrc、pip.conf、Mavensettings.xml、.env、云凭据(~/.aws/credentials、~/.config/gcloud/、服务账号 JSON 文件、~/.azure/)、密钥管理器令牌(~/.vault-token)、包注册表令牌(~/.cargo/credentials.toml)、TLS 密钥(*.pem、*.p12)、kubeconfig、SSH 密钥(id_rsa、id_dsa、id_ed25519、id_ecdsa)。即使最终阶段没有将它们复制过去,它们仍会留在中间层和构建缓存中。 - 切勿在
RUN命令中回显、写入或展开密钥值,以免将其持久保存到某个层中,或输出到构建日志。访问密钥文件(例如/run/secrets/<id>,或直接通过挂载的target=访问)——绝不要执行echo "$(cat /run/secrets/X)",也绝不要将其替换到会通过--progress=plain记录的 shell 参数中。 - 对包管理器注册表凭据,使用
RUN --mount=type=secret:
密钥仅在该RUN --mount=type=secret,id=npmrc,target=/root/.npmrc,required=false \ --mount=type=cache,target=/root/.npm \ npm ci --omit=devRUN中可用,绝不会写入某个层。若构建始终需要凭据(例如所有包都来自私有注册表,因此缺少密钥应立即导致构建失败),请使用required=true;仅当密钥为可选项(仅使用公共包也能成功构建)时,才使用required=false。 - 获取私有 Git 仓库或模块时,使用
RUN --mount=type=ssh。构建容器默认没有known_hosts——请在同一个RUN中填充该文件:
切勿为了省事使用RUN --mount=type=ssh \ mkdir -p -m 0700 /root/.ssh && \ ssh-keyscan github.com >> /root/.ssh/known_hosts && \ git clone [email protected]:org/private-repo.gitStrictHostKeyChecking=no——它会完全禁用主机密钥验证。每次步骤运行时,ssh-keyscan都会接受服务器提供的任何主机密钥;不同构建之间不会固定密钥。若要获得更强保障,请将其与服务提供商公布的主机密钥指纹进行比对,或将公布的密钥写入known_hosts,而不是进行扫描。 - 使用密钥和 SSH 源调用 buildx:
# --ssh default forwards this shell's SSH agent (SSH_AUTH_SOCK); list every key the build can use: ssh-add -l docker buildx build \ --secret id=npmrc,src=$HOME/.npmrc \ --ssh default \ .RUN --mount=type=ssh步骤可以使用ssh-add -l列出的每个密钥,因此只公开此构建所需的密钥。在交互式终端中,运行ssh-agent bash,启动一个使用专用 Agent 的 shell,然后运行ssh-add <key-file>,确认ssh-add -l只列出该密钥,并在此 shell 中运行构建。每条命令都会启动新 shell 的工具无法在命令之间保留该 Agent,因此请让用户运行这些步骤。或者,使用--ssh default=<key-file>直接传入未加密的密钥文件,例如专用部署密钥;BuildKit 会拒绝以这种方式传入受密码短语保护的密钥,因此应改为将此类密钥加载到 Agent 中。 .dockerignore中对.env和凭据文件的排除属于纵深防御,而非主要防护机制——请保留这些排除项,但不要将其作为唯一的保护措施。
请参阅 references/multi-stage-builds.md,了解各语言的模式(npm、pip、Maven、Go GOPRIVATE)。
.dockerignore
始终在 Dockerfile 旁生成一个 .dockerignore。排除:
.git/、.github/、.vscode/、.idea/node_modules/、__pycache__/、.venv/、vendor/(在构建阶段重新构建时)*.md、LICENSE、docs/- 构建输出、测试产物和 IDE 配置
.env文件及任何密钥
请参阅 assets/dockerignore-example,获取完整模板。
非 root 用户
始终将最终镜像配置为以非 root 用户运行。
- 在运行阶段创建专用用户和组:
RUN addgroup --system --gid 1001 appgroup && \ adduser --system --uid 1001 --ingroup appgroup appuser - 设置应用文件的所有权:
COPY --from=build --chown=appuser:appgroup /app /app - 将
--chown与COPY --link结合使用时,始终使用分配给用户和组的数字 UID:GID(例如,如果上文使用了--uid 1001 --gid 1001,则使用--chown=1001:1001),不要使用命名用户。--link会创建独立层,其中无法使用之前RUN指令创建的命名用户。 - 将
USER appuser指令放在所有文件操作之后、ENTRYPOINT/CMD之前。 - 对于 distroless 镜像,使用内置的非 root 用户:
USER nonroot:nonroot。
镜像体积优化
- 运行阶段优先使用
FROM scratch(Go 静态二进制文件)、distroless 或基于 Alpine 的镜像。 - 使用 BuildKit 缓存挂载安装 OS 软件包,而不要在同一层中通过
rm -rf删除缓存;请参阅上文的“层缓存”。缓存挂载会使软件包缓存完全不进入镜像层,因此无需清理。 - 不要在运行时镜像中安装文档、man 手册页或调试工具。
- 积极使用
.dockerignore,尽可能缩小构建上下文。
通用规则
- 始终将
# syntax=docker/dockerfile:1指令作为第一行,以启用 BuildKit 功能。 - 在任何
COPY或RUN指令之前设置WORKDIR,绝不要依赖默认的/。 - 优先使用 exec 格式的
ENTRYPOINT(["binary"]),而不是 shell 格式。 - 添加
EXPOSE以说明监听端口。 - 添加元数据标签:
LABEL org.opencontainers.image.source=...
相关技能
- 首次搭建 Docker 项目框架并确定要创建哪些文件时,请使用
docker-project-foundations。 - 涉及服务依赖、健康检查、覆盖配置、网络和卷模式时,请使用
docker-compose-patterns。 - 涉及具有破坏性的 Docker CLI 命令(
docker system prune、docker rm -f、镜像/网络/构建器清理),以及破坏性命令防护措施的跨产品索引时,请使用docker-destructive-guardrails。
参考资料
references/multi-stage-builds.md— Go、Node.js、Python 和 Java 的语言专属多阶段构建模式references/layer-caching.md— 深入介绍层排序、缓存失效和 BuildKit 缓存挂载
资源文件
assets/Dockerfile.go— 使用 distroless 运行时和非 root 用户的 Go 多阶段构建assets/Dockerfile.nodejs— 使用适当层缓存和非 root 用户的 Node.js 多阶段构建assets/Dockerfile.python— 使用虚拟环境、层排序和非 root 用户的 Python 构建assets/dockerignore-example— 完整的.dockerignore模板
脚本
scripts/verify-build.sh— 构建当前目录中的 Dockerfile,然后报告镜像大小和已配置的用户。从项目根目录(包含Dockerfile的目录)运行,并使用此技能目录下的脚本路径:
将bash "<skill-dir>/scripts/verify-build.sh" [--help] [IMAGE_NAME]<skill-dir>替换为包含此SKILL.md的文件夹的绝对路径;scripts/路径相对于该文件夹,而非项目目录。不要先切换到技能目录:脚本会构建当前目录中的内容。如果无法解析技能目录,请运行docker build -t verify-build-test .,然后运行docker images verify-build-test和docker inspect verify-build-test --format '{{.Config.User}}'。所有 Docker 命令均成功或请求了帮助时,退出状态为0;验证失败时,退出状态为失败的 Docker 命令所返回的非零状态;参数无效时,退出状态为2。
检查
checks/verification.md— 供人工审查使用的详细验证操作手册。
评论
支持 Markdown