多阶段构建是什么
构建描述里可以有多个起点,每个起点是一个独立阶段,各自拥有文件系统与指令序列。阶段之间通过复制指令把产物从构建阶段搬到运行阶段,最终镜像默认只取最后一个阶段,因此编译工具链与中间文件不会进入成品。
基本信息
| 组织方式 | 每条起点指令开启一个阶段,可用别名命名 |
|---|---|
| 阶段传递 | 从指定阶段复制文件,路径以该阶段的文件系统为准 |
| 最终镜像 | 默认取最后一个阶段,也可指定发布某个阶段 |
| 体积收益 | 编译器、包缓存与源码都留在构建阶段,成品只带运行必需内容 |
| 常见组合 | 带完整工具链的构建阶段,配精简运行阶段 |
典型使用场景
- 编译型语言服务:构建阶段编译二进制,运行阶段只带二进制的极简基础镜像
- 前端项目:构建阶段打包静态资源,运行阶段用轻量 Web 服务器托管产物
- 需要编译扩展的应用:构建阶段装编译依赖并生成扩展,运行阶段只装运行时依赖
- 产物同时用于多处:一个阶段产出文件,多个阶段分别取用
- 调试与发布并行:保留一个带工具的阶段用于排查,发布时按目标阶段构建
典型结构
典型结构
FROM golang:1.23 AS build # 构建阶段,带完整工具链
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download # 先下载依赖,改动代码时仍可复用缓存
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/app
FROM gcr.io/distroless/static-debian12:nonroot # 运行阶段,只带运行时
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]阶段划分的建议
| 阶段 | 放什么 | 目标 |
|---|---|---|
| 构建阶段 | 工具链、源码、编译产物 | 能稳定产出可执行文件或静态资源 |
| 运行阶段 | 产物、运行时、证书、必要配置 | 体积小、依赖少、攻击面小 |
| 调试阶段 | 带 shell 与排查工具的镜像 | 线上临时排查时不污染运行镜像 |
| 依赖缓存策略 | 依赖清单先复制,安装与源码复制分开 | 让依赖层在源码变化时仍能命中缓存 |
落地时的注意点
- 阶段命名统一小写,引用时保持一致,避免大小写导致的构建失败
- 复制产物时确认路径与文件名,二进制权限有时需要在运行阶段确认
- 运行阶段缺少证书时外部请求会失败,需要显式复制证书链
- 调试用阶段不要作为最终阶段,否则会把工具链一起发布出去
- 产物依赖动态链接库时,运行阶段要带上对应依赖,否则启动即报找不到库
构建时指定目标是很有用的调试手段:只构建到中间阶段,可以快速验证产物是否正确,避免每次从头构建完整流程。
运行阶段越小越好用,但也越缺工具。线上排查时用带工具的临时容器接入同一网络,比往运行镜像里装工具更合适。