Docker einrichten
Im folgenden Kapitel erfährst du, wie Docker auf den gängigen Betriebssystemen installiert wird.
Docker auf Linux installieren
Möglichkeit 1 - Per Installationsskript
Am einfachsten und sichersten kannst du Docker auf deinem Linux-Rechner per Installationsskript einrichten. Lade das Installationsskript einfach mit folgendem Befehl per cURL auf deinen Rechner:
$ curl -fsSL https://get.docker.com -o get-docker.sh
Solltest du noch kein cURL auf deinem Rechner installiert haben kannst du es per apt herunterladen. Hierfür solltest du zunächst schauen, ob dein apt-system aktuell ist und im Anschluss die Installation von cURL ausführen:
$ sudo apt-get update
$ sudo apt-get install curl
Wenn du nun per $ ls
dein aktuelles Verzeichnis durchsuchst solltest du das Installationsskript (get-docker.sh) entdecken. Führe dies zur Installation nun mit Root-Rechten aus:
$ sudo sh get-docker.sh
Nun sollte Docker installiert sein, damit du auch als normaler User Docker benutzen kannst, füge den aktuellen Benutzer zur Gruppe Docker hinzu (anschließend einmal ab- und anmelden dass Änderungen übernommen werden):
$ sudo usermod -aG docker $USER
Als letzten Schritt solltest du noch Docker Compose installieren, dies wird benötigt, um später Docker-Setups mit vielen Containern zu betreiben (Aktuelle Version beachten).
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname-s)-$(uname -m)" -o /usr/local/bin/docker-compose
Dass Compose später auch problemlos ausführbar ist empfiehlt es sich vor erster Benutzung noch die Zugriffsrechte zu ändern:
$ sudo chmod +x /usr/local/bin/docker-compose
Möglichkeit 2 - direkte Installation per apt
Die offizielle Docker-Dokumentation empfiehlt die Installation per apt von deren Seite. Hierfür solltest du zunächst dein apt-manager updaten:
$ sudo apt-get update
Im Anschluss kannst du Docker und die dependencies herunterladen:
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
Auch hier musst du im Anschluss den normalen User mit Docker verknüpfen und mit einem Neustart bestätigen:
$ sudo usermod -aG docker $USER
Nun kannst du mit einem der folgenden Befehle testen, ob alles richtig eingerichtet wurde:
$ docker run hello world
$ docker --version
Docker auf Windows/Mac installieren
Für Windows und Mac stellt Docker eine Desktop-Version mit verschiedenen Vorteilen bereit. So ist Docker Compose bereits in der Installation inbegriffen, Updates müssen nicht von Hand installiert werden und die Installation ist im Allgemeinen deutlich einfacher. Für die Installation ist eine Anmeldung bei Docker Hub erforderlich, dies ist allerdings nicht schlimm, da man für den späteren Arbeitsverlauf mit Docker sowieso ein Account benötigt. Unter folgendem Link kannst du Docker Desktop für Mac oder Windows herunterladen.
Unter Windows kann es je nach Alter oder Konfiguration deines Gerätes zu schwierigkeiten kommen. Wichtig ist, dass Windows Hypervisor (Hyper-V) von deinem Prozessor unterstützt wird. Dies siehst du am besten, wenn du unter “Ausführen” (windows + R) nach “optionalFeatures” suchst. Ist in dem anschließend zu sehenden Fenster die Option “Hyper-V” ausgegraut kannst du Docker nur bedingt benutzen (Modus für Windows-Container). Ein problemloses Arbeiten über verschiedene Setups hinaus ist dadurch nicht möglich. Wird Hyper-V von deinem Prozessor allerdings unterstützt kannst du nach der Installation ohne große Einrichtung loslegen. Auch Mac benötigt einige Voraussetzungen. Dein Gerät sollte mindestens aus dem Jahr 2010 sein und mindestens mit macOS Yosemite laufen. Als Test kannst du im Terminal einfach folgende Befehle zum Test ausführen:
$ docker --version
$ docker-compose --version
$ docker-machine --version
Images und Container
In diesem Kapitel lernst du was der Unterschied von Container und Images ist und wie diese funktionieren.
Bevor man die Funktionsweise von Docker-Containern beschreibt ist es wichtig zuerst zwischen einem Docker-Container und einem Docker-Image zu unterscheiden. Grundsätzlich beruhen alle Docker-Container auf Images. Images sind sozusagen der Bauplan für den späteren Container. In dem Image sind alle Abhängigkeiten, die ein Container für den ordnungsgemäßen Betrieb braucht, hinterlegt. Die Images können ganz einfach über Docker Hub oder auch GitHub heruntergeladen werden. Nachdem das Image auf dem lokalen PC liegt kann es gestartet werden. Der nun laufende Prozess wird jetzt aber als Container bezeichnet. Es handelt sich also ähnlich wie bei der Magma/Lava Thematik nur um eine Eigenschaft, die zu der Namensänderung führt. Container können jedoch noch beliebig angepasst und manipuliert werden. Wird ein Container gestoppt und ein neuer Container aus dem gleichen Image erzeugt nimmt er die Veränderungen, die im Container gemacht wurden jedoch nicht an.
Kurz gesagt: Docker-Container sind laufende Prozesse, die auf dem Abbild von Docker-Images basieren. Im nächsten Kapitel wird das erste Image ausgeführt.
Hello World
In diesem Abschnitt wirst du die ersten Praxis-Schritte mit Docker machen. Im Details lernst du:
- Das Ausführen eines Hello-World-Containers
- Das Erstellen eines eigenen Images
- Dateien Mappen
- Docker Volumes
- Der Aufbau eines Dockerfile
- Das Arbeiten mit Datencontainern
Mit der Installation von Docker wurde auch das Command-Line-Interface (Docker-cli) installiert. Es handelt sich hierbei und eine Befehlsbibliothek mit allen Funktionen rund um Docker. Dieses macht das Arbeiten mit Container, Images und Co erst möglich. Aufrufen kannst du das Docker-cli mit dem Schlagwort $ docker
. Darauf folgt dann der eigentliche Command. Die Docker Befehle lassen sich in drei Gruppen aufteilen:
- Das Arbeiten mit Containern (z.B.
$ docker run
oder$ docker ps
) - Das Manipulieren von Images (z.B.
$ docker build
) - Das Verbinden mit Docker-Hub (z.B.
$ docker pull
oder$ docker login
)
Nach den Command folgen meist noch Parameter oder Argumente. Für was genau die einzelnen Befehle stehen erfährst du natürlich mit dem Bearbeiten der einzelnen Kapitel.
So führst du ein Hello-World-Container aus
Zum besseren Einstieg in Docker gibt es ein vorgefertigtes Image mit dem Namen hello-world. Mit folgenden Befehl startest du aus dem Image heraus einen Container:
$ docker run hello-world
Darauf hin dürfte folgende message zu sehen sein:
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Wie in der zweiten Zeile zu lesen ist, verlief die Installation von Docker problemlos. Außerdem wird beschrieben, wie das hello-world image von Docker-Hub gepullt und ausgeführt wurde. Dies ist ein Workflow, der dir auf deinem weiteren Weg mit Docker noch öfters über den Weg läuft.
Alle deine Docker-Container anzeigen kannst du mit diesem Befehl:
$ docker ps
Dein eben ausgeführtes hello-world wird nun nicht angezeigt werden, da es nach dem Ausführen direkt wieder beendet wurde.
Mit dem Befehl $ docker images
kannst du alle auf deinem Setup existierenden images anzeigen. Dort sollte nun auch das hello-world image zu sehen sein.
Außerdem kannst du mit dem Befehl $ docker ps -a
auch die gestoppten Container sehen. Wenn du nun noch einmal den hello-world-Container mit docker run erzeugst und dir im Anschluss alle Container anzeigen lässt siehst du, dass durch jeden docker run Befehl ein neuer Container erzeugt wird, allerdings beide auf dem gleichen Image beruhen.
Eigenes Image erstellen
In diesem Abschnitt erfährst du wie du ein eigenes Docker-Images erstellst.
Wie du nun schon gelernt hast kannst du mit dem Befehl $ docker run
Container starten. Hierbei gibt es noch einige Parameter, die diesen Schritt etwas umfänglicher machen. So kannst du beispielsweise mit --name
deinem Container einen Namen geben. Mit dem Parameter -it
wird ein sog. interactive terminal geöffnet.
In der Praxis kann das ganze dann folgendermaßen aussehen:
$ docker run -it --name containername ubuntu:14.04 /bin/bash
Der Container wird gestartet. Lässt man sich nun mit ls -la
die Inhalte des Containers ausgeben, in dem man sich nun befindest erkennt man, dass es sich um eine isolierte Linux-Umgebung handelt (ubuntu 14.04). Durch das bash
am Ende des Befehls wird definiert, dass sich das ganze in einer bash-shell öffnen soll.
Dies ist nun unser Basis-Image, das wir um ein darauf aufbauendes Image erweitern wollen. Das zweite Image soll cURL beinhalten. Dies wird wie üblich per apt installiert. Du solltest hierzu natürlich sichergehen, dass du dich noch in der Ubuntu-Umgebung befindest.
Das ganze sieht dann wie folgt aus:
root@43f656d835f4:/# apt-get update
…
root@43f656d835f4:/# apt-get install curl
Nun kannst du mit exit
die Shell verlassen. Im folgenden werden wir die Änderungen aus der Shell committen und daraus ein neues Image erstellen.
Dies sieht Syntax-technisch zunächst aus wie bei einem Commit bei git. Der Unterschied ist allerdings, dass bei Docker nach der Commit-Message noch die ID des betreffenden Containers und der Name des neuen Images anzugeben sind:
$ docker commit -m “Commit-Message - Curl hinzugefügt” 1234567890AB curl-image
Bei erfolgreicher Eingabe sollte nun die neu erzeugte Image-ID ausgegeben werden. Außerdem kannst du mit $ docker image
sehen, ob das Image angelegt wurde.
Bind-Mounts
Eines der wichtigsten Features in Docker ist natürlich auch das Teilen von Daten. Den Anfang macht hier das Mappen von Daten auf lokaler Ebene. So kann man Daten vom Host-Rechner ganz einfach in das Docker-Setup teilen oder anders herum. Wie das genau aussieht lernst du im folgenden Kapitel.
Das Mappen von Daten ist eine gängige Methode für Test- oder Entwicklungsumgebungen auf lokaler Ebene. Dieser Vorgang wird im Englischen auch „Bind-Mounts“ genannt. Für größere Projekte mit mehreren Setups eignet sich diese Technik nicht besonders. Eine deutlich mobilere und bessere Methode ist hier der Einsatz von Volumes, was dies genau ist erfährst du später. Als ersten Schritt benötigst du eine Datei, die du mappen willst. Im Beispiel wird mit einer einfachen .txt-datei gearbeitet. Diese befindet sich auf dem Host-Rechner in einem eigenen Ordner.
Die Grundlegende Syntax zu Mappen von Dateien sieht so aus:
$ docker run -v hostverzeichnis:dockerverzeichnis
Wie du siehst wird hierfür ein Container erzeugt. Um das ganze etwas anschaulicher zu machen arbeiten wir im folgenden Beispiel mit ein paar Parametern mehr.
$ docker run -it -v ~/webroot/lokal:/home/daten --name Datenparty ubuntu:14.04 /bin/bash
Wie du feststellen kannst öffnen wir das ganze wieder in einer isolierten Linux-Umgebung. So können wir feststellen, ob die Daten auch wirklich geteilt wurden. Außerdem nennen wir den Container zur besseren Übersicht um. Der Parameter -v
leitet hier das Mapping ein. Darauf folgt der Pfad zum Ordner mit der gemappten Datei. Nach dem Doppelpunkt wird das Zielverzeichnis im Container definiert. Wichtig hierbei ist, dass der Ordner „daten“ erst mit diesem Befehl erzeugt wird und die Daten aus dem im Befehl angegeben Host-Pfad in diesen verknüpft werden.
Dass die Daten wirklich aktiv gemappt werden und nicht bloß in den Container kopiert werden kannst du auch testen. Wenn du die Datei lokal durch beispielsweise Umbenennen manipulierst und durch ein einfaches $ ls
das Verzeichnis in deinem Container aktualisierst siehst du, dass die Dateien wirklich verknüpft sind. Natürlich funktioniert dies auch in die andere Richtung, also von Docker-Container auf Host-Verzeichnis.
Docker Volumes
In diesem Abscnitt lernst du was Volumes sind und wie diese erstellt, befüllt und entfernt werden.
Docker Volumes ist der präferierte Weg für persistente Daten die von Docker Containern generiert und verwendet werden. Während Bind-Mounts (das Mappen von Dateien) von der Verzeichnisstruktur der Host-Maschine abhängig sind, werden die Volumes vollständig von Docker verwaltet. Volumes haben mehrere Vorteile gegenüber Bind-Mounts.
- Volumes können sicherer von mehreren Containern gemeinsam genutzt werden.
- Volumes sind einfacher zu sichern oder zu migrieren als Bind-Mounts.
- Für neue Volumes kann der Inhalt vorab mit einem Container gefüllt werden.
- Du kannst Volumes mithilfe von Docker-CLI-Befehlen oder der Docker-API verwalten.
- Volumes funktionieren sowohl auf Linux- als auch auf Windows-Containern.
Wenn dein Container nicht persistente Daten generiert, solltest du die Verwendung eines tmpfs-Mount in Betracht ziehen, um zu vermeiden, dass die Daten dauerhaft irgendwo gespeichert werden, und um die Leistung des Containers zu erhöhen. Weiteres über tmpfs-Mounts findest in der offiziellen Docker-Dockumentation.
Volume erstellen und verwalten
Im Gegensatz zu einem Bind-Mount kannst du Volumes außerhalb des Bereichs eines beliebigen Containers erstellen und verwalten. Hier ist eine Auflistung den gängigsten Befehlen:
Erstellen eines Volumes:
$ docker volume create mein-volume
Volumes auflisten:
$ docker volume ls
Volume inspizieren:
$ docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
Ein Volume entfernen:
$ docker volume rm my-vol
Volume mit Container starten
Wenn du einen Container mit einem noch nicht vorhandenen Volume starten, erstellt Docker das Volume für dich. Das folgende Beispiel lädt das Volume html
in das Verzeichnis /app/
des Containers. vol-nginx
ist der Name des Containers.
$ docker run -d --name vol-nginx -v mein_vol:/app nginx:latest
Um den Container zu stoppen und zu entfernen sind zwei Kommandos nötig. Zu beachten ist auch, dass das Entfernen von einem Volume ein weiterer separater Schritt ist.
$ docker container stop vol-nginx
$ docker container rm vol-nginx
$ docker volume rm vol-nginx
Ein Container benutzen um ein Volume zu füllen
Wenn du einen Container startest, der ein neues Volume erstellt, wie oben beschrieben, und der Container Dateien oder Verzeichnisse in dem Verzeichnis hat, das gemountet werden sollen (wie /app/
oben), wird der Inhalt des Verzeichnisses in das Volume kopiert. Der Container stellt dann das Volume bereit und verwendet es, und andere Container, die das Volume verwenden, haben auch Zugriff auf den vorgefüllten Inhalt.
Um dies zu veranschaulichen, startet dieses Beispiel einen nginx-Container und füllt das neue Volume html
mit dem Inhalt des Containerverzeichnisses /usr/share/nginx/html
, in dem Nginx seinen Standard-HTML-Inhalt speichert.
$ docker run -d --name=mein-nginx -v html:/usr/share/nginx/html nginx:latest
Um nach dem Ausführen des Beispiels den Container und das Volume zu löschen, kann man wieder dieselben Befehle ausführen wie im oberen Abschnitt.
Entfernen aller Volumes
So entfernst du alle nicht verwendeten Volumes und gibst Speicherplatz frei:
$ docker volume prune
Dockerfile
In diesem Abschnitt lernst du warum das Dockerfile gebraucht wird und welche gängigen Befehle du kennen solltest. Außerdem erfährst du, wie dein erstes Image anhand des Dockerfiles konfigurieren, bauen und ausführen kannst.
Das Dockerfile ist ein wichtiges Werkzeug zum automatischen Bauen von Images. Bei einem Dockerfile handelt es sich um eine simple Textdatei mit Instruktionen und Argumenten zum Aufbau der Images. Die Syntax sieht dabei wie folgt aus:
INSTRUKTION Argument
Die Instruktionen sind festgelegte Begriffe. Eine vollständige Übersicht zu allen Instruktionen findest du in der offiziellen Docker-Dokumentation. Einige Beispiel-Instruktionen sind FROM
zum festlegen des Basis-image, mit ENV
kann man Variablen setzen oder mit RUN
Programme oder Dienste starten, die zum Erstellen des Images notwendig sind.
Am häufigsten generierst du vermutlich Images aus bereits bestehenden Dockerfiles beispielsweise aus dem Dockerhub. Dies geschieht mit folgendem Befehl:
$ docker build -f /dockerfile .
Der Parameter -f
und der Dahinter gestellte Pfad geben den Ort des Dockerfiles an. Der Punkt am Ende des Befehls sorgt dafür, dass das Image im gleichen Verzeichnis wie das Dockerfile erzeugt wird
Eine weitergeführte Alternative ist der Parameter -t
. Mit diesem kann man das daraus erstellte Image beliebig benennen.
In der Praxis sieht das folgendermaßen aus:
$ docker build -t repository/tag .
Um besser zu verstehen wie der Workflow rund um so ein Dockerfile abläuft wird im Folgenden erklärt, wie ein einfaches Dockerfile geschrieben und ausgeführt wird.
Gehe zunächst in ein beliebiges Verzeichnis auf deinem Computer. Dort wird nun das Dockerfile erstellt und mit einem beliebigen Editor (in diesem Fall vim) geöffnet:
$ touch Dockerfile
$ vim Dockerfile
Nun wird das Dockerfile wie oben beschrieben mit Instructions und Argumenten gefüllt. Da Docker das File von Oben nach Unten abarbeitet ist, es wichtig die einzelnen Instruktionen in der richtigen Reihenfolge zu setzen:
FROM ubuntu:14.04
MAINTAINER Max Mustermann
RUN apt-get update
RUN apt-get install -y curl apache2
ENV MyVar="Irgendein Wert"
WORKDIR /home
VOLUME ["/var/www"]
ENTRYPOINT ["top"]
Natürlich gibt es durch die große Anzahl der Instruktionen auch viele Wege so ein Dockerfile aufzubauen. In unserem Beispiel wird zunächst das Basisimage mit FROM
definiert. als zweiten Schritt wird der Urheber des Images genannt, dies ist nur konventionsbedingt, allerdings keine Pflicht. Mit RUN
werden nun einige Dienste gestartet und mit ENV
eine Umgebungsvariable initialisiert, hier könnte man beispielsweise direkt einen Port angeben.
Mit WORKDIR
wird das Arbeitsverzeichnis gesetzt und alle darauf folgenden Befehle von dort aus ausgeführt. Mit VOLUME
kann man Verzeichnisse definieren, die nach Außen zur Verfügung gestellt werden. Am Ende setzen wir mit ENTRYPOINT
fest, was beim Starten des Containers zuerst ausgeführt wird, in unserem Beispiel starten wir das Programm „top“.
Wenn das Dokument beschrieben und gespeichert ist, kann man es direkt mit dem bereits kennengelernten Befehl ausführen:
$ docker build -t repository/image .
Wenn alles gut läuft wird nun der build-prozess sichtbar und man erkennt, wie da Dockerfile Schritt für Schritt abgearbeitet wird.
Mit $ docker images
sollte jetzt auch das neu angelegte image angezeigt werden.
Im nächten Schritt kannst du mit docker run
den Container starten:
$ docker build -t repository/image .
Hier zeigen sich die Vorteile des Dockerfiles, da du ansonsten nichts angeben musst. Wie im File beschrieben wird nun das Programm top gestartet. Top zeigt alle laufende Prozesse und einige Metadaten an. In unserem Beispiel allerdings nur einen laufenden Prozess und zwar der Container, in dem es ausgeführt wird.