一、生产镜像目标与威胁模型
生产镜像应可复现、体积合理、默认非 root、没有构建凭据,并输出 SBOM 与 provenance。BuildKit 提供并行构建、缓存和 secret mount,但安全仍取决于 Dockerfile、依赖锁定和发布授权。
镜像身份使用不可变 digest,tag 只是可变别名。回滚选择前一个已验证 digest,不能重新解析旧 tag。
锁定源码/依赖 → 多阶段构建 → 测试
↘ 受控缓存 ↓
最小运行镜像 → 非 root → SBOM/provenance → 签名 → Registry二、多阶段 Dockerfile
构建依赖留在前一阶段,运行镜像只复制产物。基础镜像锁定明确版本,关键环境进一步使用 digest;升级由自动化变更触发并完整测试。
# syntax=docker/dockerfile:1.7
FROM node:22.19.0-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
FROM deps AS test
COPY . .
RUN npm run test -- --run && npm run build
FROM nginx:1.27.4-alpine AS runtime
COPY --from=test /app/dist /usr/share/nginx/html
RUN chown -R nginx:nginx /usr/share/nginx/html /var/cache/nginx /var/run
USER nginx
EXPOSE 8080
HEALTHCHECK CMD wget -qO- http://127.0.0.1:8080/health || exit 1三、缩小构建上下文
.dockerignore 同时优化速度并防止凭据进入 build context。即使后续 RUN 删除秘密,旧层和缓存仍可能保留它,所以必须从源头排除。
.git
node_modules
dist
coverage
.env*
*.pem
*.key
docker-compose*.ymldocker buildx build --progress=plain --no-cache -t app:verify .
docker history --no-trunc app:verify
docker image inspect app:verify --format '{{.Size}} {{.Config.User}}'四、用 Secret Mount 访问私有依赖
不要用 ARG、ENV 或 COPY 传 token,它们可能出现在元数据、镜像层和日志。secret mount 只在该 RUN 临时出现,同时禁止脚本 echo 凭据。
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc,required=true \
--mount=type=cache,target=/root/.npm npm ciDOCKER_BUILDKIT=1 docker build --secret id=npmrc,src="$PWD/.npmrc" \
-t registry.example.com/team/app:$(git rev-parse --short HEAD) .
docker history --no-trunc registry.example.com/team/app:$(git rev-parse --short HEAD)五、缓存加速与污染防护
先复制 lockfile 再安装依赖,源码变化不让依赖层失效。共享缓存写权限只给可信主线;不信任分支只读缓存。定期无缓存构建,发现被缓存掩盖的下载和依赖问题。
docker buildx create --name prod-builder --driver docker-container --use
docker buildx inspect --bootstrap
docker buildx build \
--cache-from type=registry,ref=registry.example.com/team/app:buildcache \
--cache-to type=registry,ref=registry.example.com/team/app:buildcache,mode=max \
--tag registry.example.com/team/app:${GIT_SHA} --push .六、Rootless 构建与非 Root 运行
Rootless 降低守护进程与构建逃逸影响,但不代表容器无风险。运行时继续采用非 root、只读根文件系统、最小 capability、no-new-privileges,并只为必要目录创建 tmpfs。
dockerd-rootless-setuptool.sh install
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
docker info | sed -n '/Security Options/,+8p'
docker run --rm --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m \
--cap-drop=ALL --security-opt=no-new-privileges -p 8080:8080 app:verify七、多架构、SBOM 与 Provenance
SBOM 描述包含什么,provenance 描述如何构建;二者不是漏洞扫描或签名替代品。多架构发布后逐个平台验证实际 digest 和启动行为。
docker buildx build --platform linux/amd64,linux/arm64 \
--sbom=true --provenance=mode=max \
--tag registry.example.com/team/app:${GIT_SHA} --push .
docker buildx imagetools inspect registry.example.com/team/app:${GIT_SHA}八、按 Digest 签名并校验身份
无密钥签名必须严格校验证书身份与发行者,不能只看 verify 成功。部署准入还检查允许的工作流、镜像 digest、基础镜像政策和高危漏洞例外。
DIGEST=$(docker buildx imagetools inspect registry.example.com/team/app:${GIT_SHA} --format '{{json .Manifest.Digest}}' | tr -d '"')
cosign sign --yes registry.example.com/team/app@${DIGEST}
cosign verify registry.example.com/team/app@${DIGEST} \
--certificate-identity-regexp='https://github.com/example/repo/' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com'九、CI 构建和发布
CI 使用短期仓库凭据,测试阶段失败阻止 push。缓存、SBOM 和 provenance 与同一构建动作关联,后续签名使用构建输出 digest,而不是再次查可变 tag。
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with: { registry: registry.example.com, username: '${{ github.actor }}', password: '${{ secrets.REGISTRY_TOKEN }}' }
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: registry.example.com/team/app:${{ github.sha }}
sbom: true
provenance: mode=max
cache-from: type=registry,ref=registry.example.com/team/app:buildcache十、验收与回滚
从干净环境按 lockfile 构建,镜像用户非 root,只读根文件系统正常启动。历史和文件系统中没有 token、私钥、源码及缓存。SBOM、provenance 和签名绑定同一 digest。
- 每个平台都通过健康检查。
- 高危漏洞例外有负责人和截止时间。
- 部署策略拒绝未签名或身份不符镜像。
- 回滚只引用已验证 digest。
总结
BuildKit 把速度、可重复性和供应链证据放入同一构建链。多阶段缩小运行面,secret mount 防止凭据进层,rootless 与非 root 降低权限,SBOM、provenance 和签名让部署身份可验证。