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 镜像可以得到最小的容器镜像。