多阶段构建是什么
多阶段构建把一份构建文件分成多个阶段:编译工具链、依赖与中间产物留在前面的阶段,最终阶段只把运行需要的产物复制过来。好处很直接:编译环境不进产物镜像,体积更小、依赖更少、推送更快,攻击面也随之收窄,是编译型语言与前端项目的常规做法。
基本信息
| 原理 | 每个阶段独立,前面的阶段默认被丢弃,产物按需复制 |
|---|---|
| 阶段引用 | 复制时可按阶段名称引用,也可按出现顺序的序号引用 |
| 调试方式 | 构建时可以只构建到指定阶段,便于验证编译结果 |
| 收益 | 编译依赖与工具链不进产物镜像,体积与攻击面同时下降 |
| 成本 | 构建缓存仍会占用构建机磁盘,需要定期清理 |
| 适用项目 | 编译型语言、前端打包、需要运行多个构建步骤的项目 |
典型使用场景
- 前端项目:在一个阶段安装依赖并打包,另一个阶段只放运行时与静态文件
- 编译型语言:在构建阶段编译出可执行文件,运行阶段只带运行时库
- 减小镜像体积:把体积从数百兆压到几十兆,缩短拉取与发布时间
- 隔离构建依赖:避免版本库、编译器与包管理器出现在生产镜像里
- 统一构建环境:把工具链版本固化在构建阶段,保证不同机器构建结果一致
- 分离调试镜像:保留一个带工具的阶段,需要排查时单独构建使用
写法对照
| 写法 | 含义 | 使用注意 |
|---|---|---|
| 多个构建阶段声明 | 每个阶段独立命名 | 名称便于后续引用 |
| 复制阶段产物 | 从指定阶段复制文件 | 路径取自该阶段文件系统,不是构建机 |
| 按序号引用 | 按阶段出现顺序引用 | 插删阶段后序号会变化,建议用具名 |
| 指定目标阶段 | 只构建到某个阶段 | 用于调试与产物校验 |
| 运行阶段基础镜像 | 选择精简运行时 | 需确认动态库与证书齐备 |
前端场景示例
前端场景示例
FROM node:20-bookworm-slim AS build
WORKDIR /src
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:1.27-alpine
COPY --from=build /src/dist /usr/share/nginx/html
# 构建阶段不会进入最终镜像,工具链与依赖都留在中间层未使用的中间阶段不会进入最终镜像,但构建缓存会占用构建机磁盘:日常配合清理命令回收空间,否则构建机往往先于业务出现磁盘告警。
复制路径的语义容易混淆:它取的是阶段内文件系统的路径,不是构建机上的目录,镜像内路径写错时会报找不到文件。
运行阶段的基础镜像要选对:过于精简的镜像可能缺少时区数据、证书或动态库,表现为程序能启动但访问外部服务失败。
调试时只构建到中间阶段:可以进到该阶段里检查产物是否完整,比反复构建完整镜像再排查效率高得多。
落地建议
- 构建阶段与运行阶段的依赖分开维护,职责更清晰
- 运行阶段选择带证书与时区数据的基础镜像
- 为调试保留一个带工具的阶段,但不进入最终镜像
- 固定构建阶段的基础镜像版本,保证构建结果可复现
- 定期重新构建以获取基础镜像的安全更新