- Einführung
- Kapitel 1 Erste Schritte mit Docker: Wesentliche Befehle.
- Kapitel 2 Erstellen Ihres ersten Docker-Images mit einem Dockerfile.
- Kapitel 3 Verwalten von Multi-Container-Anwendungen mit Docker Compose.
- Kapitel 4 Schreiben eines Skripts zur Automatisierung von Container-Aktualisierungen.
- Kapitel 5 Vereinfachung häufiger Aufgaben mit Hilfsskripten.
- Kapitel 6 Fortgeschrittene Dockerfile-Techniken für optimierte Builds.
- Kapitel 7 Automatisierung von Builds und Pushes mit GitHub Actions.
- Kapitel 8 Skripterstellung für Entwicklungsumgebungen.
- Kapitel 9 Skripte für Continuous Integration und Tests.
- Kapitel 10 Erstellen benutzerdefinierter Entrypoint-Skripte für flexible Container.
- Kapitel 11 Verwalten von Docker-Kontexten in Bash-Skripten.
- Kapitel 12 Skripterstellung für Netzwerk-Erstellung und -Verwaltung.
- Kapitel 13 Automatisierung der Volume-Verwaltung und Datenspeicherung.
- Kapitel 14 Ein Skript zum Bereinigen nicht verwendeter Docker-Ressourcen.
- Kapitel 15 Skripterstellung für Backup und Wiederherstellung von Docker-Volumes.
- Kapitel 16 Automatisierung des Docker-Image-Prunings.
- Kapitel 17 Skripterstellung von Health Checks für Ihre Container.
- Kapitel 18 Erstellen eines Python-Skripts zur Interaktion mit der Docker-API.
- Kapitel 19 Erstellen eines Benutzerverwaltungs-Skripts für gemeinsam genutzte Docker-Umgebungen.
- Kapitel 20 Skripterstellung für die Bereitstellung einer Full-Stack-Anwendung.
- Kapitel 21 Ein Skript zur Inspektion und Formatierung von Container-Informationen.
- Kapitel 22 Automatisierung des Taggens und Pushens von Images in eine Registry.
- Kapitel 23 Skripterstellung für die sichere Handhabung von Secrets in Docker.
- Kapitel 24 Ein Skript zum ordnungsgemäßen Stoppen und Entfernen von Containern.
- Kapitel 25 Fortgeschrittene Skripterstellung: Kombinieren von Docker-Befehlen für komplexe Workflows.
Nützliche Docker-Skripte
Inhaltsverzeichnis
Einführung
Es gibt einen eindeutigen Moment in der Reise jedes Docker-Nutzers, der sich wie eine kleine Offenbarung anfühlt. Er tritt oft ein, nachdem man docker run mit einem Dutzend Flags zum zwanzigsten Mal in einer Stunde eingetippt hat, oder nachdem man mühsam eine Folge von docker ps-, docker stop- und docker rm-Befehlen ausgeführt hat, um eine lokale Umgebung aufzuräumen. Es ist der Moment, in dem man sich in seinem Stuhl zurücklehnt, den Eingabeaufforderung anstarrt und denkt: „Da muss es doch einen effizienteren Weg geben, das zu erledigen.“ Sie haben natürlich absolut recht. Der Weg von sich wiederholenden manuellen Befehlen hin zu optimierter Effizienz ist mit Skripten gepflastert.
Docker hat grundlegend verändert, wie wir Software bauen, versenden und ausführen. Es bietet ein unglaubliches Versprechen: eine konsistente, isolierte und portable Umgebung für jede Anwendung. Indem es eine Anwendung und ihre Abhängigkeiten in einer einzelnen, standardisierten Einheit – einem Container – verpackt, löst Docker das klassische „Bei mir funktioniert es“-Problem, das Entwickler seit Jahrzehnten plagt. Dieser Containerisierungsansatz bietet eine leichte Alternative zu herkömmlichen virtuellen Maschinen, indem er das Betriebssystem virtualisiert statt der zugrunde liegenden Hardware. Diese Effizienz bedeutet, dass Sie mehrere Container auf einem einzelnen Host ausführen können, jeder mit seinem eigenen isolierten Satz an Prozessen und Ressourcen.
Die Stärke von Docker liegt in seiner Einfachheit und seiner robusten Befehlszeilenschnittstelle (CLI). Mit wenigen Befehlen können Sie ein Image aus einer Registry wie Docker Hub herunterladen, es als Container ausführen und haben eine komplexe Anwendung – wie eine Datenbank oder einen Webserver – in Sekundenschnelle einsatzbereit. Das ist ein gewaltiger Sprung nach vorn gegenüber den Zeiten manueller Installationen und komplexem Abhängigkeitsmanagement. Doch sich ausschließlich auf manuell eingetippte Befehle zu verlassen, ist, als besäße man einen Sportwagen und führe ihn ausschließlich im ersten Gang. Man erlebt die Kernfunktion, verpasst aber die Geschwindigkeit, Kraft und Eleganz, zu der er wirklich fähig ist.
Hier kommt das Skripting ins Spiel. Ein Skript ist im Kern eine in einer Datei gespeicherte Reihe von Anweisungen, die von einem Programm ausgeführt werden sollen. Für Docker-Nutzer bedeutet dies meist Shell-Skripte (wie Bash) oder Skripte, die in einer höheren Sprache wie Python geschrieben sind. Diese Skripte nehmen die einzelnen, atomaren Befehle der Docker-CLI und verweben sie zu leistungsstarken, automatisierten Workflows. Sie verwandeln mehrstufige, fehleranfällige manuelle Prozesse in einen einzigen, zuverlässigen Befehl. Sie sind der Schlüssel, um das volle Potenzial Ihrer Docker-Umgebung freizusetzen.
Denken Sie an die Routineaufgaben, die Sie täglich erledigen. Das Starten einer bestimmten Entwicklungsumgebung mit mehreren verknüpften Containern. Das Sichern eines kritischen Datenbank-Volumes. Das Aufräumen ungenutzter Images und Container, um Speicherplatz zurückzugewinnen. Das Aktualisieren einer laufenden Anwendung auf eine neue Image-Version ohne Ausfallzeiten. Jeder dieser Workflows beinhaltet eine Abfolge von Docker-Befehlen, und jeder ist ein perfekter Kandidat für Automatisierung durch Skripting. Ein gut geschriebenes Skript stellt sicher, dass diese Aufgaben jedes einzelne Mal konsistent ausgeführt werden, und eliminiert das Risiko menschlicher Fehler – wie das Vergessen eines Kommandozeilen-Flags oder das Vertippen eines Containernamens.
Dieses Buch entstand aus dem praktischen Bedarf an dieser Automatisierung. Zwar gibt es unzählige Ressourcen, die Ihnen die Grundlagen von Docker beibringen – was ein Image ist, wie man ein Dockerfile schreibt, welchen Zweck ein Container erfüllt –, doch viele hören dort auf. Sie geben Ihnen die Bausteine, zeigen aber nicht immer, wie man sie zu etwas Größerem zusammenfügt. Dieses Buch ist anders. Es ist nicht nur ein weiterer Leitfaden zu Docker-Befehlen; es ist ein praktisches, anwendungsorientiertes Kompendium nützlicher Skripte, die darauf ausgelegt sind, reale Probleme zu lösen und Ihr Leben als Entwickler, Systemadministrator oder DevOps-Ingenieur erheblich zu erleichtern.
Wir gehen über die grundlegenden docker run- und docker build-Befehle hinaus und erforschen die Kunst des Skriptings. Dieses Buch ist eine Sammlung von Rezepten, ein Werkzeugkasten aus einsatzbereiten Lösungen für die häufigen – und manchmal unüblichen – Herausforderungen, denen Sie bei der Arbeit mit Docker begegnen. Jedes Kapitel widmet sich einer spezifischen Aufgabe und liefert ein vollständiges, funktionierendes Skript, das Sie sofort verwenden können. Noch wichtiger: Wir werden zerlegen, wie jedes Skript funktioniert, und die Befehle, die Logik und die verwendeten Techniken erklären, damit Sie die Skripte nicht nur nutzen, sondern auch verstehen, anpassen und erweitern können, um sie Ihren individuellen Anforderungen anzupassen.
Für wen ist dieses Buch also gedacht? Es ist für den Entwickler, der eine konsistente, per Einzeiler aufrufbare Einrichtung für seine lokale Entwicklungsumgebung erstellen möchte. Es ist für den Systemadministrator, der Docker-Hosts verwalten und routinemäßige Wartungsaufgaben wie Backups und Aufräumen automatisieren muss. Es ist für den DevOps-Profi, der robuste Continuous-Integration- und Continuous-Deployment-Pipelines (CI/CD) baut. Im Grunde ist dieses Buch für jeden, der diesen Moment des „Da muss es doch einen besseren Weg geben“ erlebt hat und bereit ist, Automatisierung zu umarmen.
Die einzige Voraussetzung ist ein grundlegendes Verständnis von Docker. Sie sollten mit Kernkonzepten wie Images, Containern, Volumes und Netzwerken vertraut sein. Sie sollten Docker installiert haben und etwas Erfahrung mit der Ausführung grundlegender Befehle mitbringen. Sie müssen jedoch kein Skripting-Guru sein. Wir beginnen mit einfachen Shell-Skripten und führen schrittweise fortgeschrittenere Techniken ein und tauchen sogar in die Verwendung der Docker-API mit Python ein, um sicherzustellen, dass der Stoff unabhängig von Ihrer aktuellen Skripting-Kenntnis zugänglich ist.
Im Laufe der Kapitel dieses Buches begeben wir uns auf eine Reise vom Einfachen zum Komplexen. Wir beginnen damit, ein solides Fundament zu legen, indem wir wesentliche Befehle und die Grundlagen des Bauens von Images mit einem Dockerfile sowie der Verwaltung von Multi-Container-Setups mit Docker Compose abdecken. Diese frühen Kapitel schaffen die Basis und stellen sicher, dass Sie die notwendigen Bausteine zur Hand haben, bevor wir anfangen, sie zu automatisierten Workflows zusammenzufügen. Sie dienen als Auffrischung und Basis für die kommende Skripting-Arbeit.
Von dort aus tauchen wir kopfüber in die Welt der Automatisierung ein. Sie lernen, wie man ein Skript schreibt, das automatisch eine neue Version eines Images herunterlädt und einen Container neu startet, um ein Update abzuschließen – ein Eckpfeiler der Wartung laufender Anwendungen. Wir werden die Erstellung generischer Helfer-Skripte erkunden, die eine breite Palette häufiger Aufgaben vereinfachen und Ihre Kommandozeilen-Interaktionen mit Docker mächtiger und weniger gesprächig machen. Diese Skripte sind darauf ausgelegt, praktische Werkzeuge zu sein, die Sie sofort in Ihren Workflow integrieren können.
Ein erheblicher Teil unserer Reise wird der feinen Kunst des Ressourcenmanagements gewidmet. Docker-Umgebungen, insbesondere solche, die intensiv für Entwicklung und Tests genutzt werden, können sich schnell mit ungenutzten Images, Volumes und gestoppten Containern füllen und wertvollen Speicherplatz verbrauchen. Wir stellen Skripte bereit und zerlegen diese, die speziell darauf ausgelegt sind, diesen Ballast sicher und effizient zu beseitigen. Sie lernen, wie man das Entfernen alter Images automatisiert und wie man ein Skript implementiert, das intelligent nur die Ressourcen entfernt, die nicht mehr benötigt werden.
Daten sind das Lebensblut vieler Anwendungen, und ihre Verwaltung in einer containerisierten Welt bringt eigene Herausforderungen mit sich. Wir werden dies frontal angehen mit Kapiteln zum Skripting der Verwaltung von Docker-Volumes. Sie lernen, wie man robuste Skripte schreibt, um eine der wichtigsten administrativen Aufgaben zu erledigen: das Sichern Ihrer wichtigen Daten aus einem Volume und – genauso wichtig – deren Wiederherstellung. Dies bietet ein Sicherheitsnetz und ist eine unverzichtbare Fähigkeit für jeden, der zustandsbehaftete Anwendungen in Docker betreibt.
Mit wachsender Kompetenz werden wir in fortgeschrittenere Gebiete vordringen. Wir werden uns anspruchsvolle Dockerfile-Techniken ansehen, die Ihnen helfen, kleinere, sicherere und effizientere Images zu erstellen. Ein optimiertes Image baut schneller, verbraucht weniger Speicherplatz und bietet eine geringere Angriffsfläche – alles entscheidende Faktoren in einer Produktionsumgebung. Wir zeigen Ihnen, wie Sie Ihre Dockerfiles strukturieren, um den Layer-Caching-Mechanismus von Docker für schnellere Builds voll auszuschöpfen.
Automatisierung endet nicht an Ihrer lokalen Maschine, und moderne Softwareentwicklung ist tief mit CI/CD-Plattformen verwoben. Ein eigenes Kapitel führt Sie durch den Prozess der Verwendung von GitHub Actions, um das Bauen und Hochladen Ihrer Docker-Images in eine Registry zu automatisieren. Dies schließt die Lücke zwischen Ihrem Code-Repository und Ihrer Deployment-Umgebung und schafft eine nahtlose Pipeline vom Commit zum Container-Image.
Wir werden auch untersuchen, wie Skripte speziell für Entwicklungsumgebungen zugeschnitten werden können. Dazu gehören Skripte, die komplexe Anwendungsstapel einrichten, verschiedene Docker-Kontexte für das Wechseln zwischen lokalen und entfernten Docker-Daemons verwalten und die Erstellung benutzerdefinierter Netzwerke skripten, um sicherzustellen, dass Ihre Container so kommunizieren können, wie es Ihre Anwendung erfordert. Diese Skripte helfen, Konsistenz in einem Entwicklerteam durchzusetzen und stellen sicher, dass jeder in einer identischen Umgebung arbeitet.
Die Interaktion mit Containern hört nicht auf, sobald sie laufen. Wir werden untersuchen, wie man Healthchecks skriptet, um den Status Ihrer Anwendungen zu überwachen, und wie man benutzerdefinierte Entrypoint-Skripte erstellt, die Ihre Container zur Laufzeit flexibler und konfigurierbarer machen. Sie lernen sogar, wie Sie die CLI umgehen und direkt mit der Docker Engine über ihre API interagieren, was eine völlig neue Welt der Automatisierungsmöglichkeiten mit Sprachen wie Python eröffnet.
Sicherheit ist in jeder Umgebung ein vorrangiges Anliegen, und Docker bildet da keine Ausnahme. Ein eigenes Kapitel wird sich auf das Skripting des sicheren Umgangs mit Secrets konzentrieren, ein kritischer Aspekt beim Bereitstellen von Anwendungen, die eine Verbindung zu Datenbanken oder anderen geschützten Diensten benötigen. Wir werden verschiedene Strategien betrachten, wie man sensible Informationen an Container weitergibt, ohne sie in Images oder Skripte einzubacken – ein häufiges, aber gefährliches Anti-Pattern.
Schließlich führen wir alles zusammen, indem wir komplexe, reale Workflows angehen. Wir gehen durch das Skripting der Bereitstellung einer Full-Stack-Anwendung – vom Bauen der Images bis zum Starten der vernetzten Dienste. Wir decken auch Skripte für administrative Aufgaben ab, wie die Verwaltung von Benutzern in einer geteilten Docker-Umgebung, das ordnungsgemäße Stoppen und Entfernen von Containern, um saubere Shutdowns zu gewährleisten, und die Erstellung benutzerdefinierter Tools zur Inspektion und Formatierung von Container-Informationen, um genau die Ausgabe zu erhalten, die Sie benötigen.
Die Philosophie hinter den Skripten in diesem Buch ist eine der Praktikabilität und Anpassungsfähigkeit. Sie sind so klar und unkompliziert wie möglich geschrieben, mit Kommentaren, die die komplexeren Teile erklären. Sie sind jedoch nicht als unveränderliche Artefakte gedacht. Sie werden ermutigt, sie zu nehmen, zu modifizieren, zu brechen und neu aufzubauen, um sie perfekt an Ihre Bedürfnisse anzupassen. Das Ziel ist nicht nur, Ihnen eine Bibliothek von Skripten an die Hand zu geben, sondern Sie mit dem Wissen zu befähigen, Ihre eigenen zu erstellen.
Um das Meiste aus diesem Buch herauszuholen, ermutige ich Sie, ein aktiver Leser zu sein. Lesen Sie nicht nur den Text; starten Sie Ihr Terminal und führen Sie die Skripte aus. Experimentieren Sie mit den Befehlen und versuchen Sie, den Code zu modifizieren, um zu sehen, was passiert. Der beste Weg, Skripting zu lernen, ist, es zu tun, und der beste Weg, ein Werkzeug zu verstehen, ist, es zu benutzen. Richten Sie eine sichere, lokale Docker-Umgebung ein, in der Sie frei experimentieren können, ohne Angst zu haben, etwas Kritisches zu beschädigen.
Die Reise vom manuellen, befehl für befehl arbeitenden Docker-Nutzer zum Meister der Automatisierung ist eine lohnende. Sie wird Ihnen Zeit sparen, Fehler reduzieren und Ihnen letztlich ermöglichen, sich auf die interessanteren und kreativeren Aspekte Ihrer Arbeit zu konzentrieren. Die Fähigkeit, einen komplexen Prozess in ein einzelnes, ausführbares Skript zu kapseln, ist eine Superkraft in der Welt der Softwareentwicklung und -operation.
Dieses Buch ist Ihr Leitfaden zur Entwicklung dieser Superkraft. Es ist Zeit, über das lästige repetitive Tippen hinauszugehen und die Eleganz der Automatisierung zu umarmen. Hören wir auf zu tippen und fangen an zu skripten.
KAPITEL EINS: Erste Schritte mit Docker: Wesentliche Befehle.
Bevor man eine Sinfonie komponieren kann, muss man zuerst die einzelnen Noten beherrschen. In der Welt der Docker-Automatisierung sind unsere Noten die Befehle der Kommandozeilenschnittstelle (CLI). Dies sind die fundamentalen Verben, die es uns ermöglichen, unsere Absichten dem Docker-Daemon mitzuteilen, der Engine, die unsere containerisierte Welt antreibt. Während das endgültige Ziel dieses Buches darin besteht, diese Befehle zu eleganten und leistungsstarken Skripten zu verweben, muss diese Reise mit einem soliden Verständnis der Befehle selbst beginnen. Dieses Kapitel ist Ihr Werkzeugkasten, eine geführte Tour durch die wesentlichen Werkzeuge, nach denen Sie immer wieder greifen werden. Diese Befehle zu beherrschen ist der erste und wichtigste Schritt, um in Docker und folglich in seiner Automatisierung versiert zu werden.
Wir werden den vollständigen Lebenszyklus eines Containers erkunden, von der Suche nach dem richtigen Softwarepaket in einer öffentlichen Registry bis hin zu dessen Ausführung, Untersuchung, Zustandsverwaltung und schließlich seiner Bereinigung. Jeder Befehl ist ein Baustein. Zu verstehen, nicht nur was ein Befehl tut, sondern auch seine verschiedenen Optionen und den Kontext, in dem er am effektivsten ist, ist von größter Bedeutung. Dies sind die Werkzeuge, die Sie verwenden werden, um die ausgeklügelten automatisierten Workflows in den folgenden Kapiteln zu bauen. Betrachten Sie dies als Ihr Grundlagentraining, um sicherzustellen, dass Ihr Vokabular stark ist, bevor Sie anfangen, Poesie zu schreiben.
Unsere Reise beginnt dort, wo die meisten Docker-Workflows beginnen: mit einem Image. Ein Image ist ein Bauplan, eine schreibgeschützte Vorlage, die die Anwendung, ihre Bibliotheken und ihre Abhängigkeiten enthält. Diese Images werden typischerweise in einer Registry gespeichert, einem Repository zum Speichern und Verteilen von ihnen. Die gängigste und öffentliche Registry ist Docker Hub. Das Erste, was Sie oft tun müssen, ist, das offizielle Image für die Software zu finden, die Sie verwenden möchten. Dafür ist der Befehl docker search ein nützlicher, wenn auch grundlegender Ausgangspunkt. Er fragt Docker Hub nach Images ab, die Ihrem Suchbegriff entsprechen.
Wenn Sie beispielsweise Images finden möchten, die mit dem beliebten Webserver Nginx zusammenhängen, würden Sie ausführen:
docker search nginx
Die Ausgabe liefert eine Liste von Images mit Spalten für den Namen, eine kurze Beschreibung, die Anzahl der Sterne (ein grobes Beliebtheitsmaß) und ob das Image offiziell ist. Offizielle Images sind ein kuratierter Satz von Repositories von Docker, der einen geprüften und gut dokumentierten Ausgangspunkt für gängige Software bietet. Es ist fast immer Best Practice, das offizielle Image zu verwenden, wenn eines verfügbar ist, da diese mit Blick auf Sicherheit und Stabilität gewartet werden.
Sobald Sie das Image identifiziert haben, das Sie verwenden möchten, wie das offizielle nginx-Image, müssen Sie es von der Registry auf Ihren lokalen Rechner holen. Dies ist die Aufgabe des Befehls docker pull. Dieser Befehl lädt die Image-Layer herunter, die erforderlich sind, um das vollständige Image auf Ihrem System zusammenzusetzen. Die Syntax ist unkompliziert und erfordert den Namen des Images und optional einen Tag. Ein Tag wird verwendet, um ein Image zu versionieren, wie 1.21 oder latest. Wenn kein Tag angegeben wird, verwendet Docker standardmäßig den als latest getaggten.
Um das offizielle Nginx-Image herunterzuladen, würden Sie ausführen:
docker pull nginx
Dies entspricht docker pull nginx:latest. Um eine bestimmte Version herunterzuladen, würden Sie den Tag so angeben: docker pull nginx:1.21. Die Verwendung spezifischer Versionstags anstelle von latest ist eine entscheidende Praxis für die Erstellung reproduzierbarer Umgebungen und zuverlässiger Skripte. Der latest-Tag kann vom Image-Verwalter jederzeit aktualisiert werden, was bedeutet, dass sich Ihr Skript von einem Tag auf den anderen anders verhalten könnte. Das Festlegen auf eine bestimmte Version stellt sicher, dass Sie immer mit exakt derselben Software arbeiten. Nach dem Herunterladen können Sie mit dem Befehl docker images alle Images auf Ihrem lokalen Rechner anzeigen.
Mit einem auf Ihrem Rechner heruntergeladenen Image haben Sie den Bauplan. Der nächste logische Schritt ist, diesen Bauplan zu nutzen, um etwas Reales zu konstruieren: einen Container. Ein Container ist eine ausführbare Instanz eines Images. Der Befehl zum Erstellen und Starten eines Containers ist docker run, wohl der wichtigste und funktionsreichste Befehl in der Docker-CLI. Seine Grundform ist einfach, aber seine Kraft liegt in der Vielzahl von Flags, die verwendet werden können, um zu konfigurieren, wie der Container läuft.
Die einfachste mögliche Ausführung dieses Befehls erfolgt mit einem Image wie hello-world, das genau zu diesem Zweck entwickelt wurde.
docker run hello-world
Wenn Sie dies ausführen, entfaltet sich eine Abfolge von Ereignissen. Docker prüft, ob das hello-world-Image auf Ihrem lokalen System vorhanden ist. Wenn nicht, zieht es es automatisch von Docker Hub. Es erstellt dann einen neuen Container aus diesem Image. Es führt den im Image eingebetteten Befehl aus, der in diesem Fall eine freundliche Nachricht in Ihr Terminal druckt. Schließlich stoppt der Container, sobald der Befehl beendet ist. Dieses einfache Beispiel demonstriert die Kernfunktion des Erstellens und Ausführens eines Containers.
Für die meisten realen Anwendungen benötigen Sie jedoch mehr Kontrolle. Sie möchten Container im Hintergrund ausführen, mit ihnen interagieren, sie für Ihr Netzwerk erreichbar machen und ihnen persistente Daten geben. Hier kommen die Flags für docker run ins Spiel. Lassen Sie uns zunächst zwischen zwei primären Betriebsmodi unterscheiden: interaktiv und getrennt (detached).
Ein interaktiver Container ist einer, mit dem Sie Ihr Terminal verbinden, sodass Sie Befehle direkt darin ausführen können. Dies wird mit den Flags -it erreicht. Das -i-Flag hält die Standard-Eingabe (STDIN) offen, und das -t-Flag weist ein Pseudo-TTY zu, was Ihnen im Wesentlichen eine Befehlszeile innerhalb des Containers gibt. Dies ist perfekt, um ein Image zu erkunden oder eine einmalige Aufgabe auszuführen. Um beispielsweise einen Ubuntu-Container auszuführen und eine Bash-Shell darin zu erhalten, würden Sie verwenden:
docker run -it ubuntu /bin/bash
Ihr Terminal-Prompt würde sich ändern, und Sie würden nun Befehle innerhalb des Ubuntu-Containers ausführen. Dies ist unglaublich nützlich für Debugging oder Entwicklung. Wenn Sie exit eingeben, endet der Bash-Prozess, und der Container stoppt.
Der alternative Modus ist der getrennte Modus (detached mode), der mit dem -d-Flag angegeben wird. Dies ist das Arbeitstier für die Ausführung langlebiger Dienste wie Webserver, Datenbanken oder APIs. Wenn Sie einen Container im getrennten Modus ausführen, startet Docker den Container und gibt Ihnen dann Ihr Host-Terminal zurück, wobei der Container im Hintergrund weiterläuft. Der Befehl gibt die eindeutige ID des neu erstellten Containers als Bestätigung aus. Um beispielsweise einen Nginx-Webserver im Hintergrund zu starten:
docker run -d nginx
Sie haben nun einen laufenden Webserver, aber wie greifen Sie darauf zu? Standardmäßig sind Container vom Netzwerk des Hosts isoliert. Um einen Dienst, der in einem Container läuft, erreichbar zu machen, müssen Sie einen Port vom Host-Rechner auf einen Port im Container abbilden, indem Sie das -p-Flag verwenden. Das Format ist -p [host_port]:[container_port]. Wenn wir auf unseren Nginx-Server auf Port 8080 unseres lokalen Rechners zugreifen möchten, bilden wir ihn auf Port 80 im Container ab, was der Standardport ist, auf dem Nginx lauscht.
docker run -d -p 8080:80 nginx
Wenn Sie nun einen Webbrowser öffnen und zu http://localhost:8080 navigieren, sehen Sie die standardmäßige Nginx-Willkommensseite, die aus Ihrem Container heraus bedient wird. Dieses Port-Mapping ist fundamental, um containerisierte Anwendungen nutzbar zu machen.
Wenn Sie einen Container ausführen, weist Docker ihm einen zufälligen, oft skurril generierten Namen zu, wie vigilant_murdock oder dreamy_bassi. Zwar amüsant, sind diese Namen für die Automatisierung nicht hilfreich. Wenn Sie in einem Skript zuverlässig auf einen bestimmten Container verweisen möchten, müssen Sie ihm einen vorhersagbaren Namen geben. Das --name-Flag ermöglicht Ihnen genau dies. Ein gut benannter Container ist der erste Schritt zu einer verwaltbaren Umgebung.
docker run -d -p 8080:80 --name my-web-server nginx
Jetzt können Sie, anstatt seinen zufälligen Namen oder seine ID finden zu müssen, diesen Container in nachfolgenden Befehlen einfach als my-web-server ansprechen, was weitaus bequemer und für das Schreiben robuster Skripte essenziell ist.
Ein weiteres kritisches Konzept ist die Datenspeicherung. Das Dateisystem innerhalb eines Containers ist standardmäßig flüchtig (ephemeral). Das bedeutet, dass alle Daten, die darin erstellt oder geändert wurden, für immer verloren sind, wenn Sie den Container entfernen. Dies ist in Ordnung für zustandslose Anwendungen, aber katastrophal für alles, das einen Zustand beibehalten muss, wie eine Datenbank. Docker löst dies mit Volumes. Ein Volume ist ein Mechanismus zum Persistieren von Daten, die von Docker-Containern erzeugt und verwendet werden. Wir werden in Kapitel 13 tief in das Volume-Management per Skript eintauchen, aber die grundlegende Verwendung mit docker run ist jetzt bereits essenziell zu kennen.
Das -v-Flag wird verwendet, um ein Volume in einen Container einzubinden. Sie können entweder Docker das Volume verwalten lassen (ein benanntes Volume) oder ein bestimmtes Verzeichnis von Ihrem Host-Rechner in den Container einbinden (ein Bind Mount). Für eine MySQL-Datenbank möchten Sie möglicherweise das Datenverzeichnis persistieren:
docker run -d --name my-database -e MYSQL_ROOT_PASSWORD=mysecretpassword -v mysql-data:/var/lib/mysql mysql
In diesem Befehl haben wir auch das -e-Flag eingeführt, das eine Umgebungsvariable innerhalb des Containers setzt. Dies ist eine sehr gängige Methode, um Konfigurationsdetails wie Passwörter oder Einstellungen an die im Container laufende Anwendung zu übergeben. Der Teil -v mysql-data:/var/lib/mysql weist Docker an, ein benanntes Volume namens mysql-data zu erstellen (falls es noch nicht existiert) und es an den Pfad /var/lib/mysql im Container einzubinden. Jetzt bleibt das mysql-data-Volume selbst dann erhalten, wenn Sie den my-database-Container entfernen, und Sie können es an einen neuen Container anhängen, um Ihre Arbeit fortzusetzen.
Schließlich für viele temporäre Aufgaben, wie das Ausführen einer Testsuite oder eines Build-Prozesses, erstellen Sie einen Container, er führt seine Aktion aus und stoppt dann. Standardmäßig existiert dieser gestoppte Container weiterhin auf Ihrem System. Mit der Zeit kann dies zu einer erheblichen Menge an Ballast führen. Das --rm-Flag weist Docker an, den Container automatisch zu entfernen, sobald er beendet wird. Dies ist eine fantastische Aufräumfunktion.
docker run --rm -it ubuntu echo "This container will disappear when I'm done."
Sobald der echo-Befehl abgeschlossen ist, wird der Container sofort und automatisch entfernt, was Ihr System sauber hält. Die Verwendung von --rm für kurzlebige Single-Task-Container ist eine Best Practice, die die Ansammlung gestoppter Container verhindert.
Sobald Sie Container ausgeführt haben, insbesondere im getrennten Modus, benötigen Sie eine Möglichkeit zu sehen, was vor sich geht. Der primäre Befehl dafür ist docker ps. Allein listet dieser Befehl alle laufenden Container auf Ihrem System auf. Die Ausgabe ist eine übersichtliche Tabelle, die mehrere nützliche Informationen anzeigt: die ID des Containers, das Image, aus dem er erstellt wurde, den Befehl, den er ausführt, wann er erstellt wurde, seinen aktuellen Status, eventuelle Port-Mappings und seinen Namen.
Diese Ansicht ist entscheidend, um einen schnellen Überblick über Ihre aktive Umgebung zu erhalten. Sie erzählt jedoch nur einen Teil der Geschichte. Um alle Container zu sehen, einschließlich derer, die gestoppt oder beendet wurden, müssen Sie das -a-Flag hinzufügen.
docker ps -a
Dieser Befehl ist Ihre Anlaufstelle für ein vollständiges Inventar. Sie werden Container sehen, die erfolgreich beendet wurden (Status Exited (0)) und solche, die fehlgeschlagen sind (z. B. Exited (1)). Dies ist oft der erste Schritt beim Debuggen, warum ein Container nicht wie erwartet läuft.
Für Skripting-Zwecke ist die standardmäßige tabellarische Ausgabe von docker ps nicht immer ideal. Sie ist für menschliche Augen konzipiert. Wenn Sie eine Liste von Containern an einen anderen Befehl übergeben müssen, wollen Sie oft nur die IDs. Das -q-Flag (quiet) ändert die Ausgabe so, dass nur die Container-IDs angezeigt werden, eine pro Zeile. Um beispielsweise eine Liste aller Container-IDs zu erhalten, laufend oder gestoppt:
docker ps -a -q
Diese Ausgabe kann direkt in anderen Befehlen verwendet werden. Ein gängiges Muster zum Entfernen aller gestoppten Container ist beispielsweise die Verwendung von Befehlssubstitution: docker rm $(docker ps -a -f "status=exited" -q). Hier haben wir auch das -f-Flag (oder --filter) eingeführt, mit dem Sie die Liste basierend auf bestimmten Kriterien eingrenzen können, in diesem Fall dem Status des Containers. Diese Kombination aus Filtern und leiser Ausgabe ist eine mächtige Technik, die die Basis vieler Aufräum-Skripte bildet.
Dass ein Container läuft, zu wissen, ist oft nicht genug. Sie müssen seine Ausgabe sehen oder mit ihm interagieren. Für getrennte Container, deren Ausgabe nicht mit Ihrem Terminal verbunden ist, ist der Befehl docker logs essenziell. Er ruft die Logs (Standardausgabe und Standardfehler) von einem Container ab.
docker logs my-web-server
Dies zeigt alle Logs an, die der Nginx-Container seit seinem Start generiert hat. Für einen kontinuierlich laufenden Dienst möchten Sie die Logs möglicherweise in Echtzeit beobachten, ähnlich dem Befehl tail -f unter Linux. Das -f-Flag (follow) für docker logs macht genau das.
docker logs -f my-web-server
Ihr Terminal streamt nun die Logs, während sie generiert werden, was für Live-Monitoring und Debugging unschätzbar wertvoll ist.
Manchmal müssen Sie mehr tun, als nur Logs anzusehen; Sie müssen einen Befehl in einem Container ausführen, der bereits läuft. Sie möchten beispielsweise eine Konfigurationsdatei in Ihrem laufenden Nginx-Container inspizieren oder den Inhalt einer Datenbank prüfen. Dies ist die Aufgabe des Befehls docker exec. Er ermöglicht es Ihnen, einen Befehl in einem laufenden Container zu "executen" (auszuführen).
Um eine interaktive Shell in unseren laufenden my-web-server-Container zu erhalten, würden Sie verwenden:
docker exec -it my-web-server /bin/bash
Dies sieht dem Befehl docker run -it sehr ähnlich, aber der entscheidende Unterschied ist, dass docker exec sich an einen bereits existierenden, laufenden Container anhängt, während docker run einen neuen erstellt. Die Verwendung von exec ist der Standardweg, um administrative Aufgaben an einem laufenden Dienst durchzuführen, ohne ihn stoppen und neu starten zu müssen.
Der Lebenszyklus eines Containers besteht nicht nur aus der Erstellung; er umfasst auch die Steuerung. Sie werden häufig Ihre Container stoppen, starten und neu starten müssen. Die Befehle für diese Aktionen sind unkompliziert: docker stop, docker start und docker restart. Der Befehl docker stop fährt einen Container ordnungsgemäß herunter, indem er zuerst ein SIGTERM-Signal sendet, was der Anwendung erlaubt, einen sauberen Shutdown durchzuführen. Wenn er nicht innerhalb einer Karenzzeit (typischerweise 10 Sekunden) stoppt, folgt Docker mit einem SIGKILL-Signal, um ihn zwangsweise zu beenden.
docker stop my-web-server
Einmal gestoppt, taucht der Container nicht mehr in der standardmäßigen docker ps-Ausgabe auf, existiert aber weiterhin und ist mit docker ps -a sichtbar. Um ihn wieder zum Leben zu erwecken, verwenden Sie docker start.
docker start my-web-server
Der Container setzt mit seiner vorherigen Konfiguration fort, einschließlich seines Namens und aller angehängten Volumes. Der Befehl docker restart ist lediglich eine praktische Abkürzung, die einen Stop gefolgt von einem Start ausführt, was nützlich ist, um Konfigurationsänderungen anzuwenden, die einen Dienst-Neustart erfordern.
Eine gut gewartete Docker-Umgebung ist eine saubere. Mit der Zeit können gestoppte Container und ungenutzte Images ansammeln und erheblichen Speicherplatz verbrauchen. Wir haben bereits das --rm-Flag für docker run gesehen, aber Sie benötigen auch Befehle, um bestehende Ressourcen manuell aufzuräumen. Der Befehl docker rm wird verwendet, um einen oder mehrere gestoppte Container zu entfernen.
docker rm [container_id_or_name]
Sie können einen laufenden Container nicht entfernen, es sei denn, Sie verwenden das -f-Flag (force), aber der sauberere Ansatz ist immer, ihn zuerst zu stoppen und dann zu entfernen. Sie können auch eine Liste von Container-IDs übergeben, um mehrere Container auf einmal zu entfernen – eine Technik, die, wie erwähnt, perfekt für Skripting geeignet ist.
Ähnlich können Images noch mehr Speicherplatz beanspruchen als Container. Der Befehl docker rmi wird verwendet, um ein oder mehrere Images zu entfernen. Ein Image kann nicht entfernt werden, wenn es derzeit von einem Container verwendet wird, selbst einem gestoppten. Sie müssen die abhängigen Container zuerst entfernen, bevor Sie das Image entfernen können, auf dem sie basieren.
docker rmi nginx:1.21
Diese manuellen Aufräum-Befehle sind effektiv, aber sie sind auch ideale Kandidaten für Automatisierung. Spätere Kapitel zeigen Ihnen, wie Sie Skripte erstellen, die ungenutzte Ressourcen intelligent finden und entfernen, was Ihnen Zeit und Speicherplatz spart.
Schließlich gibt es einen Befehl, der als universelles Werkzeug für tiefe Inspektion fungiert: docker inspect. Dieser Befehl kann auf Container, Images, Volumes und Netzwerke angewendet werden. Er gibt ein detailliertes JSON-Array zurück, das eine Fülle von Informationen über das angegebene Docker-Objekt enthält. Die Ausgabe kann auf den ersten Blick überwältigend sein, aber sie ist eine unglaublich mächtige Ressource für Automatisierung.
docker inspect my-web-server
Dies druckt ein großes JSON-Objekt auf den Bildschirm, das alles über den Container beschreibt: seinen Zustand, seine Konfiguration, seine Netzwerkeinstellungen (einschließlich seiner internen IP-Adresse), seine Volume-Einbindungen und vieles mehr.
Die wahre Macht von docker inspect für das Skripting wird durch sein --format-Flag freigesetzt. Dieses Flag ermöglicht es Ihnen, ein Go-Template anzugeben, um bestimmte Informationen aus der JSON-Ausgabe zu extrahieren. Anstatt eine riesige JSON-Datei mit einem Werkzeug wie jq zu parsen, können Sie Docker bitten, Ihnen genau den Datenpunkt zu geben, den Sie benötigen. Um beispielsweise nur die interne IP-Adresse eines Containers zu erhalten:
docker inspect --format='{{.NetworkSettings.IPAddress}}' my-web-server
Diese Fähigkeit, den Zustand und die Konfiguration jedes Docker-Objekts präzise abzufragen, macht docker inspect zu einem Eckpfeiler fortgeschrittenen Skriptings. Sie können es verwenden, um dynamische Informationen wie die IP-Adresse eines Containers abzurufen und sie in andere Befehle oder Konfigurationsdateien einzuspeisen, was echte dynamische und intelligente Automatisierung schafft.
Diese Befehle – pull, run, ps, logs, exec, stop, rm, rmi und inspect – bilden den Kernwortschatz von Docker. Sie sind die wesentlichen Werkzeuge, die Sie täglich verwenden werden. Indem Sie ihre Funktionen und Optionen verstehen, haben Sie das notwendige Fundament gebaut. Sie können nun den gesamten Lebenszyklus eines Containers manuell verwalten. Mit diesem soliden Verständnis der einzelnen Bausteine sind wir nun bereit, den nächsten Schritt zu gehen: unsere eigenen Baupläne für Anwendungen zu definieren, indem wir lernen, wie man ein Dockerfile erstellt.
This is a sample preview. The complete book contains 27 sections.