多阶段构建核心是用单个dockerfile分离编译与运行:第一阶段用含工具链镜像(如golang:1.21)编译,以as命名;第二阶段换alpine等轻量镜像,仅通过copy --from=builder复制必要产物(如二进制、jar、dist),剔除编译器、源码及中间文件,并配合cgo_enabled=0等语言特性和非root用户、healthcheck等增强安全与运维。

多阶段构建配置的核心是用一个 Dockerfile 分离“怎么编”和“怎么跑”,只把最终需要的文件复制到轻量运行镜像里,不带编译器、源码或中间产物。
明确划分构建与运行两个阶段
第一阶段用含完整工具链的镜像(如 golang:1.21、maven:3.8、node:18),完成依赖安装、代码编译或打包;第二阶段换极简镜像(如 alpine:latest、distroless 或 scratch),只放可执行文件或静态资源。
- 构建阶段必须用 AS 命名,比如
FROM golang:1.21-alpine AS builder - 运行阶段不需要命名,但要用
COPY --from=builder显式指定来源 - 避免在运行阶段重复安装构建时用过的包(如 gcc、make、npm)
正确使用 COPY --from 复制产物
这是多阶段构建的关键动作,只复制真正需要的东西,不是整个工作目录。
- Go 项目:复制编译好的二进制文件,例如
COPY --from=builder /app/main . - Java 项目:复制 target 目录下的 jar 包,例如
COPY --from=builder /app/target/app.jar . - 前端项目:复制 dist 构建产物,例如
COPY --from=builder /app/dist /usr/share/nginx/html - 不要写
COPY --from=builder /app .这类宽泛路径,容易带入源码或缓存
注意语言特性和精简技巧
不同语言有各自减体积的关键点,忽略容易让最终镜像变大或无法运行。
- Go:设
CGO_ENABLED=0+GOOS=linux,生成静态二进制,省去 libc 依赖 - Python:用
--prefix=/install安装依赖,再整体复制/install目录,避开编译工具链 - Node.js:构建阶段用
npm ci --only=production,避免 devDependencies 进入最终镜像 - Java:Maven 构建加
-DskipTests,跳过测试阶段节省时间和空间
可选但推荐的安全与运维增强
多阶段不只是瘦身,还能顺便加固和规范运行行为。
- 创建非 root 用户,用
useradd -u 1001 -m appuser+USER appuser - 加
HEALTHCHECK,优先用语言内置能力(如 Python 的urllib.request)而非 curl - 设置
WORKDIR和EXPOSE明确路径与端口,提升可读性 - 用 BuildKit(
DOCKER_BUILDKIT=1)启用缓存优化,加快重复构建











