Es gibt bereits Materialien zu Habr, wie Docker- Container für die Kompilierung des Projekts konfiguriert werden können . Beispiel: Verwenden von Docker zum Erstellen und Ausführen eines C ++ - Projekts . In diesem Artikel wird wie im vorherigen Artikel das Problem des Erstellens des Projekts behandelt, aber hier möchte ich über das Lernprogramm hinausgehen und die Probleme der Verwendung von Containern in solchen Aufgaben sowie des Aufbaus der Erstellungsinfrastruktur mit Docker genauer betrachten.
Ein bisschen über Docker
Zur Klarheit der weiteren Diskussion ist es notwendig, eine Beschreibung einiger Docker- Komponenten bereitzustellen.
Bild
Das Docker-Image ist eine schreibgeschützte Vorlage mit Anweisungen zum Erstellen eines Containers. Um das Image zu erstellen , müssen Sie eine Docker-Datei erstellen, die alle Schritte der Assembly beschreibt. Jeder dieser Schritte erstellt eine separate Ebene im Bild . Jede nachfolgende Ebene wird über alle vorherigen Ebenen gelegt und enthält nur die Änderungen, die an der vorherigen Ebene vorgenommen werden müssen.
Zum Beispiel für eine Docker-Datei :
FROM ubuntu:18.04 ADD app.sh /app ENTRYPOINT /bin/bash /app/app.sh
Das Docker- Image hat die folgende Struktur:

Ebenen im Bild werden zwischengespeichert und können wiederverwendet werden, wenn keine Änderungen erkannt werden. Wenn die Ebene geändert (hinzugefügt / gelöscht) wird , werden alle nachfolgenden Ebenen von Grund auf neu erstellt. Um Änderungen am Container-Image (und entsprechend an der Umgebung des gestarteten Prozesses) vorzunehmen , reicht es aus, die Docker-Datei zu reparieren und mit dem Erstellen des Images zu beginnen.
Behälter
Ein Docker-Container ist eine Startinstanz des Images . Es kann erstellt, gestartet, gestoppt, gelöscht usw. werden. Standardmäßig sind Container voneinander und vom Hostsystem isoliert. Beim Start startet der Container einen Befehl, der in ENTRYPOINT oder CMD angegeben werden kann , und stoppt, wenn er abgeschlossen ist. Eine akzeptable Situation ist, wenn sowohl CMD als auch ENTRYPOINT vorhanden sind , da sie in der Dokumentation beschrieben interagieren.
Wenn Sie jeden Container erstellen, wird zusätzlich zu allen vorhandenen eine neue Ebene hinzugefügt. Es ist im aktuellen Container beschreibbar und wird zusammen mit dem Container zerstört. Alle Schreibvorgänge, bei denen während des Betriebs des Containers neue Dateien erstellt werden, werden auf diese Ebene angewendet. Das Bild bleibt immer unverändert. Die Ebenenstruktur des erstellten Containers sieht also folgendermaßen aus:

Bei Verwendung des docker run
wird jedes Mal ein neuer Container mit einer eigenen Ebene zum Schreiben erstellt. Bei Build-Tasks bedeutet dies, dass bei jedem Start eine neue saubere Umgebung erstellt wird, die nichts mit früheren Ausführungen zu tun hat. Die Liste der erstellten Container kann durch Ausführen des folgenden Befehls angezeigt werden: docker container ls -a
.
Wir sammeln das Projekt im Container
Der Klarheit halber beschreiben wir kurz den Prozess des Erstellens einer Anwendung in einem Container. Dieser Prozess wird in Artikel 1 und Artikel 2 ausführlicher beschrieben.
Schematisch mögliche Schritte zum Erstellen der Anwendung in Docker können wie folgt dargestellt werden:

Lassen Sie uns die gezeigten Schritte analysieren:
- Wir verwenden die Docker-Datei , die die Umgebung beschreibt, Befehle zum Zusammenstellen und Kopieren der Ergebnisse und basierend darauf erstellen wir ein Image des Containers.
- Wir verwenden das resultierende Image, um den Container mit dem
docker run
zu erstellen und zu starten. Wir mounten den Quellordner und den Ordner, in den das Assembly-Ergebnis in den Container kopiert wird. - Nach Abschluss des Containers werden Assembly-Artefakte im bereitgestellten Verzeichnis abgelegt.
Ein Beispiel finden Sie im Artikel .
Da hier der docker run
verwendet wird, wird für jeden Start ein separater Container mit einer eigenen Ebene zum Schreiben erstellt , sodass temporäre Dateien aus früheren Assemblys nicht in die aktuelle gelangen. Denken Sie daran, angehaltene Behälter zu reinigen.
Das Mounten des Quellverzeichnisses erleichtert das Debuggen der Assembly. Es birgt jedoch Risiken: Sie können eine Version von Code erfassen, der die Qualitätskontrolle nicht bestanden hat oder dem Versionskontrollsystem überhaupt nicht hinzugefügt wurde. Um dies zu vermeiden, können Sie das Git-Repository bei jedem Build in den Container klonen, z. B. in der Datei :
FROM ubuntu:bionic RUN apt-get update \ && apt-get install -y apt-utils RUN apt-get update \ && apt-get install -y make gcc g++ qt5-default git RUN mkdir -p /app/src WORKDIR /app/build # ENTRYPOINT git -C /app/src clone https://github.com/sqglobe/SimpleQtProject.git \ && qmake /app/src/SimpleQtProject/SimpleQtProject.pro \ && make \ && cp SimpleQtProject /app/res/SimpleQtProject-ubuntu-bionic
Hier erfolgt das Klonen aufgrund von Caching in ENTRYPOINT
, nicht in der RUN
Anweisung. ENTRYPOINT
wird immer ausgeführt, wenn der Container ENTRYPOINT
wird, und das Ergebnis des RUN
Befehls kann aus dem Cache entnommen werden .
Infrastruktur aufbauen
Um ein Projekt für verschiedene Betriebssysteme oder Linux- Distributionen zu erstellen, kann eine bestimmte Konfiguration von Servern (Build-Maschinen, Server mit einem Versionskontrollsystem usw.) verwendet werden. In der Praxis musste ich mich mit folgender Infrastruktur befassen:

Hier greift der Benutzer auf den Webserver zu, über den das Projekt auf Computern mit Ubuntu und Red Hat erstellt wird . Als nächstes wird auf jedem Computer das Git-Repository mit dem Projekt in ein temporäres Verzeichnis geklont und die Assembly gestartet. Der Benutzer kann die resultierenden Dateien von derselben Seite herunterladen, von der aus er den gesamten Prozess gestartet hat.
Eine solche Assembly ist wiederholbar, da die Entwickler dieselbe Umgebung verwenden.
Von den Minuspunkten ist es notwendig, eine gesamte Infrastruktur zu warten, mehrere Server zu verwalten, Fehler in Skripten und Webanwendungen zu beseitigen usw.
Mit Docker vereinfachen
Die Unterstützung der oben gezeigten Infrastruktur erfordert bestimmte finanzielle und personelle Kosten. Wenn Ihr Team an einem kleinen Startup arbeitet oder Sie der einzige Entwickler sind, können Sie Docker- Container verwenden, um Ihre Build-Infrastruktur zu implementieren.
Stellen Sie sich ein triviales Qt- Projekt vor, das mit qmake - SimpleQtProject erstellt wurde . Der Docker- Ordner des angegebenen Projekts enthält eine Reihe von Dateien:
Diese Dateien implementieren die Idee, Quellcode in einen Container zu klonen.
Die gesamte Assembly wird mit dem Makefile gestartet. Es ist sehr kurz und enthält genügend Kommentare. Grundlage ist die Erstellung eines Images und der Start des Containers:
%: %.docker docker build -t simple-qt-$(strip $(subst .docker,, $< )) --file $< . docker run --mount type=bind,source=$(RELEASE_DIR),target=/app/res simple-qt-$(strip $(subst .docker,, $< ))
In dieser Phase der Assembly wird ein Image des Containers mit dem Namen erstellt, der aus dem Präfix simple-qt- und dem Namen des Systems besteht (für Centos 7 ist es simple-qt-centos7 ). Als Docker- Datei wird die entsprechende Datei mit der Berechtigung .docker verwendet . Als Nächstes wird der Container basierend auf dem erstellten Image gestartet und ein Ordner zum Kopieren von Baugruppenartefakten darauf gemountet.
Nachdem Sie den make
im Docker- Verzeichnis ausgeführt haben, enthält der Ordner Docker / Releases die Build-Ergebnisse für mehrere Plattformen.
Daher sieht unsere Infrastruktur zum Erstellen von SimpleQtProject folgendermaßen aus:

Vorteile dieser Konfiguration:
- Lokalität . Der Entwickler sammelt ein Projekt für mehrere Plattformen auf seinem lokalen Computer. Dadurch entfällt die Notwendigkeit, eine Flotte von Servern aufzunehmen, das Kopieren von Artefakten zwischen Servern über das Netzwerk zu konfigurieren, Netzwerkbefehle zu senden und zu verarbeiten.
- Isolierung der Umwelt . Der Container bietet eine vollständig isolierte Umgebung zum Erstellen einer bestimmten Anwendung. Es ist möglich, Projekte mit inkompatiblen Umgebungen auf demselben Computer zu erstellen (z. B. solche, für die unterschiedliche Versionen derselben Bibliothek erforderlich sind).
- Versionierung Durch Platzieren der Docker-Datei im Git-Repository können Sie Änderungen in der Build-Umgebung mit der Veröffentlichung neuer Releases verfolgen, auf frühere Versionen der Build-Umgebung zurücksetzen usw.
- Mobilität . Bei Bedarf wird diese Infrastruktur problemlos auf einem anderen Computer bereitgestellt. Mit der Technologie zum Erstellen eines Container-Images können Sie ganz einfach Änderungen am Image selbst vornehmen. Aktualisieren Sie einfach die Docker-Datei und beginnen Sie mit der Erstellung des Images.
- Selbstdokumentierend . Im Wesentlichen enthält eine Docker-Datei Schritte zum Bereitstellen einer Assembly-Umgebung. Stellen Sie daher bei Bedarf eine solche Umgebung bereit, aber bereits in einem regulären System. Sie können die Befehle daraus verwenden.
- Leichtigkeit . Der Container startet zu dem Zeitpunkt, an dem die Montage beginnt, und stoppt automatisch nach Abschluss. Es verschwendet keine CPU-Zeit und RAM.
Es gibt jedoch ein deutliches Minus: Für die Zusammenstellung des Projekts muss das Container-Image zusammengesetzt werden. Wenn Sie zum ersten Mal starten, kann dies lange dauern. Bei wiederholten Bildern , insbesondere wenn sich die Docker-Datei nicht geändert hat, wird das Bild mithilfe des Caches um ein Vielfaches schneller zusammengestellt.
Denken Sie auch daran, angehaltene Behälter zu reinigen.
Fazit
Abschließend möchte ich darauf hinweisen, dass Docker nicht die einzige Containerisierungstechnologie ist. Es gibt jedoch einige Merkmale, die es für Montageaufgaben von demselben LXC günstig unterscheiden:
- Sie können einen Container mithilfe einer Text- Docker-Datei erstellen . Dies ist eine Datei mit einfacher Syntax. Sie können sie (wie immer) zum Projekt-Repository hinzufügen und ständig zur Hand haben.
- Jedes Mal, wenn wir den Docker- Container mit dem
docker run
wir eine saubere Umgebung, als würden wir zum ersten Mal alles tun. Temporäre Dateien zwischen Assemblys werden nicht gespeichert. - Der Container startet nicht das gesamte Betriebssystem, sondern nur den erforderlichen Montageprozess.