Einleitung: Warum C++-Anwendungen containerisieren?
Die Entwicklung mit C++ ist traditionell mit dem Problem verbunden: "Es funktioniert auf meinem Rechner". Unterschiedliche Compiler-Versionen, Bibliotheken und Systemabhängigkeiten machen die Bereitstellung zum Kopfzerbrechen. Docker löst dieses Problem: Sie packen die Anwendung mit der gesamten Umgebung in einen leichten Container, der auf jedem System gleich läuft.
In diesem Artikel werden wir den vollständigen Prozess der Containerisierung einer C++-Anwendung durchgehen – von einem einfachen Dockerfile bis hin zum Multi-Stage-Build mit Optimierung der Image-Größe.
1. Grundlagen: Dockerfile für eine C++-Anwendung
Beginnen wir mit dem einfachsten Beispiel. Angenommen, wir haben ein C++-Programm, das "Hello, Docker!" ausgibt.
Quellcode (hello.cpp)
#include <iostream>
int main() { std::cout << "Hello, Docker!" << std::endl; return 0;}Erstes Dockerfile
# Verwende ein Image mit GCCFROM gcc:13.2
# Lege das Arbeitsverzeichnis festWORKDIR /app
# Kopiere die QuelldateienCOPY hello.cpp .
# KompiliereRUN g++ -o hello hello.cpp
# StarteCMD ["./hello"]Bauen und starten wir es:
docker build -t cpp-hello .docker run cpp-helloDer Nachteil dieses Ansatzes ist, dass das Image aufgrund des vollständigen GCC-Toolchains etwa 1,2 GB wiegt. Für die Produktion ist das inakzeptabel.
2. Multi-Stage-Build
Die beste Praxis ist, Build und Ausführung zu trennen. In der ersten Phase kompilieren wir die Anwendung mit dem vollständigen Toolset, in der zweiten kopieren wir nur das fertige Binary in ein sauberes, minimales Image.
Optimiertes Dockerfile
# Phase 1: BuildFROM gcc:13.2 AS builder
WORKDIR /app
COPY hello.cpp .
RUN g++ -static -o hello hello.cpp
# Phase 2: AusführungFROM alpine:3.19
WORKDIR /app
# Kopiere das statisch kompilierte BinaryCOPY --from=builder /app/hello .
CMD ["./hello"]Der entscheidende Punkt ist das Flag -static. Es bindet alle Abhängigkeiten in das Binary ein, sodass es auf einem reinen Alpine ohne zusätzliche Bibliotheken ausgeführt werden kann. Die Image-Größe reduziert sich von 1,2 GB auf ~6 MB!
3. Realistisches Projekt: CMake + Abhängigkeiten
Betrachten wir einen komplexeren Fall: eine Anwendung, die die Boost-Bibliothek verwendet und über CMake gebaut wird.
Projektstruktur
myapp/├── CMakeLists.txt├── src/│ └── main.cpp└── DockerfileCMakeLists.txt
cmake_minimum_required(VERSION 3.20)project(MyApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Boost REQUIRED COMPONENTS system)
add_executable(myapp src/main.cpp)target_link_libraries(myapp Boost::system)main.cpp
#include <iostream>#include <boost/asio.hpp>
int main() { boost::asio::io_context io; std::cout << "Boost.Asio läuft im Container!" << std::endl; return 0;}Dockerfile mit Abhängigkeiten
# Phase 1: BuildFROM ubuntu:22.04 AS builder
# Installiere Build-AbhängigkeitenRUN apt-get update && apt-get install -y \ build-essential \ cmake \ libboost-system-dev \ && rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Kopiere die QuelldateienCOPY CMakeLists.txt .COPY src/ src/
# BaueRUN mkdir build && cd build && \ cmake .. -DCMAKE_BUILD_TYPE=Release && \ make -j$(nproc)
# Phase 2: AusführungFROM ubuntu:22.04
# Installiere nur Runtime-AbhängigkeitenRUN apt-get update && apt-get install -y \ libboost-system1.74.0 \ && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/build/myapp .
CMD ["./myapp"]Beachten Sie: In der zweiten Phase installieren wir nur die Runtime-Version von Boost (libboost-system1.74.0).