Im ersten Teil unserer Artikel-Serie haben Sie gesehen, wie Sie interaktiv mit Containern arbeiten können. Da wurden Images heruntergeladen, Container im Vorder- und Hintergrund gestartet, oder mit Ein- und Ausgabekanälen verbunden. Mithilfe von Umgebungsvariablen wurden Parameter an die Software im Container übergeben, und Optionen an der Kommandozeile konfigurierten die Abbildung von Netzwerk-Ports auf dem Host. Im zweiten Teil geht es nun weiter mit einem weiteren wichtigen Aspekt des Umgangs mit Containern: der Datenhaltung. Dies ist ein relativ komplexes Thema, da es verschiedene Mechanismen in Docker gibt, die jeweils Vor- und Nachteile haben.

Docker speichert alle Daten, die Bestandteil eines Images sind, in Schichten (in englischer Dokumentation werden diese „layers“ genannt). Dies geschieht automatisch und ist im Detail höchstens für den Ersteller eines neuen Images interessant. Wenn ein neuer Container gestartet wird, erzeugt Docker eine weitere Schicht, die für den Container beschreibbar ist. Änderungen am Dateisystem während des Ablaufs werden in dieser Schicht abgelegt. Tatsächlich verlassen die meisten Container sich auf diesen Mechanismus allerdings nur in Ausnahmefällen, denn die Daten in der Container-Schicht werden gelöscht, wenn der Container selbst vom System entfernt wird.

Bei der Erzeugung eines Images kann der Ersteller festlegen, dass gewisse Pfade innerhalb des Containers als „Volumes“ angebunden werden sollen. Beim Start des Containers erzeugt Docker für diese Pfade automatisch diese Volumes, bei denen es sich um automatisch verwaltete externe Datenblöcke handelt. Für Fachleute lassen sich für Volumes wie auch Containerschichten verschieden Storage-Treiber einrichten bzw. deren Verhalten durch Parameter kontrollieren. Normalerweise ist allerdings das Standardverhalten von Docker absolut ausreichend.

Images sind mit Volumes vorkonfiguriert

Wichtig ist hingegen, die Ablage von Daten selbst unter Kontrolle zu bringen. Um zunächst herauszufinden, welche Volumes ein Image verwendet, sollte zunächst die Dokumentation gelesen werden – falls das nicht hilft, können Sie das Image oder auch einen laufenden Container direkt „inspizieren“. Im folgenden Beispiel wird die JSON-Ausgabe von docker inspect für das Image mysql mit dem Tool jq verarbeitet, um den Inhalt des Elements Config.Volumes zu extrahieren.

Dieselbe Struktur kann auch in der inspect-Ausgabe für einen laufenden Container gefunden werden. Dort gibt es allerdings ausserdem das Element Mounts, in dem auch die automatisch erzeugte ID des Volumes sichtbar ist. Natürlich gibt es auch ein Unterkommando, mit dem alle Volumes angezeigt werden können, die derzeit im System existieren.

Nachdem nun bekannt ist, welche Container-Pfade externe Volumes verwenden, können Sie beim Start des Containers alternative Vorgaben für diese Pfade machen. Im folgenden Beispiel wird das Volume für den Pfad /var/lib/mysql (dort legt MySQL seine Daten ab) mit einem Namen versehen:

So können Sie das Volume nun identifizieren. Es ist möglich, Volumes zwischen Containern zu teilen (solange nur ein Container gleichzeitig schreibenden Zugriff darauf hat!), was in komplexen Umgebungen wichtig ist, damit Ausfallsicherheit gewährleistet werden kann und der Speicherort von Volumes durch verschiedene Treiber frei gewählt werden kann. In jedem Fall ist der Inhalt eines Volumes vom Container unabhängig und wird nicht mehr zusammen mit dem Container gelöscht.

Lokale Verzeichnisse können Volumes ersetzen

Eine Alternative zur Verwendung von Volumes ist die, Verzeichnisse aus dem lokalen Dateisystem des Hosts direkt im Container verfügbar zu machen. Es ist dabei zu beachten, dass die Mechanismen innerhalb des Containers mit denen des Host-Dateisystems kompatibel sein müssen. Die Docker-Dokumentation weist darauf hin, dass etwa ein Windows-Host ein bestimmtes technisches Verhalten seines Dateisystems an einen Linux-Container weitergeben könnte, mit dem eine Datenbank im Container nicht rechnet. Daher wird allgemein die Verwendung von Volumes vorgezogen, aber für gewisse Zwecke sind auch lokale Dateien oder Verzeichnisse sehr nützlich. Zum Beispiel könnte ich als Entwickler ein lokales Verzeichnis mit Quelltexten direkt einem laufenden Docker-Container verfügbar machen:

Im Beispiel wird das lokale Host-Verzeichnis /home/sturm/source (Pfade müssen immer absolut sein!) im Container unter /source verfügbar gemacht. Am Ende des Kommandos wird das Kommando node im Container ausgeführt und wiederum der Pfad /source/index.js übergeben. Wesentlich komplexere Varianten sind denkbar, wenn etwa innerhalb des Containers ein Prozess die Dateien in einem Verzeichnis auf Veränderungen überwacht und sich selbst neu startet. Dann kann ein Entwickler auf dem Host mit einem Editor an den Quelltextdateien arbeiten, und im Container reagiert der Prozess auf Änderungen.

Nebenbei illustriert dieses letzte Beispiel noch ein Detail, das bisher nicht angesprochen wurde: Bei der Verwendung von -v, --volume (das ist die lange Version der Option) oder --mount (eine alternative Syntax, die zur Übergabe komplexerer Parameter besser geeignet ist) dürfen auch Pfade auf der Container-Seite verwendet werden, die nicht vom Ersteller des Images als Volumes deklariert worden sind. So können Sie beliebige Dateien oder Ordner innerhalb eines Containers von außen ersetzen bzw. den Speicherort nach außen verlegen.

Zur Sicherheit sichern

Zum Schluss soll noch das Thema Backups angesprochen werden. Wenn ein Container zum langfristigen Betrieb eines Servers verwendet wird, sollten natürlich die dabei entstehenden Daten gesichert werden, wie bei jedem anderen System. Grundsätzlich gibt es bei Docker zwei Möglichkeiten zur Datensicherung. Der erste Ansatz besteht darin, Daten auf der Host-Seite zu sichern. Wenn ein Verzeichnis auf dem Host im Container verwendet wurde, ist das offensichtlich. Wenn hingegen ein Volume verwendet wurde, müssen Sie herausfinden, wo dieses auf dem Host abgelegt ist. In einem der obigen Beispiele können Sie in der Ausgabe von docker inspect sehen, dass der Standardordner auf Linux unter /var/lib/docker/volumes zu finden ist.

Wenn Sie Daten vom Host sichern möchten, sollten sie sicherstellen, dass diese sich nicht im Zugriff durch Prozesse im Container befinden. Es könnte notwendig sein, den Container temporär anzuhalten, um dies zu erreichen. Sie müssen analysieren, was die Container-Prozesse genau mit den Daten tun, um zu beurteilen, welche Schritte notwendig sind.

Der zweite mögliche Ansatz besteht darin, einen Sicherungsvorgang im Container selbst ablaufen zu lassen. Zum Beispiel bietet MySQL für Sicherungen das Tool mysqldump an, das den Inhalt einer oder mehrerer Datenbanken im SQL-Format auf die Konsole ausgeben kann. Mit einem geeigneten Kommando können Sie mysqldump im bereits laufenden Container ausführen, die Ausgabe aber auf dem Host in eine Datei umleiten.

Um das Beispiel zu verstehen, sei zunächst erwähnt, dass docker exec ein Kommando in einem laufenden Container ausführt. Hier können Sie sehen, wie eine Shell in einem laufenden Container gestartet wird. Der Container weist seinen eigenen Systemnamen mit der von außen sichtbaren ID aus.

Um das Backup mit mysqldump auszuführen, kann das folgende Kommando verwendet werden:

Analog zum vorherigen Beispiel wird noch immer die Shell bash aufgerufen. Allerdings empfängt diese nun mit -c ein längeres Kommando, in dem unter anderem die von außen herein gereichte Umgebungsvariable MYSQL_ROOT_PASSWORD ausgewertet und der Wert als Passwort an mysqldump übergeben werden. Dies funktioniert nur, weil mysqldump auf dem Weg über die Shell gestartet wird, anstatt es direkt aufzurufen. Eine elegante Lösung – und die Datei sqldump-backup.sql findet sich im Anschluss im lokalen Verzeichnis auf dem Host wieder. Da mysqldump innerhalb des Containers läuft und sich dort um die Vermeidung eventueller Parallelzugriffe kümmert, braucht für diesen Ansatz die Datenbank nicht temporär beendet zu werden.

Aufräumarbeiten

Um im Kontext dieses Artikels einen ordentlichen Abschluss zu schaffen, soll noch ein Unter-Unterkommando erwähnt sein, dass gerade für Entwickler interessant ist: prune. Wenn Sie oft ad-hoc und temporär mit Containern arbeiten und testen, immer wieder neue starten und beenden, dann werden auch immer wieder Container und Volumes angelegt, die sich im Laufe der Zeit auf dem System ansammeln und Gigabytes an Speicher verbrauchen.

Natürlich wissen Sie nun, dass Sie mit docker container ls -a alle Container sehen können (auch die, die gerade nicht laufen!), und dass docker volume ls alle Volumes anzeigt. Beide Kommandos haben allerdings auch je ein Unterkommando prune, mit dem Sie die nicht mehr benötigten Volumes und Container löschen können. Natürlich ist dabei Vorsicht geboten, aber auf einem Entwickler- oder Testsystem ist das manchmal dringend notwendig!

Den Ausgaben im Beispiel können Sie entnehmen, was die Kommandos genau tun: Für Container werden all diejenigen gelöscht, die nicht laufen, und für Volumes sind es die, die nicht mehr von einem Container verwendet werden. Bitte seien Sie vorsichtig, damit Sie nicht versehentlich Daten löschen, die Sie noch brauchen!

Containerdaten und Backups – Fazit

Damit ist das Ende dieser Einführung erreicht. Ich hoffe, dass der Umfang nützlich ist – es lässt sich mit Docker noch vieles machen, was in diesen beiden Artikeln nicht erwähnt wurde. Ich empfehle, selbst mit den Kommandos zu experimentieren (fast alle unterstützen eine Option --help!), und natürlich auch die Dokumentation zu Docker zu lesen. Ich wünsche gutes Gelingen und viel Produktivität mit Docker-Containern!

Titelmotiv: Photo by frank mckenna on Unsplash

Oliver Sturm

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Die von Ihnen hier erhobenen Daten werden von der Host Europe GmbH zur Veröffentlichung Ihres Beitrags in diesem Blog verarbeitet. Weitere Informationen entnehmen Sie bitte folgendem Link: www.hosteurope.de/AGB/Datenschutzerklaerung/