Docker-basiertes Build von C/C++-Anwendungen
Hinweis: Basierend auf diesem Beitrag habe ich buildock auf GitHub veröffentlicht.
Viele C/C++-Programmierer und Projektmanager kennen den Schmerz, eine reproduzierbare Build-Umgebung für alle Entwickler zu schaffen: Works for me ist nicht ohne Grund ein verbreitetes Meme.
Mein Ansatz ist es, nicht zwingend die Anwendung selbst zu dockerisieren, sondern das Build-System – dabei werden sowohl die spezifische Compilerversion als auch das System drumherum inklusive aller benötigten Systemebenen-Bibliotheken in einem Docker-Image gekapselt.
Nehmen wir das obligatorische Hello World in C++:
// main.cpp
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
return 0;
}und das entsprechende Makefile:
all:
g++ -o helloworld main.cpp
Wie können wir dieses einfache Projekt kompilieren, ohne einen Compiler und GNU make auf dem lokalen Rechner zu installieren (kein Schummeln durch Build-Server erlaubt)?
Eigentlich ist es recht einfach:
docker run --user $(id -u):$(id -g) -v $PWD:/app -it ulikoehler/ubuntu-gcc-make makeAufgeschlüsselt:
docker run: Einen neuen Docker-Container erstellen und einen Befehl darin ausführen--user $(id -u):$(id -g): Dies lässt den Docker-Container mit der ID des aktuellen Benutzers und der Gruppe des aktuellen Benutzers laufen – einerseits um zu verhindern, dass der Compiler Ausgabedateien alsrooterstellt, und andererseits als Schutz gegen einige IT-Sicherheitsrisiken. Siehe auch Docker-Container als aktueller Benutzer & Gruppe ausführen-v $PWD:/app: Das aktuelle Verzeichnis ($PWD) auf/appim Container mounten. Da das zum Bau des Containers verwendete Dockerfile dieWORKDIR /app-Direktive enthält, wird jeder Befehl im Container standardmäßig im Verzeichnis/appausgeführt – und damit im aktuellen lokalen Verzeichnis auf dem Host.-itführt den Container im interaktiven Modus aus, d.h. Tasteneingaben werden an den ausgeführten Befehl weitergeleitet. Außerdem bedeutet dies, dass unser Befehl erst beendet wird, wenn der Container die Ausführung abgeschlossen hat.ulikoehler/ubuntu-gcc-make: Dies ist das Image, das wir für dieses Beispiel verwenden. Es ist nichts weiter als einubuntu:18.04-Basisimage mit installiertembuild-essentialsundmakesowieWORKDIRauf/appmake: Dies ist der Befehl, den wir im Container ausführen. Sie können hier einen beliebigen Befehl verwenden, sogar kein Befehl ist möglich (in diesem Fall wird der Standardbefehl des Containers verwendet – im Fall vonulikoehler/ubuntu-gcc-makeist dasCMD [ "/usr/bin/make" ])
Hier ist das vollständige Dockerfile, das zum Erstellen von ulikoehler/ubuntu-gcc-make verwendet wird:
FROM ubuntu:18.04
RUN apt update && apt -y install build-essential make && rm -rf /var/lib/apt/lists/*
WORKDIR /app
CMD [ "/usr/bin/make" ]