多阶段构建
使用多阶段构建减小镜像体积,分离构建和运行环境
📋 目录
多阶段构建原理
为什么需要多阶段构建
传统构建方式的问题:
- 镜像包含构建工具和依赖,体积大
- 包含源代码,安全性差
- 构建环境和运行环境混合
多阶段构建的优势:
- 减小镜像体积:只保留运行时需要的文件
- 提高安全性:不包含构建工具和源代码
- 优化构建缓存:构建阶段和运行阶段分离
基本语法
基本结构
# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html命名构建阶段
FROM node:16 AS builder
# ... 构建步骤
FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html构建阶段分离
示例:Node.js 应用
# 构建阶段
FROM node:16 AS builder
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
RUN npm ci
# 复制源代码并构建
COPY . .
RUN npm run build
# 运行阶段
FROM node:16-alpine
WORKDIR /app
# 只复制构建产物和运行时依赖
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
CMD ["node", "dist/index.js"]示例:Go 应用
# 构建阶段
FROM golang:1.18 AS builder
WORKDIR /app
# 下载依赖
COPY go.mod go.sum ./
RUN go mod download
# 构建应用
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app .
# 运行阶段
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# 只复制编译好的二进制文件
COPY --from=builder /app/app .
CMD ["./app"]示例:Python 应用
# 构建阶段
FROM python:3.9 AS builder
WORKDIR /app
# 安装依赖
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# 运行阶段
FROM python:3.9-slim
WORKDIR /app
# 只复制已安装的包
COPY --from=builder /root/.local /root/.local
COPY . .
# 确保使用用户安装的包
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]只复制必要文件
使用 —from 复制
# 从构建阶段复制文件
COPY --from=builder /app/dist /usr/share/nginx/html
# 从其他镜像复制文件
COPY --from=nginx:alpine /etc/nginx/nginx.conf /etc/nginx/nginx.conf复制多个文件
FROM builder AS builder
# ... 构建步骤
FROM nginx:alpine
# 复制多个文件
COPY --from=builder /app/dist /usr/share/nginx/html
COPY --from=builder /app/config /app/config实际应用案例
案例 1:前端应用
# 构建阶段
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 运行阶段
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]案例 2:Java 应用
# 构建阶段
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# 运行阶段
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=builder /app/target/app.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]案例 3:多服务应用
# 共享构建阶段
FROM node:16 AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci
# API 构建阶段
FROM base AS api-builder
COPY api ./api
RUN npm run build:api
# Web 构建阶段
FROM base AS web-builder
COPY web ./web
RUN npm run build:web
# 运行阶段
FROM nginx:alpine
COPY --from=api-builder /app/api/dist /usr/share/nginx/api
COPY --from=web-builder /app/web/dist /usr/share/nginx/html最佳实践
1. 命名构建阶段
# 使用有意义的名称
FROM node:16 AS builder
FROM nginx:alpine AS production2. 最小化最终镜像
# 只复制运行时需要的文件
COPY --from=builder /app/dist /usr/share/nginx/html
# 不要复制 node_modules、源代码等3. 利用构建缓存
# 先复制依赖文件(变化频率低)
COPY package*.json ./
RUN npm install
# 再复制源代码(变化频率高)
COPY . .
RUN npm run build4. 使用特定目标
# 只构建特定阶段
docker build --target builder -t myapp:builder .
# 构建最终镜像
docker build --target production -t myapp:latest .5. 调试构建阶段
# 构建到特定阶段
docker build --target builder -t myapp:builder .
# 运行构建阶段进行调试
docker run -it myapp:builder /bin/bash高级技巧
使用外部镜像作为源
# 从其他镜像复制文件
COPY --from=nginx:alpine /etc/nginx/nginx.conf /etc/nginx/nginx.conf
COPY --from=node:16 /usr/local/bin/node /usr/local/bin/条件构建
# 根据构建参数选择阶段
ARG BUILD_ENV=production
FROM node:16 AS builder
# ... 构建步骤
FROM node:16-alpine AS dev
COPY --from=builder /app .
FROM nginx:alpine AS production
COPY --from=builder /app/dist /usr/share/nginx/html
# 根据参数选择最终阶段
FROM ${BUILD_ENV}