为什么多阶段构建 多阶段构建将“编译阶段”和“运行阶段”拆分,最终镜像仅包含运行所需的产物,大幅降低体积与攻击面,同时配合缓存策略缩短构建时间、提升 CI 稳定性。
基础示例(Go) 1 2 3 4 5 6 7 8 9 10 11 12 13 FROM golang:1.22 AS buildWORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o app ./cmd/app FROM gcr.io/distroless/base-debian12COPY --from=build /src/app /app USER 65532 :65532 ENTRYPOINT ["/app" ]
要点:
先复制 go.mod/go.sum 再 download,最大化利用缓存
使用 -ldflags="-s -w" 去除符号表缩小体积
Distroless 无 shell,安全面更小
Node/前端镜像示例 1 2 3 4 5 6 7 8 9 10 11 FROM node:20 -alpine AS buildWORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . RUN npm run build FROM nginx:alpineCOPY --from=build /app/dist /usr/share/nginx/html
Java 示例(分层与JRE) 1 2 3 4 5 6 7 8 9 10 11 FROM maven:3.9 -eclipse-temurin-21 AS buildWORKDIR /src COPY pom.xml . RUN mvn -B -q -e -DskipTests=true dependency:go-offline COPY . . RUN mvn -B -q -DskipTests=true package FROM eclipse-temurin:21 -jre-alpineWORKDIR /app COPY --from=build /src/target/app.jar app.jar ENTRYPOINT ["java" ,"-XX:+UseContainerSupport" ,"-jar" ,"/app/app.jar" ]
BuildKit/缓存/多架构 启用 BuildKit 与缓存挂载:
1 DOCKER_BUILDKIT=1 docker build --progress=plain .
在 Dockerfile 中使用缓存(Go 模块):
1 2 3 RUN --mount=type =cache,target=/go/pkg/mod \ --mount=type =cache,target=/root/.cache/go-build \ go build -o app ./cmd/app
构建多架构镜像:
1 2 docker buildx create --use docker buildx build --platform linux/amd64,linux/arm64 -t repo/app:1.0 --push .
.dockerignore 与层优化
忽略不必要的文件(.git、node_modules、测试数据、临时产物)
将变化频率高的文件靠后 COPY,减少层失效
合并 RUN 层,清理包管理器缓存
1 2 RUN apk add --no-cache curl && \ rm -rf /var/cache/apk/*
安全与合规
非 root 用户运行(USER)
只读根文件系统与最小权限
使用 Trivy/Grype 做漏洞扫描
1 trivy image repo/app:1.0
1 2 syft repo/app:1.0 > sbom.json cosign sign --key cosign.key repo/app:1.0
运行与可观测性
正确处理信号:ENTRYPOINT 使用可转发信号的 init(如 distroless 默认支持,或 tini)
暴露健康检查
1 HEALTHCHECK --interval=30s --timeout =3s CMD curl -f http://localhost:8080/health || exit 1
常见踩坑
将 package.lock/yarn.lock 忽略导致缓存失效严重
在运行镜像内执行 shell 指令失败(distroless 无 sh),调试需在构建镜像或临时替换为 alpine
时区/证书缺失:按需安装 tzdata 或 CA 证书
层顺序差导致频繁全量重建
FAQ
Alpine 与 Distroless 选哪个?Alpine 可调试;Distroless 更安全更小。开发阶段 Alpine,生产可切 Distroless
如何加速构建?并行构建、缓存挂载、镜像代理(registry mirror)
镜像太大?排查大文件(dive 工具)、压缩产物、剔除无关二进制与文档
总结 多阶段构建是现代容器化的“标配”。结合 BuildKit 缓存、多架构构建、非 root 安全策略与 SBOM/签名流程,既能获得小体积高安全的镜像,又能保障交付效率与可追溯性。