Docker Multi-Stage Builds | Smaller Images
이 글의 핵심
Split builder and runtime stages with COPY --from, shrink C++ and Node images, pick slim or distroless bases, fix missing.so errors, and avoid Alpine/glibc mismatches.
Introduction
Multi-stage builds put several FROM lines in one Dockerfile, name stages with AS, compile in the builder stage, then COPY —from only the binary and required .so files into a small runtime image. The final image does not ship compilers, headers, or source—smaller pulls and smaller attack surface.
This article focuses on patterns illustrated with C++ toolchains; the same ideas apply to Node, Go, and other compiled or bundled apps.
After reading this post
- You understand builder vs runtime separation
- You can write a minimal multi-stage Dockerfile
- You can troubleshoot missing shared libraries and Alpine vs glibc issues
Table of contents
Multi-stage build concepts
Builder vs runtime
- Multi-stage: Multiple FROM lines; name stages with AS. Build in the first stage; in the second, use a light base (alpine, distroless, debian-slim) and COPY —from=build_stage to bring only the binary and needed
.sofiles. - The final image excludes compilers, headers, and build tools → smaller size and attack surface.
flowchart TB
subgraph stage1["Stage 1: builder"]
S1[ubuntu:22.04] --> S2[Install g++, cmake]
S2 --> S3[Copy source]
S3 --> S4[Build]
S4 --> S5[myapp binary]
end
subgraph stage2["Stage 2: runtime"]
R1[debian:bookworm-slim] --> R2[Install libstdc++6 only]
R2 --> R3[COPY --from builder]
R3 --> R4["Final image ~50MB"]
end
S5 -.->|COPY --from| R3
Basic multi-stage Dockerfile
The builder stage compiles with g++/cmake; the debian:bookworm-slim stage copies only myapp and installs libstdc++6 for dynamic linking.
# Stage 1: build
FROM ubuntu:22.04 AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ cmake \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /src
COPY . .
RUN cmake -B build && cmake --build build
# Stage 2: runtime
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
libstdc++6 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /src/build/myapp /usr/local/bin/
CMD [myapp]
Dynamic vs static linking
- Dynamic: Install libstdc++6 (and friends) in the runtime stage—usually easier to maintain.
- Static: Options like
-static-libstdc++can reduce runtime packages—verify licensing and ABI policies.
vcpkg + multi-stage example
# ========== Stage 1: build ==========
FROM ubuntu:22.04 AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ cmake git ca-certificates \
&& rm -rf /var/lib/apt/lists/*
ENV VCPKG_ROOT=/opt/vcpkg
RUN git clone https://github.com/Microsoft/vcpkg.git ${VCPKG_ROOT} \
&& ${VCPKG_ROOT}/bootstrap-vcpkg.sh -disableMetrics
WORKDIR /src
COPY vcpkg.json .
RUN ${VCPKG_ROOT}/vcpkg install
COPY CMakeLists.txt .
COPY src/ src/
RUN cmake -B build \
-DCMAKE_TOOLCHAIN_FILE=${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake \
&& cmake --build build --target myapp
# ========== Stage 2: runtime ==========
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
libstdc++6 libgcc-s1 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /src/build/myapp /usr/local/bin/
USER nobody
CMD [myapp]
Optimize the deployment image
Slim bases and clean layers
- Base images: Alpine is tiny but uses musl—a glibc binary may not run. debian-slim, ubuntu minimal, or distroless are safer defaults for many glibc binaries.
- Cleanup: Use apt-get install —no-install-recommends and remove /var/lib/apt/lists/ after installs to shrink layers.
- Fewer layers: Combine RUN steps when practical.
Base image comparison
| Base | Size | glibc | Typical C++ | Notes |
|---|---|---|---|---|
| ubuntu:22.04 | ~77MB | Yes | Yes | Easy default |
| debian:bookworm-slim | ~80MB | Yes | Yes | Often recommended |
| alpine:3.19 | ~7MB | No (musl) | Caution | glibc binaries won’t run |
| gcr.io/distroless/cc-debian12 | ~50MB | Yes | Yes | No shell—strong isolation |
distroless example
FROM debian:bookworm-slim AS builder
# ....build ...
FROM gcr.io/distroless/cc-debian12
COPY --from=builder /usr/local/bin/myapp /app/myapp
ENTRYPOINT [/app/myapp]
distroless has no shell or package manager—minimal attack surface. For debugging, override entrypoint at docker run time.
Common errors
“cannot find -lstdc++” / “undefined reference”
Cause: Runtime stage missing libstdc++. Fix:
RUN apt-get update && apt-get install -y --no-install-recommends \
libstdc++6 libgcc-s1 \
&& rm -rf /var/lib/apt/lists/*
“error while loading shared libraries: libxxx.so.X”
Cause: Dynamic dependency missing in the runtime image. Fix: On the builder, run:
ldd build/myapp
Install missing .so in the runtime stage or COPY --from=builder the needed libraries.
Alpine: “FATAL: kernel too old” or glibc mismatch
Cause: Alpine uses musl; binaries built for glibc won’t run. Fix: Use debian-slim or distroless for glibc binaries, or build inside Alpine if you standardize on musl.
”no such file or directory” when running the binary
Cause: Missing dynamic linker or wrong architecture.
Fix: file build/myapp and ldd build/myapp.
Conclusion
Multi-stage Docker builds keep heavy toolchains out of production and make runtime images easier to scan and deploy. Pair with layer caching, .dockerignore, and a registry workflow—see Docker Compose production patterns and the Node.js deployment guide for full-stack examples.
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Split builder and runtime stages with COPY —from, shrink C++ and Node images, pick slim or distroless bases, fix missin… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- Docker Compose로 Node API·PostgreSQL·Redis 한 번에 띄우기
- Node.js 배포 가이드 | PM2, Docker, AWS, Nginx
- GitHub Actions로 Node.js CI/CD 파이프라인 만들기 | 테스트·빌드·Docker·배포
이 글에서 다루는 키워드 (관련 검색어)
Docker, Multi-Stage Build, Container, CI, distroless, Image Size, Security 등으로 검색하시면 이 글이 도움이 됩니다.