关于 docker-pro
docker-pro 是面向创建、审查和现代化 Docker 构建与运行文件的技能,供处理容器化项目的开发者使用。它围绕 BuildKit 提供安全、可维护的 Dockerfile、忽略文件和构建配置指导。
主要功能
- 01检查仓库后再修改配置
- 02采用 BuildKit 缓存与密钥挂载
- 03建议加固基础镜像和多阶段构建
- 04固定外部镜像摘要
- 05创建 .dockerignore 与 Bake 配置
- 06规范 Compose 本地开发配置
适用场景
- 01为应用新建 Dockerfile
- 02审查并现代化现有构建文件
- 03减少构建上下文中的敏感文件
- 04整理可复现的本地及 CI 构建
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ bretfisher/ skills --skill docker-pro安装后在 Claude Code 中输入/docker-pro确认已加载。
详细介绍
Modern Docker 和 BuildKit 规则
创建、编辑、审查或现代化任何 Docker 构建或运行文件时,请使用此技能:Dockerfile、.dockerignore、docker-bake.hcl 和 compose.yaml。除非仓库中已有明确冲突的惯例,或用户明确要求采用不同的权衡,否则应坚定地坚持这些默认做法并主动应用。
这些规则假定使用 BuildKit 作为构建器(现代 Docker Engine 和 docker buildx 中的默认构建器)。正是这一假设,让我们可以使用以下所有现代前端功能,而无需针对旧版构建器添加兼容处理。
工作方式
- 编写前先检查仓库。识别语言/运行时、包管理器、锁定文件、构建输出目录以及任何现有 Docker 文件,确保输出符合实际情况,而不是套用通用模板。
- 优先采用正确且改动最小的方案,但如果正在编辑相关文件,就不要保留旧式模式(例如在 compose 中使用
version:、将密钥烘焙进层、使用不带缓存挂载的apt-get)。 - 不要把基础镜像标签、包名、端口或构建命令臆造为事实。如果仓库没有提供某个值,请使用标注明确的占位符并说明这一点。尤其是镜像标签可能会变化;如果不确定某个标签是否存在,请告知用户应对照镜像仓库目录确认,而不要直接断言。
- 完成下方的交接检查清单,让用户能够维护每项改动。
Dockerfile 布局
如果把人们最常修改的内容(版本和配置)放在顶部可预测的位置,并让构建逻辑从上到下排列,Dockerfile 就更易于维护。请遵循以下顺序:
# syntax=docker/dockerfile:1 # 1. frontend directive — MUST be the very first line
ARG NODE_VERSION=22 # 2. global version ARGs — one place to bump bases
ARG ALPINE_VERSION=3.22
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS deps # 3. all FROM stages, each named AS
FROM deps AS build
FROM cgr.dev/chainguard/node:latest AS runtime
# ... stage bodies follow, in build order ...
1. 前端指令放在第一行
每个 Dockerfile 都应以以下内容开头:
# syntax=docker/dockerfile:1
这会在构建时从 Docker Hub 获取最新的稳定 1.x Dockerfile 前端,从而使 Dockerfile 功能与引擎版本解耦——即使是较旧的 Docker Engine 也能理解最新语法。固定为 :1(而非 :1.0 或完整版本号)是有意为之:v1 系列保证不会有破坏性变更,因此可以免费获得新功能和修复。只有当用户明确需要前端本身逐字节可复现时,才固定到次要版本(例如 :1.7)。
它必须是第一行——位于注释和 ARG 之前。如果它不是第一行,BuildKit 就会忽略它,并悄然退回内置前端。
2. 在全局 ARG 中集中管理版本
在全局作用域(第一个 FROM 之前)将基础镜像版本声明为 ARG,让所有版本都集中在顶部的一个代码块中:
ARG NODE_VERSION=22
ARG ALPINE_VERSION=3.22
全局 ARG 在 FROM 行中有效。若要在阶段内部(例如在 RUN 中)使用它,还需在该阶段内重新声明一个不带默认值的 ARG NODE_VERSION——全局参数不会自动传入阶段主体。这样做的好处是:升级版本只需在顶部修改一行,而且可以在构建时通过 --build-arg NODE_VERSION=20 覆盖任何值,无需修改文件。
3. 将所有 FROM 阶段放在顶部,并为每个阶段命名
将每个构建阶段的 FROM 放在文件顶部附近,并使用明确的 AS <name>,然后再写较长的阶段主体。把 FROM 行放在一起,能让镜像关系图一目了然,也能将所有基础镜像引用集中在一处,与上面的版本 ARG 相配合。命名阶段还能提供稳定的 COPY --from=<name> 目标,并允许用户使用 --target <name> 构建单个阶段。
4. 在阶段顶部附近声明 ENV
将某个阶段的 ENV 声明集中放在该阶段顶部附近,不要散落在各个 RUN 之间。环境变量是人们最常查找的配置;将它们放在一起更便于查找,也能让缓存失效行为更可预测(ENV 变更会使其下方的所有内容失效)。
5. 使用 BuildKit 前端功能
由于这里假定使用 BuildKit,请优先采用以下功能,而不是旧式替代方案——它们速度更快、更安全,或两者兼具:
# Cache mounts: persist package-manager caches across builds (don't bloat layers)
RUN --mount=type=cache,target=/root/.npm npm ci
# Secret mounts: secrets are available during RUN but never written to a layer
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
# Bind mounts: read context/another stage during RUN without COPYing it in
RUN --mount=type=bind,source=package.json,target=package.json npm ci
# COPY --link: layer is independent of its parent chain → better cache reuse
COPY --link ./dist /app/dist
# Heredocs: multi-line RUN without trailing backslashes
RUN <<EOF
set -eux
apt-get update
apt-get install -y --no-install-recommends curl
rm -rf /var/lib/apt/lists/*
EOF
# ADD --checksum: verify a remote artifact's integrity
ADD --checksum=sha256:24454f8...d68d https://example.com/app.tar.gz /opt/
基础镜像:默认采用加固版本
默认使用精简且经过加固的基础镜像。它们包含的程序包少得多,因此攻击面更小、需要修复的 CVE 更少,镜像也更小。只有当应用确实需要在运行时使用额外工具,且加固版本无法满足需求时,才选择功能齐全的通用基础镜像(Debian/Ubuntu/官方 alpine);并在这样做时说明原因。
两个免费且推荐的来源:
- Chainguard Images —
cgr.dev/chainguard/<name>。公共目录中的镜像无需身份验证即可拉取,因此是门槛最低的默认选择。大多数镜像有两种版本::latest-dev— 包含 shell 和apk程序包管理器。在需要安装软件的构建阶段使用。:latest(以及static)— distroless:没有 shell,也没有程序包管理器。在最终运行阶段使用。
- Docker Hardened Images (DHI) —
dhi.io/<name>。免费,但拉取前需要使用 Docker 账户执行一次docker login dhi.io。标签格式如dhi.io/python:3.13或dhi.io/python:3.12-alpine3.22,另有添加了 shell 和程序包管理器、供构建阶段使用的-dev版本。如果用户已经在使用 Docker 生态系统,或需要 Docker 签名的 SBOM/VEX 供应链元数据,优先选择 DHI。
多阶段构建模式让加固镜像更实用:在 -dev 构建阶段完成繁杂工作(编译器、安装程序包),然后只将构建产物通过 COPY --from 复制到没有 shell 或程序包管理器、因而无法被利用的 distroless 最终阶段。
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.24
FROM cgr.dev/chainguard/go:latest-dev AS build
FROM cgr.dev/chainguard/static:latest AS runtime
FROM build AS compile
WORKDIR /src
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
CGO_ENABLED=0 go build -o /app ./cmd
FROM runtime
COPY --from=compile /app /app
ENTRYPOINT ["/app"]
distroless 最终阶段没有 shell,因此 ENTRYPOINT/CMD 必须使用 exec 格式(["/app"],而不是 /app),并且 RUN/apt/apk 在那里都无法使用——这正是设计目的。由于各镜像的具体标签会有所不同且可能随时间变化,应将上述标签视为格式示例,而非绝对标准;必要时提醒用户对照目录确认。
为每个外部镜像固定摘要
为每个外部镜像引用固定标签和摘要,包括 FROM 行,以及会拉取注册表镜像的 COPY --from=<image>。node:24 或 :latest 这样的标签是可变的:注册表可以随时将其指向新内容,因此,同一个 Dockerfile 的两次构建可能生成不同的镜像。@sha256:... 摘要使用不可变内容寻址,因此能让构建可重现,并堵住供应链漏洞(获取的正是你审核过的字节,而不是之后推送的内容)。同时保留人类可读的标签和摘要,方便读者了解镜像的_内容_:
FROM node:24.11.1@sha256:9a2ed90cd91b1f3412affe080b62e69b057ba8661d9844e143a6bbd76a23260f
获取摘要时,如果已安装 regctl,优先使用它——它会查询注册表,而无需拉取整个镜像:
regctl manifest head node:24.11.1 # prints the digest for the current tag
如果没有 regctl,则改用 docker buildx imagetools inspect <image>:<tag>(同样无需完整拉取),或者拉取镜像并读取 docker inspect --format '{{index .RepoDigests 0}}' <image>:<tag>。不要臆造摘要——如果无法解析出真实摘要,就保留一个明确标记的 @sha256:<PIN-ME> 占位符,并告诉用户用于填入摘要的确切命令。
这与集中管理版本的 ARG 配合使用:将标签保存在一个 ARG 中以便阅读,并在相邻的 ARG 中保存摘要,这样整个引用就集中在顶部一处:
ARG NODE_VERSION=24.11.1
ARG NODE_DIGEST=sha256:9a2ed90cd91b1f3412affe080b62e69b057ba8661d9844e143a6bbd76a23260f
FROM node:${NODE_VERSION}@${NODE_DIGEST} AS build
为避免产生反效果,请注意以下两点:
- 这只适用于外部镜像 URI。引用_命名内部阶段_的
COPY --from=build无需摘要——它在构建过程中解析,不会从注册表拉取。 - 固定摘要会锁定镜像,这正是目的——但这也意味着在更新摘要之前,你将无法获得上游安全补丁。提醒用户按计划让自动化工具(Dependabot/Renovate 的 Docker 支持)更新这些摘要,就像更新任何依赖项一样;不要手动编辑哈希,更不要为了“保持最新”而退回到不固定的标签。
始终创建 .dockerignore
每个构建上下文都应有一个 .dockerignore。它可以缩小发送给构建器的内容(加快构建速度、改善缓存),更重要的是,它是一项真正的安全措施:整个上下文都会发送给守护进程,因此排除 .git、.env 和密钥可以防止机密信息和历史记录泄漏到镜像中。
创建时,先以仓库的 .gitignore 为基础(如果存在,就复制其中的条目,以便也排除 node_modules、构建输出和 dist 等已忽略的杂项),然后添加 .gitignore 中不会包含的 Docker 专用条目:
# --- copied from .gitignore ---
# (paste the project's .gitignore contents here)
# --- Docker additions ---
.git
.gitignore
.dockerignore
Dockerfile*
compose*.yaml
docker-compose*.yml
docker-bake.hcl
# secrets & local env
.env
.env.*
*.pem
secrets/
# CI / docs / editor / OS cruft
.github/
README.md
.vscode/
.idea/
.DS_Store
.git 是最重要的新增项:它通常体积很大,而且包含完整历史记录(包括曾经提交过的所有机密信息),几乎永远不需要放进镜像中。
始终添加 Bake 文件
添加 docker-bake.hcl,将构建定义为可复现、受版本控制的配置,而不是冗长且不断偏离的 docker build 命令行。Bake(docker buildx bake)会读取该文件,使用一致的标签、平台和参数构建一个或多个目标;CI 也应调用它,以确保本地构建和 CI 构建保持一致。
variable "TAG" {
default = "latest"
}
group "default" {
targets = ["app"]
}
target "app" {
context = "."
dockerfile = "Dockerfile"
tags = ["myapp:${TAG}"]
# platforms = ["linux/amd64", "linux/arm64"] # uncomment for multi-arch
}
使用 docker buildx bake(default 组)或 docker buildx bake app 进行构建。在运行时覆盖变量(TAG=v2 docker buildx bake),或按目标覆盖(--set app.platforms=linux/arm64)。使用 inherits 共享基础目标的设置,使用 matrix 将一个目标扩展为多个变体。请注意,如果不带 -f,docker buildx bake 会优先自动发现 compose.yaml,然后才查找 docker-bake.hcl,并将服务的 build: 块转换为目标。因此,如果仓库中同时存在这两个文件,请在 CI 中使用 -f docker-bake.hcl 指定所需文件。
使用 Compose 进行本地开发
如果是首次设置且尚无 Compose 文件,请完成所需的构建文件,并在最终答复中提出添加 compose.yaml。在获得批准前,先不要创建 Compose 文件,让用户决定本地开发的范围。如果用户暂时无法回复,请在下次回复时附上该提议。如果 Compose 文件已存在,则在原处进行现代化改造。
约定:
- 将文件命名为
compose.yaml(Compose Spec 的规范文件名),而不是docker-compose.yml。 - 不要使用顶层
version:键。 它在 Compose V2 中已过时,现在会发出警告;修改任何文件时都应将其移除。 - 源代码优先使用文件同步,而非绑定挂载。 传统绑定挂载(
./src:/app/src)会使容器路径依赖于主机目录结构,还可能速度较慢或遇到权限问题。现代的develop.watch机制则会将变更的文件 同步 到容器中,速度更快、跨平台兼容,而且可以为每种路径变更选择相应操作。
services:
web:
build: .
ports:
- "3000:3000"
develop:
watch:
# sync: push live source changes into the running container (hot reload)
- action: sync
path: ./src
target: /app/src
ignore:
- node_modules/
# rebuild: dependency manifests / compiled languages need a fresh image
- action: rebuild
path: package.json
# sync+restart: config the process only reads at startup
- action: sync+restart
path: ./config.yml
target: /app/config.yml
使用 docker compose watch 启动。根据变更所需的操作进行选择:可热重载的源代码使用 sync,锁文件或编译后的代码使用 rebuild,仅在启动时读取的配置使用 sync+restart。
完成时
在面向用户的最终答复中完成以下检查,确保用户无需打开其他文档也能维护这些文件:
- 对每项已采用的默认设置或现代化改造,分别提供一行“变更 — 原因”。涵盖前端、基础镜像、摘要固定、缓存挂载、运行时用户、
.dockerignore、Bake,以及使用时的每项 Compose 变更。原因应说明其好处;仅罗列设置并不完整。 - 提供准确的构建和运行命令,以及验证结果和未解决的占位内容,以便用户复现操作。
- 如果是首次设置,请附上上述 Compose 提议,供用户批准下一步。