Skip to content

Docker 多阶段构建

Docker 多阶段构建让 C++ 项目的容器化变得简单:构建阶段包含完整工具链,运行阶段只包含最终二进制,镜像体积从 GB 级降到 MB 级。

基础多阶段构建

dockerfile
# Dockerfile
# ===== 构建阶段 =====
FROM ubuntu:22.04 AS builder

# 安装构建工具
RUN apt-get update && apt-get install -y \
    cmake ninja-build g++ clang \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

# 安装 vcpkg
RUN git clone https://github.com/microsoft/vcpkg.git /vcpkg \
    && /vcpkg/bootstrap-vcpkg.sh -disableMetrics

ENV VCPKG_ROOT=/vcpkg
ENV PATH=$VCPKG_ROOT:$PATH

WORKDIR /app

# 先复制依赖文件(利用 Docker 层缓存)
COPY vcpkg.json CMakeLists.txt ./

# 安装依赖(这一层会被缓存,除非 vcpkg.json 变化)
RUN cmake -B build \
    -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \
    -DCMAKE_BUILD_TYPE=Release \
    && cmake --build build --target install_deps 2>/dev/null || true

# 复制源码并构建
COPY src/ src/
COPY include/ include/

RUN cmake -B build \
    -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \
    -DCMAKE_BUILD_TYPE=Release \
    -G Ninja \
    && cmake --build build --parallel

# ===== 运行阶段 =====
FROM ubuntu:22.04 AS runtime

# 只安装运行时依赖
RUN apt-get update && apt-get install -y \
    libssl3 \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# 创建非 root 用户
RUN useradd -r -s /bin/false appuser

WORKDIR /app

# 只复制最终二进制
COPY --from=builder /app/build/myapp .

# 复制配置文件(如果需要)
COPY config/ config/

USER appuser

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s \
    CMD curl -f http://localhost:8080/health || exit 1

ENTRYPOINT ["./myapp"]
CMD ["--config", "config/production.json"]

使用 Conan 的多阶段构建

dockerfile
FROM ubuntu:22.04 AS builder

RUN apt-get update && apt-get install -y \
    cmake ninja-build g++ python3-pip \
    && pip3 install conan \
    && rm -rf /var/lib/apt/lists/*

RUN conan profile detect --force

WORKDIR /app

# 先安装依赖(缓存层)
COPY conanfile.txt ./
RUN conan install . --output-folder=build --build=missing \
    -s build_type=Release

# 构建
COPY . .
RUN cmake -B build \
    -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake \
    -DCMAKE_BUILD_TYPE=Release \
    -G Ninja \
    && cmake --build build

FROM debian:bookworm-slim AS runtime
COPY --from=builder /app/build/myapp /usr/local/bin/
EXPOSE 8080
CMD ["myapp"]

最小化镜像(Alpine)

dockerfile
# 使用 Alpine 进一步减小镜像
FROM alpine:3.19 AS builder

RUN apk add --no-cache \
    cmake ninja g++ musl-dev openssl-dev

WORKDIR /app
COPY . .

RUN cmake -B build -DCMAKE_BUILD_TYPE=Release -G Ninja \
    -DCMAKE_EXE_LINKER_FLAGS="-static"  # 静态链接
RUN cmake --build build

FROM scratch AS runtime  # 空镜像!
COPY --from=builder /app/build/myapp /myapp
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/myapp"]

docker-compose.yml

yaml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: runtime
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=postgres
      - REDIS_HOST=redis
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redisdata:/data

volumes:
  pgdata:
  redisdata:

构建优化技巧

dockerfile
# 技巧 1:BuildKit 缓存挂载(加速包安装)
# syntax=docker/dockerfile:1
FROM ubuntu:22.04 AS builder
RUN --mount=type=cache,target=/var/cache/apt \
    apt-get update && apt-get install -y cmake g++

# 技巧 2:并行构建
RUN cmake --build build --parallel $(nproc)

# 技巧 3:ccache 加速重复构建
RUN --mount=type=cache,target=/root/.ccache \
    CXX="ccache g++" cmake --build build

# 技巧 4:只复制必要文件
.dockerignore:
build/
.git/
*.md
tests/
benchmarks/

关键认知

多阶段构建的关键是分离构建环境和运行环境。先复制 vcpkg.json/conanfile.txt 再安装依赖,利用 Docker 层缓存避免每次重新下载。静态链接(-static)配合 scratch 镜像可以得到最小的容器镜像。

系统学习 C++ 生态,深入底层架构