Docker and C++: Advanced Level Application Containerization

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Docker and C++: Application Containerization: Advanced Level

\n\n

Containerizing C++ applications using Docker is not just about moving a binary into an isolated environment. At an advanced level, it requires considering the specifics of compilation, dependency management, image size optimization, and debugging. In this article, we will break down best practices for creating efficient Docker images for C++ projects, using multi-stage builds, static and dynamic linking, as well as modern tools such as CMake and vcpkg.

\n\n

1. Multi-stage Build

\n

The classic problem with Docker images for C++ is the huge size due to including the compiler, header files, and libraries in the final image. Multi-stage builds solve this problem: the first stage (build stage) contains all the tools for compilation, while the second stage (runtime stage) contains only the minimal set of runtime dependencies.

\n\n
# First stage: build\nFROM gcc:13 AS builder\nWORKDIR /app\nCOPY . .\nRUN apt-get update && apt-get install -y cmake libboost-all-dev && \\\n    cmake -B build -DCMAKE_BUILD_TYPE=Release && \\\n    cmake --build build --parallel $(nproc)\n\n# Second stage: runtime\nFROM alpine:3.19\nRUN apk add --no-cache libstdc++ libgcc\nCOPY --from=builder /app/build/my_app /usr/local/bin/my_app\nCMD ["my_app"]
\n\n

This approach reduces the final image from 1.5 GB (GCC image) to ~30 MB. For a C++ application to work in Alpine, only libstdc++ and libgcc are sufficient.

\n\n

2. Dependency Management: vcpkg and Conan

\n

For modern C++ projects with dozens of libraries, manually installing dependencies via apt-get becomes unmanageable. The best solution is to use package managers such as vcpkg or Conan directly in the Dockerfile.

\n\n
FROM ubuntu:22.04 AS builder\nRUN apt-get update && apt-get install -y curl git cmake build-essential\nRUN git clone https://github.com/Microsoft/vcpkg.git /opt/vcpkg && \\\n    /opt/vcpkg/bootstrap-vcpkg.sh && \\\n    /opt/vcpkg/vcpkg install fmt spdlog nlohmann-json\n\nWORKDIR /app\nCOPY . .\nRUN cmake -B build -DCMAKE_TOOLCHAIN_FILE=/opt/vcpkg/scripts/buildsystems/vcpkg.cmake && \\\n    cmake --build build
\n\n

Important: to avoid rebuilding vcpkg and libraries with every source code change, copy vcpkg.json and CMakeLists.txt before copying the rest of the files. Docker caches layers, and dependencies will only be reinstalled when the manifest changes.

\n\n

3. Image Optimization: Static Linking and Alpine

\n

To minimize the image size, you can use static linking. This is especially relevant for microservices and CLI utilities. However, remember: static linking can increase the binary size but eliminates the need to include dynamic libraries in the container.

\n\n
# CMakeLists.txt\nset(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++")\ntarget_link_libraries(my_app PRIVATE fmt::fmt spdlog::spdlog)
\n\n

The final image can be built on scratch (empty image):

\n\n
FROM scratch\nCOPY --from=builder /app/build/my_app /\nENTRYPOINT ["/my_app"]
\n\n

Such an image weighs only a few megabytes. However, note: static linking is not always possible (for example, when using OpenSSL or CUDA), and in such cases, it is better to use Alpine with dynamic libraries.

\n\n

4. Debugging C++ Applications in a Container

\n

Debugging in Docker is a separate challenge. Advanced methods include:

\n
    \n
  • Using debug images: build a separate image with debug symbols and gdbserver. Connect to it from your IDE (VS Code, CLion) via remote debugging.
  • \n
  • Core dumps: enable writing core files in the container and analyze them after a crash:
  • \n
\n\n
# Dockerfile for                

Blogs

Book Recommendations