14
Grundkurs DOCKER Gianluca Arbezzano präsentiert von

Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

  • Upload
    ngonhu

  • View
    214

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

GrundkursDOCKERGianluca Arbezzano

präsentiert von

Page 2: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

2

von Gianluca Arbezzano

Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in der heutigen Gesellschaft darstellt. Keine vernünftige Entscheidung kann noch getroffen werden, ohne dabei nicht nur die Welt, wie sie ist, sondern auch die Welt, wie sie sein wird, in Betracht zu ziehen.

– Isaac Asimov 1981

Es gibt zwei Arten von Erfindungen: zum einen sol-che, die es uns erlauben, Dinge zu tun, die wir zuvor nicht tun konnten. Und zum anderen solche, die uns helfen, Dinge besser zu tun. Die Entdeckung des Feuers etwa ermöglichte dem Menschen, Nahrung zu kochen, wilde Tiere zu vertreiben und sich in kalten Nächten aufzuwärmen. Doch erst jetzt, viele Jahrtausende spä-ter, können wir dasselbe per Knopfdruck erledigen und unsere Häuser mittels Elektrizität heizen.

Genauso schuf die Erfindung des Rades die Voraus-setzungen für Reisen und den Aufbau von Handel. Doch erst durch die Erfindung des Automobils wurde das Rei-sen und der Fernhandel in dem globalen Ausmaß mög-lich, wie wir es heute kennen.

Auf eine ähnliche Weise stellt das Web ein großes Netzwerk dar, das das Potential hat, Menschen auf der ganzen Welt miteinander zu verbinden. Doch erst durch die Entwicklung von Web-Anwendungen wurde es möglich, dieses komplexe System wirklich nutzbar zu machen und zu individualisieren.

In dieser Perspektive können Container als die größte Revolution der letzten Jahre angesehen werden: Durch Container haben wir ein einzigartiges Werkzeug zur Hand, das uns beim Verwalten und Entwickeln von An-wendungen behilflich ist. Doch verweilen wir noch ein wenig bei der Entstehungsgeschichte der Container-Tech-nologie, denn bekanntlich ist die Kenntnis der Vergan-genheit wichtig, um eine solide Zukunft zu erschaffen.

Wie alles begannEs gibt keine genauen Berichte darüber, warum Bill Joy am 18. März 1982 chroot in das Unix-System BSD (Ber-keley Software Distribution) einbaute. Möglicherweise einfach, um Lösungen und Programme in einem isolier-ten Root-Verzeichnis zu emulieren. Das allein war schon bahnbrechend, doch dem nicht genug: 1991 erweiterte Bill Cheswick chroot um einige Sicherheits-Features, die von der FreeBSD-Distribution zur Verfügung gestellt

Docker in production

Von Linux zu Docker: Die Grundlagen der Container-Technologie Keine andere Technologie hat die IT in den letzten Jahren so geprägt wie Docker. Doch warum ist das so? Was macht Docker so besonders, wie funktioniert die Technologie unter der Haube und wie kön-nen Sie vom Trend profitieren?

Page 3: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

3

wurden. Und er implementierte die “jails”. Erneut neun Jahre später, im Jahr 2000, führte er ein, was wir heute als das “jails-Kommando” kennen.

Ab diesem Zeitpunkt waren unsere chroots isoliert. Beim Start eines Prozesses in einer chroot ist der PID einzigartig, es gibt nur diesen einen Prozess. Lediglich von außen kann man alle Prozesse einsehen, die in einer chroot laufen.

Doch gab es ein Problem: Auf lange Sicht konnten unse-re Anwendungen nicht in diesem völlig isolierten Gefäng-nis bleiben. Anwendungen müssen mit der Außenwelt kommunizieren, Informationen austauschen usw. Um dieses Problem zu lösen, implementierten Entwickler wie Eric W. Biderman und Pavel Emelyanov im Jahr 2002 das Namespace-Feature in der Kernel-Version 2.4.19. Dieses wird genutzt, um Systemressourcen wie das Netzwerk, die Prozesse und das Dateisystem zu verwalten.

All diese wunderbaren Features sind in den letzten Jah-ren im Zuge der “Container”-Bewegung populär gewor-den. Doch sind sie alles andere als neu! Vielleicht ist es aber auch genau das, was das Ganze so spannend macht: Alles Nötige existierte bereits seit langer Zeit unter der Haube. Es sind solide und getestete Features, die erst jetzt effektiv zusammengefügt und nutzbar gemacht wurden.

Dies ist nur ein kleiner Einblick in die Entstehungs-geschichte des Container-Ökosystems. Am Ende dieser Reihe werden wir versuchen nachzuvollziehen, warum schließlich Docker die Bühne betreten hat.

Isolation und VirtualisierungDie Wichtigkeit der Isolation liegt eigentlich auf der Hand: Sie hilft uns, möglichst effizient Ressourcen und Sicherheitsaspekte zu verwalten. Sie erleichtert das Mo-nitoring, um spezifische Fehler im System zu finden, die oftmals nichts mit unserer Anwendung zu tun haben.

Eine herkömmliche Lösung für diese Aufgaben ist die Virtualisierung: Man kann einen Hypervisor nutzen, um

virtuelle Server auf einer einzelnen Maschine zu erstellen. Dabei gibt es verschiedene Arten der Virtualisierung:

1. Vollständige Virtualisierung2. Teilweise Virtualisierung (virtuelle Maschinen, Xen

oder VMware)3. Betriebssystemvirtualisierung (Container)4. Anwendungsvirtualisierung (JVM)

Der Hauptunterschied zwischen diesen Virtualisierungs-methoden liegt darin, wie sie die Layer, die Anwendun-gen, das Processing, das Netzwerk und den Speicher abstrahieren und wie die oberen Ebenen mit den darun-terliegenden interagieren. Bei der vollständigen Virtua-lisierung wird beispielsweise die Hardware virtualisiert, bei der teilweisen Virtualisierung nicht.

Container sind eine Virtualisierung auf Ebene des Betriebssystems. Der Hauptunterschied zwischen Con-tainern und einer virtuellen Maschine liegt in der Virtu-alisierungsschicht: Container arbeiten auf der Ebene des Betriebssystems, virtuelle Maschinen auf der Ebene der Hardware.

Sprechen wir von Containern, liegt unser Fokus auf der Anwendungsvirtualisierung und einem speziellen Feature, das vom Kernel zur Verfügung gestellt wird und Linux Containers (LXC) heißt. Erstellen wir Con-tainer, tun wir nichts anderes, als isolierte Linux-Sys-teme auf dem gleichen Host zu erzeugen. Das bedeutet, dass wir beispielsweise das Betriebssystem nicht än-dern können, da unsere Virtualisierungsebene es nicht erlaubt, Linux Container außerhalb von Linux laufen zu lassen.

Container – warum überhaupt?Revolutionen basieren nicht einfach nur auf einem bestimmten Ereignis. Sie sind das Resultat ganz unter-schiedlicher Bewegungen und Veränderungen. Genauso

Abb. 1: Unterschiedliche Virtualisierungen

Page 4: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

4

sind Container nur ein kleines Puzzlestück einer größe-ren Geschichte.

Durch das Aufkommen des Cloud Computing hat sich unser Denken über Infrastruktur verändert. Heute verstehen wir darunter eine variable Anzahl an Servern, die sich bei Bedarf, mit einem vertretbaren Zeitaufwand und immer kostengünstiger hoch- bzw. herunterskalie-ren lassen. Anwendungen, die bislang vielleicht irgend-wo in einem Keller auf eigenen Servern liefen, liegen nun bspw. auf AWS und verfügen über einen Load Balancer und verschiedene Verfügbarkeitszonen. Kleine Teams und mittelgroße Unternehmen ohne eigene Datenzent-ren und Infrastrukturen profitieren von dieser Entwick-lung in besonderem Maße, da sie nun ebenfalls über Dinge wie Distribution, hohe Verfügbarkeit und Red-undanz nachdenken können.

Wenn unsere Anwendungen in virtuellen Maschinen laufen, ist es einfacher, in einem wachsenden Unterneh-men die Server entsprechend zu skalieren, um den Bedürf-nissen der Kunden besser gerecht zu werden. Wir haben diese Vorteile zu schätzen gelernt, aber es gibt auch etli-che neue Herausforderungen, etwa das Zeitmanagement, um dieser Dynamik nachzukommen. Die Skalierung gro-ßer Anwendungen ist in der Regel teurer: Typischerweise werden unsere Anwendungen immer größer, sodass das Deployment hohe Kosten verursachen kann.

Wir haben außerdem festgestellt, dass das Verhalten einer Anwendung nicht in allen Services und Entrypo-ints das gleiche ist: Manche erhalten mehr Traffic als andere. Also haben wir damit begonnen, unsere großen Anwendungen in mehrere Teile zu splitten, um sie leich-ter skalieren und überwachen zu können. Um unseren Anforderungen gerecht zu werden, musste allerdings ein Weg gefunden werden, die Anwendungsteile sicher und isoliert voneinander zu betreiben, ohne dass sie die Möglichkeit verlieren, miteinander zu kommunizieren.

So entstand der Architekturansatz der Microservices, den sich große Unternehmen wie Netflix, Amazon und Google zunutze machen. Heute bestehen deren Anwen-dungen aus hunderten von Microservices, die alle zu ei-nem großen und profitablen Produkt zusammengefasst sind.

Netflix war eines der ersten Unternehmen, das der Öf-fentlichkeit einen Blick unter die Motorhaube gewährte, indem sie offen erklärten, wie die Seite Netflix.com auf-gebaut wurde: Mehr als 400 Microservices sind dort im Einsatz, um Features wie die Registrierung, das Strea-ming, Rankings und viele weitere Teile der Anwendung zur Verfügung zu stellen.

Aktuell scheinen Container die beste Lösung zu sein, um eine dichte und dynamische Umgebung zu verwal-ten, in der hohe Kontroll- und Sicherheitsstandards herr-schen und die Möglichkeit gegeben ist, Anwendungen zwischen verschiedenen Servern hin- und herzuschieben.

Linux Container im praktischen EinsatzWikipedia nennt das Jahr 2008 als Startpunkt für das LXC-Projekt – das Ganze ist also nicht wirklich

neu. Joe Beda, Senior Software Engineer bei Google, spricht sogar davon, dass Container seit über zehn Jahren erfolgreich in der Produktion eingesetzt wer-den und betont, dass heutzutage alles innerhalb eines Containers laufen kann. Was Docker gemacht hat, ist, die Nutzung von Containern einfach zu gestalten. Dazu bietet Docker ein großes Ökosystem von Tools an, mit denen sich Container erstellen und betreiben lassen. Bevor wir aber auf Docker zu sprechen kom-men, müssen wir erst einmal verstehen, was ein Linux Container im herkömmlichen Sinne ist und wie er ein-gesetzt wird.

Wenn wir Container nutzen wollen und dabei eini-ge Features der Kernel Control Groups (cgroups) an-sprechen, so sind Namespaces besonders wichtig. Die Control Group begrenzt und isoliert Ressourcen von Prozessen wie Memory, Swap, CPU, I/O und Netzwerk. Es stehen außerdem Funktionalitäten für die Verwal-tung, das Monitoring und die Priorisierung sowie die Bearbeitung des Inhalts einer Kontrollgruppe zur Verfü-gung. Daher ist es sinnvoll, jeden Container mit genau einem Prozess zu verbinden, damit man die vollständige Kontrolle darüber hat, was dieser Prozess tut. Ein Na-mespace wrapt eine Ressource und macht diese für ei-nen Prozess oder eine Gruppe von Prozessen verfügbar, die diesen Namespace verwenden. Es gibt verschiedene Arten von Namespaces: PID, Netzwerk, IPC, Mount, Users und andere.

Docker-Container nutzen cgroup, um Container zu isolieren. Namespaces dienen der Zugriffskontrolle, um die Container abzusichern. cgroups verbinden Tasks mit einem Set von Ressourcen:

• memory, um ein Limit für die Speichernutzung zu setzen

• cpu, um den Scheduler zu nutzen und der CPU Zu-gang zu einer cgroup zu gewähren

• cpuset, um individuelle CPUs und Speicher-Nodes zuzuweisen

• cpuacct, um automatische Berichte über die CPU-Nutzung in einer cgroup zu erhalten

• devices, um einem spezifischen Gerät Zugang zu gewähren bzw. zu verweigern

• blkio, um I/O-Limits von Block-Geräten wie Festplat-ten oder USB festzulegen

• freezer, um Tasks in einer cgroup zu stoppen oder fortzuführen

• ns, um Namespaces innerhalb einer cgroup zu ver-wenden

• net_cls, um Linux Traffic Control zu ermöglichen, Pakete einer cgroup zu identifizieren

• net_prio, um den Netzwerk-Traffic zu priorisieren

Bekanntlich ist alles in Linux eine Datei. Daher ist auch eine cgroup nur eine Ansammlung von Dateien, die in /sys/fs/cgroup zu finden ist. Doch ist es nicht einfach, cgroups als Dateien zu verwalten. In der Praxis ist es schier unmöglich, das ganze aktuell zu halten. Deshalb

Page 5: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

5

gibt es verschiedene Tools, die uns das Managen einer cgroup erleichtern:

• libcgroup: eine Bibliothek, die die Control Group des Kernels abstrahiert und Tools wie cgreate und cgclas-sify enthält

• cgmnager: eine Kombination aus einem Daemon und einem Clients (cgm) für die Verwaltung von cgroups

libcgroup lässt sich unter Ubuntu 16.10 mit der in Lis-ting 1 dargestellten Anweisung installieren.

Im Verzeichnis können wir alle Control Groups einse-hen, die zuvor erstellt wurden. Jetzt können wir versu-chen, unsere erste eigene cgroup zu erstellen:

$ sudo cgcreate -a user -g memory,cpu:groupname

Diese Anweisung erstellt eine cgroup mit dem Namen foo, die zwei Control Groups beinhaltet: memory und cpu. Wir können nun prüfen, ob es das Verzeichnis foo in den Verzeichnissen cpu und memory gibt (siehe Lis-ting 2).

In der cgroup können wir nun Kommandos ausfüh-ren, zum Beispiel:

$ cgexec -g memory,cpu:groupname/foo sleep 5

Dadurch ist es nun möglich, die Limits der Ressourcen, also in unserem Fall memory und cpu, einzustellen. Das ist ganz einfach: Liest man sich den Inhalt von /sys/fs/cgroup/memory/foo/memory.limit_in_bytes durch, sieht man das aktuelle Speichermaximum, das ein Task in der cgroup foo nutzen kann. Dieses Limit kann wie folgt geändert werden:

echo 20000000 > /sys/fs/cgroup/memory/groupname/foo/memory.limit_in_bytes

Um die cgroup zu entfernen, kann man einfach eingeben:

sudo cgdelete -g memory,cpu:foo

Nach dieser Anweisung sollten die Verzeichnisse von foo nicht mehr existieren.

Das vorangegangene Beispiel war natürlich wenig sinn-voll und diente nur einer vereinfachten Darstellung der Funktionsweise von Containern. Ich habe aber festge-stellt, dass diese einfachen Mechanismen auf den unteren Ebenen für viele Docker-Nutzer immer noch reine Magie sind. Doch ist es keine gute Idee, etwas in Produktionsbe-trieb zu nehmen, das man nicht wirklich versteht.

Mit diesem Hintergrundwissen ausgestattet, können wir uns im nächsten Artikel anschauen, was Docker wirk-lich tut und welche Innovationen damit möglich sind.

Listing 2$ ls -lsa /sys/fs/cgroup/cpu/foo/total 00 drwxr-xr-x 2 gianarb root 0 gen 21 15:53 .0 dr-xr-xr-x 7 root  root 0 gen 21 15:53 ..0 -rw-r--r-- 1 gianarb root 0 gen 21 15:53 cgroup.clone_children0 -rw-r--r-- 1 gianarb root 0 gen 21 15:53 cgroup.procs0 -r--r--r-- 1 gianarb root 0 gen 21 15:53 cpuacct.stat0 -rw-r--r-- 1 gianarb root 0 gen 21 15:53 cpuacct.usage0 -r--r--r-- 1 gianarb root 0 gen 21 15:53 cpuacct.usage_all0 -r--r--r-- 1 gianarb root 0 gen 21 15:53 cpuacct.usage_percpu0 -r--r--r-- 1 gianarb root 0 gen 21 15:53 cpuacct.usage_percpu_sys0 -r--r--r-- 1 gianarb root 0 gen 21 15:53 cpuacct.usage_percpu_user0 -r--r--r-- 1 gianarb root 0 gen 21 15:53 cpuacct.usage_sys0 -r--r--r-- 1 gianarb root 0 gen 21 15:53 cpuacct.usage_user0 -rw-r--r-- 1 gianarb root 0 gen 21 15:53 cpu.cfs_period_us0 -rw-r--r-- 1 gianarb root 0 gen 21 15:53 cpu.cfs_quota_us0 -rw-r--r-- 1 gianarb root 0 gen 21 15:53 cpu.shares0 -r--r--r-- 1 gianarb root 0 gen 21 15:53 cpu.stat0 -rw-r--r-- 1 gianarb root 0 gen 21 15:53 notify_on_release0 -rw-r--r-- 1 root  root 0 gen 21 15:53 tasks

Listing 1$ sudo apt-get install cgroup-bin$ ls -lsa /sys/fs/cgrouptotal 00 drwxr-xr-x 14 root root 360 gen 14 20:47 .0 drwxr-xr-x 10 root root 0 gen 21 15:18 ..0 dr-xr-xr-x 6 root root 0 gen 21 15:18 blkio0 drwxr-xr-x 2 root root 60 gen 14 20:47 cgmanager0 lrwxrwxrwx 1 root root 11 gen 14 20:47 cpu -> cpu,cpuacct0 lrwxrwxrwx 1 root root 11 gen 14 20:47 cpuacct -> cpu,cpuacct0 dr-xr-xr-x 6 root root 0 gen 21 15:18 cpu,cpuacct0 dr-xr-xr-x 3 root root 0 gen 21 15:18 cpuset0 dr-xr-xr-x 6 root root 0 gen 21 15:18 devices0 dr-xr-xr-x 4 root root 0 gen 21 15:18 freezer0 dr-xr-xr-x 3 root root 0 gen 21 15:18 hugetlb0 dr-xr-xr-x 7 root root 0 gen 21 15:18 memory0 lrwxrwxrwx 1 root root 16 gen 14 20:47 net_cls -> net_cls,net_prio0 dr-xr-xr-x 3 root root 0 gen 21 15:18 net_cls,net_prio0 lrwxrwxrwx 1 root root 16 gen 14 20:47 net_prio -> net_cls,net_prio0 dr-xr-xr-x 3 root root 0 gen 21 15:18 perf_event0 dr-xr-xr-x 6 root root 0 gen 21 15:18 pids0 dr-xr-xr-x 7 root root 0 gen 21 15:18 systemd

Page 6: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

6

von Gianluca Arbezzano

Kann ich noch immer über zwei oder drei Männer springen, so ich es früher konnte? Nein. Bin ich noch immer so schnell, wie ich es mal war? Nein. Aber ich be-herrsche noch immer die Grundlagen und die Technik. So kann ich das Spiel weiterhin dominieren. Schon als Kind habe ich nie einen Schritt auf dem Weg übersprun-gen – ich habe mir eine Basis erarbeitet, weil Sportlich-keit eine flüchtige Sache ist.

– Kobe Bryant

Fangen wir also an, die große Docker-Welt zu erkun-den. Warum hat Docker eine so große Revolution aus-gelöst; warum revolutionieren Container überhaupt die gegenwärtige IT-Szene? Diese Fragen wollen wir nun beantworten. In diesem Kapitel befassen wir uns mit der Docker Engine, denn sie ist die zentrale Anwendung, die es uns ermöglicht, Docker Container zu erstellen und zu managen. Die Docker Engine ist Open Source und wurde in der Programmiersprache Go geschrieben – mit mehr als 27.000 Commits und rund 1.500 Contributors handelt es sich hierbei um eines der größten Open-Sour-ce-Projekte der Welt.

Abbildung 1 zeigt grob, wie die Docker Engine funk-tioniert: Sie befindet sich zwischen dem Betriebssytem (OS) und unseren Anwendungen. Docker Container ba-

sieren auf Linux, sodass ein Linux-Kernel nötig ist, um mit Docker zu arbeiten. Bei mir bildet ein MacBook Pro die lokale Umgebung; viele der Beispiele in diesem Buch laufen darauf in einer virtuellen Maschine, die ich mit Virtual Box betreibe.

Docker auf Ubuntu installierenDies ist eine interaktive Anleitung; neben der Lektüre können alle hier erklärten Arbeitsschritte direkt auch selbst mitgemacht werden. Darum beginnen wir damit, wie man Docker auf dem eigenen Laptop installiert. Auf Ubuntu geht es mit ein paar einfachen Schritten los; es gibt aber auch Implementierungen für andere Linux-Distributionen.

1. Repositories aktualisieren$ sudo apt-get update$ sudo apt-get install apt-transport-https ca-certificates$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80   --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

2. Wenn die Datei nicht existiert, kann man sie mit seinem bevorzugten Editor erstellen$ /etc/apt/sources.list.d/docker.list

3. Folgende Zeile muss in die Datei kopiert werden:$ deb https://apt.dockerproject.org/repo ubuntu-xenial main

Docker in production

Grundkurs Docker: Eine praktische Einführung in die Welt der Container Keine andere Technologie hat die IT in den letzten Jahren so geprägt wie Docker. Doch warum ist das so? Was macht Docker so besonders, wie funktioniert die Technologie unter der Haube und wie kön-nen Sie vom Trend profitieren? In unserem Grundkurs Docker lernen Sie anhand praktischer Beispiele, Docker und die Container-Technologie richtig einzusetzen.

Page 7: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

7

4. APT Index aktualisieren$ sudo apt-get update

5. Docker installieren$ sudo apt-get purge lxc-docker$ sudo apt-get install linux-image-extra-$(uname -r)   linux-image-extra-virtual docker-engine

6. Docker Daemon starten$ sudo service docker start

7. Den ersten Container starten$ sudo docker run hello-world

Docker stellt grundsätzlich eine gute Dokumentation für die Installation auf Linux bereit (Abb. 2); wer aber mit ei-nem MacBook oder unter Windows arbeitet, findet in den nächsten zwei Absätzen eine Installationsanleitung [1].

Docker auf dem Mac installierenDocker for Mac [2] ist ein Tool, das die Verwendung der Docker Engine unter MAC OS X erleichtert. Es handelt sich um eine einfache dmg, die direkt von der Seite heruntergeladen und auf dem MacBook installiert werden kann.

Sobald das Programm läuft (Abb. 3), kann Docker auf dem Terminal genutzt werden. Hierzu wird es einfach geöffnet, anschließend wird folgender Befehl ausgeführt:

$ sudo docker run hello-world

Docker auf Windows installierenAuf Windows kann man sich die Benutzung von Docker in der Entwicklungsumgebung auf die gleiche Weise vereinfachen: Man muss lediglich das Tool Docker for Windows herunterladen [3], es als klassische Anwen-dung installieren und schon kann man anfangen, mit Docker zu spielen.

Man sollte allerdings im Kopf behalten, dass Docker, trotz dieser beiden Lösungen, Linux benötigt, um zu funktionieren. Sowohl Docker for Mac als auch Docker for Windows arbeiten mit einer virtuellen Maschine und einem gemeinsamen Layer, der die Arbeit mit Docker genauso gestaltet wie unter Linux.

Die erste HTTP-Anwendung ausführenMicro [4] ist ein in der Programmiersprache Go ge-schriebener Dienst, der in Form einer sehr kleinen An-wendung, zwei HTTP-Zugangspunkte zur Verfügung stellt. Der Index zeigt die Container-IP an; außerdem kann über /health überprüft werden, ob der Service wie erwartet funktioniert. Der Dienst läuft auf Port 8000.

$ docker run -p 8000:8000 gianarb/micro:1.0.0

run ist einer der wichtigsten Befehle in der Docker En-gine (Abb. 4). Er startet einen neuen Container in Form

eines Images, das in diesem Fall auf gianarb/micro ver-sion 1.0.0 basiert. Mit -p kann ein Port vom Contai-ner an den Host weitergegeben werden. In diesem Fall kontaktieren wir so den HTTP-Server. Jetzt ist die Seite localhost:8000 im Browser erreichbar.

Die Architektur der Docker EngineWir haben bereits die Begriffe des Images, der Contai-ner, der Command Line und des Daemons benutzt; be-

Abb. 2: Docker ist nun betriebsbereit.

Abb. 3: Läuft Docker, ist dieses Kontextmenü verfügbar.

Abb. 1: Docker Container Virtualization (Quelle: Docker Inc.)

Page 8: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

8

vor wir weitermachen, schauen wir uns nun an, wie die Docker Engine aufgebaut ist, um die Architektur und Terminologie zu verstehen. Docker besteht aus zwei zentralen Bestandteilen, nämlich dem HTTP-Server, der in der Praxis das Backend bildet, das ein REST API zur Integration von Anwendungen in die Engine zur Verfü-gung stellt und dem Docker CLI, einem Command Line Tool, das das API verwendet, um alle Funktionen leicht benutzbar zu machen (Abb. 5).

Seit Docker 1.11 sind der Daemon und das CLI ge-trennte Binaries, die man zusätzlich zur Engine installie-ren oder weglassen kann, je nach eigenem Bedarf. Diese Lösung ist auch gut für die Produktion geeignet, wo es möglich ist, nur der Daemon zu installieren.

Der Docker Daemon kann unmittelbar mit dem Kommando dockerd aufgerufen werden. Er unterstützt verschiedene Kommunikationsprotokolle und öffnet standardmäßig einen Unix Socket unter unix:///var/run/

docker.sock. Es gibt zahlreiche Möglichkeiten, dem Be-fehl etwas anzuhängen, zum Beispiel:

• -D, um den Debug-Modus freizugeben,• -H, zur Konfiguration des tcp-Hosts, beispielsweise

tcp://192.168.1.3:2376 und• -tls, um TLS zu aktivieren bzw. zu deaktivieren. Der

Standardwert lautet false; für die Produktion ist die Aktivierung aber sehr empfehlenswert.$ dockerd -D -H tcp://192.168.1.3:2376 --tls=false

Denkt daran, dass ein Prozessmanager die beste Lösung für das Management von Prozessen und Services dar-stellt. Jede Linux-Distribution bringt ihr eigenes Tool dafür mit; für Ubuntu, Fedora und CentOS sind das Up-start und systemd.

Während des Setups auf den verbreiteten Linux-Dis-tributionen, beispielsweise im Fall der drei vorgenann-ten, richtet Docker ein allgemeines Init-Standardskript ein, das nachfolgend an die eigenen Anwendungsfälle angepasst werden kann. In Ubuntu 15.04 kann die Da-tei unter in /etc/init.d/docker gefunden werden. Sie enthält alle notwendigen Funktionen zum Prozessma-nagement wie start, restart und stop.

Image und RegistryUm das Innenleben von Docker zu verstehen, müssen wir uns nun zwei weitere Konzepte genauer anschauen: Image und Registry. Jeder Container wird aus einem Basis-Image erstellt, einem Read-Only Template, das beispielsweise die Anwendung oder eine Basisversion von Ubuntu enthält. Images können aus Containern oder anderen Images mit einem Dockerfile erstellt werden. In folgendem Beispiel ziehen wir nginx aus dem offiziellen Repository:

\$ docker pull nginx

Wir laden jetzt ein offizielles Image aus dem Hub herunter (Abb. 6); davon stehen dort viele zur Verfügung. „Offi-ziell“ bedeutet in diesem Fall, dass bestimmte Organisa-tionen oder Unternehmen die Images erstellt haben und pflegen, etwa für nginx, Ubuntu, Debian, Redis, Apache, PHP plus, und dass sie diese Images zur Erstellung von Containern freigegeben haben. Aus diesen Schichten wird dann das Dateisystem zusammengestellt – die Downloads werden von Docker parallel verwaltet, was den Pull un-term Strich effizienter und schneller macht.

\$ docker run -d --name demo_nginx -p 80:80 nginx\$ docker ps

Nun haben wir das Image (Abb. 7), das jemand ande-res erstellt hat und haben daraus einen neuen Container erstellt. Wenn man jetzt den Browser öffnet und local-host:80 aufruft, sieht man, dass dort nginx läuft. Den Container selbst können wir wie folgt direkt aufrufen:

$ docker exec -it demo_nginx /bin/bash

Abb. 4: run-Befehl

Abb. 5: Die Docker-Architektur (Quelle: Docker Inc.)

Abb. 6: nginx wird aus dem offiziellen Repository gezogen.

Page 9: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

9

Änderungen an index.html müssen gespeichert werden, damit sie erhalten bleiben. Dazu wird eine neue Schicht auf Basis der aktuellen erstellt. Wird auf das Speichern verzichtet, gehen alle Änderungen, die in einem laufen-den Container gemacht wurden, verloren. Das Kom-mando zum Speichern kann außerhalb des vorherigen Containers ausgeführt werden:

$ docker commit demo_nginx my/nginx

Nun haben wir eine neue Schicht erstellt. Das Image my/nginx kann als Ausgangspunkt für andere Images die-nen oder auf den Server deployed werden. Die geänder-ten Images werden von oben nach unten gelesen; wenn wir index.html zweimal verändern und committen, wird nur die neueste Version angezeigt.

UnionFS [5] ist ein Filesystem-Dienst für Linux, der einen Union Mount für andere Dateisysteme erstellt, um Dateien und Verzeichnisse als Branches zu mergen und ein virtuelles Dateisystem zu erstellen. Docker nutzt diese Technologie, um Images zu splitten und zu mounten. Jedes Image baut auf etwas anderem auf; die so entstandenen Schichten werden nicht kopiert, sondern wiederverwen-det, wenn sie notwendig sind. Auf der obersten Schicht kann geschrieben werden. Sie wird beim Start des Con-tainers hinzugefügt und stellt den zentralen Unterschied zwischen Images und Containern dar, denn Images haben keine solche Schicht. Loggt man sich in einen Container ein und nimmt dort via su Änderungen vor, die man ohne Commit löscht, gehen diese Änderungen verloren.

Images können auch auf eine andere Art erstellt wer-den. Am besten ist die Verwendung von Dockerfiles, weil darin alle nötigen Kommandos und Informationen enthalten sind. Wenn ein Container ohne Dockerfile aus dem interaktiven Modus heraus erstellt wird, müs-sen alle Kommandos direkt in den Container eingefügt werden. Der Container muss dann bereits nach kurzer Zeit committed werden und kann danach nicht repli-ziert oder aktualisiert werden. Das Dockerfile ist im

VCS normalerweise in der Nähe der Anwendung zu fin-den (Abb. 8), beispielsweise in der Anwendung Micro [6]. Alternativ kann man ein Repository mit einem Set Dockerfiles erstellen, um daraus die eigenen Standard-Images zu erstellen [7].

Dockerfiles nah bei der Anwendung zu speichern hilft Entwicklern dabei, eine Entwicklungsumgebung zu erstellen. Dockerfiles im Repository sind eine gute Option, um neuen Entwicklern den Einstieg in die Ar-beit an der Anwendung zu ermöglichen, ohne dass sie Zeit für die Konfiguration des Laptops mit allen Ser-vices der Anwendung brauchen. Außerdem verändern sich das Dockerfile und die Anwendung parallel, das kann über Git nachverfolgt werden. Der letzte Aspekt ist darum relevant, weil so auch Rollbacks oder QA nicht nur für den Code, sondern auch für das Docker-file möglich sind.

Ein Dockerfile ist praktisch nur eine Textdatei, die die Befehle und Anweisungen enthält, die Docker zur Er-stellung von Images braucht.

Dieses Dockerfile hier enthält ein PHP-Skript, das „Hello folks!“ ausgibt (Listing 1). Mit diesem Dockerfi-le spezifizieren wir das Basis-Image.

FROM ubuntu:15.04 

Abb. 7: Parallele Verwaltung von Pulls

Abb. 8: Dockerfile im Repository der Anwendung

Listing 1FROM ubuntu:15.04 RUN apt-get update RUN apt-get install -y php5 RUN echo "<?php echo ’Hellofolks!’;" > /var/index.php WORKDIR /var CMD ["php", "-S", "0.0.0.0:9090", "-t", "."]

Page 10: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

10

Mit dem entsprechenden Kommando wird Apache2 installiert und der Index erstellt. CMD beschreibt das Kommando, das den Prozess im laufenden Container startet. In diesem Beispiel fangen wir damit an, den inte-grierten PHP Web-Server zu starten.

$ docker build -t php-example .

build ist ein Befehl, mit dem ein Image aus einem Do-ckerfile erstellt wird. -t gibt den Namen des Images an und . gibt den Pfad des Verzeichnisses an, der das Do-ckerfile beinhaltet. Aus diesem Image können Container erstellt werden; mit dem run-Befehl kann man die An-wendung starten.

$ docker run -it -p 9090:9090 php-example

Als nächstes öffnen wir localhost:9090 in einem Browser.In der echten Welt wird die index.php der Anwen-

dung nicht mit dem Container erstellt, sondern mit der ADD-Anweisung hinzugefügt.

ADD ./directory/to/add/ /path/into/the/container

Wenn, wie in unserem Beispiel hier, die Datei index.php im gleichen Ordner wie das Dockerfile liegt, kann fol-gender Befehl

RUN echo "<?php echo ’Hello folks!’;" > /var/index.php

durch

ADD ./index.php /var/index.php

ersetzt werden.Eine ./index.php-Datei ist ein PHP-Script, das folgen-

des enthält:

<?php echo ’Hello folks!’; >

Nun wird das Image noch einmal erstellt; sobald es fer-tig ist, wird index.php aus dem Dateisystem entfernt. Danach versuchen wir erneut, einen Container zu star-ten:

$ docker build -t your-username/php-example .$ rm -rf index.php$ docker run -it -p 9090:9090 your-username/php-example

Wie man nun im Browser sieht, wird die richtige Seite auch ohne die Datei angezeigt, weil ein neuer Container erstellt wurde, der die Anwendung enthält. Das bedeu-tet, dass die Anwendung nun wie ein Artefakt geteilt und deployed werden kann.

Die Registry ist in diesem Kontext ein weiteres wich-tiges Tool, das von Docker zur Verfügung gestellt wird. Die Registry hilft beim Verwalten der Distribution un-serer Images. Es gibt sowohl öffentliche als auch pri-vate Registries; die bekannteste ist hub.docker.com. Sie enthält viele öffentlich zugängliche Images; registrierte Nutzer können dort aber auch private Images verwal-ten. Wer noch nicht auf dem Hub registriert ist, sollte das jetzt tun – es ist nämlich an der Zeit, your-username/php-example dorthin zu pushen.

$ docker login

Mithilfe dieses Befehls erlaubt man dem Docker Daemon, mit dem eigenen Hub-Account zu kommuni-zieren. Der Name des Images bestimmt ein spezifisches Verhalten, so wird etwa unter my-username der eigene Nutzername eingesetzt – in meinem Fall gianarb, da dies mein Username in Docker Hub ist.

$ docker push gianarb/php-example

Jetzt ist unser Image öffentlich im Hub zugänglich und wir können all unsere Images auf der Profilseite sehen.

$ docker rmi -f gianarb/php-example$ docker pull gianarb/php-example

Der erste Befehl löscht das Image vom Laptop; der zweite zieht es aus dem Hub. Das ist ein sehr einfa-ches Beispiel, das zeigt, was eine Registry ist und wie sie eure Images verwaltet. Docker kann auch Tags ver-

Abb. 9: Die Docker-Hilfe

Page 11: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

11

walten und stellt Tools zur Verfügung, mit denen der Unterschied zwischen den Images und den Containern im Repository markiert wird. So weiß man, womit man arbeitet, bevor der Versuch unternommen wird, ein neues Image aus einem Container zu erstellen. Im nächsten Kapitel sehen wir uns diese Funktionen in der Praxis an.

Docker Command Line ToolDie Command Line ist ein vollständiges und mächtiges Tool, um mit dem Daemon zu interagieren. Der sichere Umgang damit hilft dabei, Container zu erstellen und zu verwalten.

Mit dem Befehl docker -help können wir uns eine Liste von Kommandos anzeigen lassen (Abb. 9). Viele davon sind uns bereits über den Weg gelaufen, aber diese Übersicht ist ganz gut, um sich die am meisten verwendeten Kommandos noch einmal anzeigen zu lassen.

run$ docker run hello-world

Dieser Befehl benutzt das hello-world-Image, um einen neuen Container zu starten. Es stehen viele Optionen dafür zur Verfügung, mit denen Ports geteilt, Volumes gemounted, Container in spezifischen Netzwerken aus-geführt und der Ressourcenverbrauch für einzelne Con-tainer limitiert werden können. Was das in der Praxis bedeutet, zeigt Listing 2 für einen solchen Befehl.

• -i hält das STDIN offen• -t weist ein tty zu• -p teilt einen Port aus dem Container mit dem Host;

in diesem Fall Port 8000 bis 8000. Dieser Wert ist ein Array, es können also auch mehrere Ports geteilt wer-den. Es müssen dafür einfach nur weitere -p-Werte spezifiziert werden.

• -v teilt Dateien und Verzeichnisse wie Volumes aus dem Host mit dem Container. Dieser Wert ist ebenfalls ein Array; in diesem Beispiel werden zwei Volumes geteilt. Das erste ist mein ssh-key, der vom Host unter dem Pfad /home/gianarb/.ssh/id_rsa im Container /root/.ssh/id_rsa gespeichert wird.

• -network fügt diesen Container dem Front-Tier-Netz-werk hinzu. Was das bedeutet, werden wir später sehen.

• -memory beschränkt das durch den Container ver-wendbare RAM auf 10 Megabyte

• -name ist der Name des Containers• gianarb/micro ist der Name des Images

ps

$ docker ps

Dieser Befehl zeigt die Nummer der Container an, die aktuell laufen, mit der Option -a kann die Liste der ge-stoppten Container angezeigt werden (Abb. 10). Jeder Container hat eine ID oder einen Namen; zur Ausfüh-rung von Befehlen, die eine Container ID brauchen, kann der vollständige Name oder eine partielle ID ge-nutzt werden (es reicht sogar der erste Buchstabe, wenn er nur einmalig verwendet wird).

exec

$ docker exec container_name ls -lsa

Damit lassen sich die Logs anzeigen, die ein Container geschrieben hat. Mit der Option -f kann man die Logs verfolgen.

push and pull

$ docker pull gianarb/micro:1.0.0$ docker push gianarb/micro

Diese beiden Befehle werden benutzt, um Images zu ver-walten und sie in eine Remote Registry zu schieben oder daraus zu beziehen.

tags$ docker tag 5wgs46h gianarb/micro:1.0.1

Jedem Image kann ein Tag zugewiesen werden, dabei handelt es sich um ein Label, das dem Image zugeord-net wird. Der Standard-Tag ist latest, man kann aber auch dem Best Practice folgen und beispielsweise wie in einer normalen Code-Basis mit SemVer [8] versionie-ren. In diesem Beispiel taggen wir das Image mit der ID

Abb. 10: Liste der gestoppten Container

Listing 2$ docker run -i -t -p 8000:8000 \   -v /home/gianarb/.ssh/id_rsa:/root/.ssh/id_rsa \   -v $PWD:/opt \   --network front-tier \   --memory 10M \   --name site \   gianarb/micro

Page 12: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

12

5wgs46h durch etwas wie example:1.0.1. Außerdem kann der Befehl tag zur Vorbereitung des Pushs eines Images in die eigene Registry genutzt werden. Der Stan-dard ist hier registry-1.docker.io, allerdings kann auch ein anderes Ziel definiert werden, um das Image in eine private Registry zu pushen.

$ docker tag 5wgs46h registry.gianarb.it:gianarb/micro:1.0.1

inspect

$ docker inspect 4645shgre

inspect gibt Informationen über Images und laufende Container aus. Eine sehr nützliche Option ist -format, das den typischen CLI-Output in ein rohes JSON-Format konvertiert, inklusive Informationen zu Volumes, Ports, IP und allem anderen, was der Daemon über den Con-tainer weiß. Das ist sehr nützlich, um grundlegende Au-tomatisierungen mit Tools wie jq [9] einzurichten. Diese erlauben, JSON-Dateien direkt an den Bash zu übergeben.

start, stop, restart, kill

$ docker save gianarb/micro > micro.tar.gz$ cat micro.tar.gz | docker import - my/micro:new$ docker import http://example.exampleapp.com/micro.tar.gz

Wie bereits erwähnt ist die Registry der beste Weg, um Images auszuliefern und zu verteilen; Docker unterstützt jedoch auch den Befehl save, mit dem ein tarball des Images erstellt wird, sowie den Befehl import, mit dem Docker Images aus dem tarball erstellt werden. Dieses Vorgehen ist beim Entwickeln von Anwendungen gut geeignet, besonders wenn man etwas mit jemandem tei-len möchte. Wenn es hingegen um die Verwaltung der Produktions-Releases und um das Deployment geht, ist die Registry die beste Option zur Auslieferung und Ver-teilung von Images.

Volumes und DateisystemeWie wir bereits wissen, hat jeder Container sein eige-nes isoliertes Dateisystem. Wir wissen außerdem, dass der Container aus verschiedenen Schichten besteht. Mit den Volumes haben wir uns aber noch nicht be-schäftigt. Ein Data Volume ermöglicht es dem Con-tainer, das Union-Dateisystem zu umgehen. Volumes werden initialisiert, wenn ein Container erstellt wird; sie stellen Funktionen für das Persistieren und Tei-len von Daten bereit. Jedes Volume hat einen Mount Point und falls bereits Daten vorliegen, werden diese in das Container-Volume kopiert und sind dort ver-fügbar bzw. nutzbar:

$ docker run -it -v /tmp ubuntu /bin/bash

Mit der Option -v werden neue Volumes erstellt und dem Container hinzugefügt:

$ docker run -it -v /tmp -v /opt ubuntu /bin/bash

Man kann auch mehrere Volumes miteinander verbin-den, um sie alle demselben Container anzuhängen.

Ein Volume zu erstellen ist nützlich, wenn eine An-wendung Daten enthält, die mit in anderen Container gemounted werden sollen. Ein und dasselbe Volume kann von mehreren Containern verwendet werden. Wenn der aktuelle Container stirbt oder gelöscht wird, sind alle im Volume gespeicherten Dateien weiterhin verfügbar und können an den nächsten Container ange-hängt werden. Volumes werden normalerweise genutzt, um beispielsweise Logs und den Cache zu verwalten. Sie können mit dem Keyword VOLUME auch im Docker-file beschrieben werden:

VOLUME /tmp

Auch spezifische Verzeichnisse aus dem Host können di-rekt in den Container -v /tmp:/temporary gemapt werden. Der erste Pfad ist das Verzeichnis im Host; der zweite der Mount Point für den Container. Dauerhafte Änderungen an einem Volume werden innerhalb und außerhalb des Containers geteilt, weshalb dieses Feature perfekt für die Entwicklung geeignet ist. So kann das Projekt in einen Container gemounted und die Produktionsumgebung eben dort simuliert werden. Es ist allerdings wichtig zu bedenken, dass Daten innerhalb eines Volumes persistiert werden, auch wenn der Container abläuft.

Wenn ein Volume nicht gemapt wird, aber man wis-sen möchte, wo es sich auf dem Host befindet, kann der Befehl docker inspect genutzt werden. Das Feld Mounts gibt, wie Listing 3 zeigt, Auskunft.

Außerdem können CLI-Befehle genutzt werden, um Volumes zu erstellen, zu persistieren oder zu löschen:

$ docker volume ls$ docker volume create$ docker volume rm

Listing 3{    ...        "Mounts": [            {                "Name": "0f83dc",                "Source": "/var/lib/docker/volumes/0f83dc/_data",                "Destination": "/tmp",                "Driver": "local",                "Mode": "", "RW": true,                "Propagation": ""            }        ],    ...}

Page 13: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

13

Diese Funktion stellt eine der Kernfunktionen für die Ver-wendung von Containern in der Entwicklung und im De-ployment dar. Aus diesem Grund stellt Docker auch eine gute Abstraktionsschicht zur Verfügung, auf der eigene Plug-ins zum Mounten verschiedener Dateisysteme wie NFS oder eigens entwickelter Lösungen verwendet wer-den können. Es gibt zahlreiche Plug-ins, die das Mounten dezentraler Speicherdienste erlauben, beispielsweise Flo-cker [10]. Infinit [11] ist ein weiteres spannendes Projekt, das eine Docker-Integration für die Verwaltung dezentra-lisierter Volumes auf verschiedenen Plattformen anbietet, beispielsweise für Amazon S3 und Azure. Das Projekt bietet einen Dienst für die Verwendung von persistiertem und skalierbarem Speicher verschiedener Cloud-Anbieter an. Wenn man mit einem Cluster von Docker Engines arbeitet, wird das Volume auf dem gleichen Host erstellt, auf dem der Container gestartet wird. Wenn man Contai-ner auf verschiedenen Servern aufsetzt, bedeutet das, dass die anderen Maschinen keinen Zugriff auf die Volumes haben. Infinit schafft hier Abhilfe und kümmert sich um die Migration von Volumes in einem Cluster.

volume kann außerdem zur Erstellung von Backups verwendet werden. Ein Volume, das in einem anderen Container erstellt wurde, kann mit -volumes-from <vo-lume-name> angehängt werden, wie das Beispiel zeigt:

$ docker run --rm --volumes-from img_avatar -v \   $PWD:/backup ubuntu tar cvf /backup/backup.tar      /var/www/front/public/avatar

Wie bereits gezeigt, kann das gleiche Volume zugleich in mehr als einem Container eingesetzt werden. Allerdings muss man dabei in Erinnerung behalten, dass zwei An-wendungen, die in dieselben Dateien schreiben, zu feh-lerhaften Daten führen können. Das ist ganz insgesamt ganz nützlich, man muss allerdings auch wissen, wann und wo man es nutzt.

Network und LinksVolumes helfen also dabei, Daten zu verwalten. Genau-so nützlich sind die Features Network und Links, die dazu da sind, Container miteinander zu verbinden und die Kommunikation zwischen unseren Anwendungen zu ermöglichen. Das ist darum so wichtig, weil es zumeist zu den primären Anforderungen an eine Anwendung gehört, dass sie mit anderen Services kommunizieren kann, beispielsweise mit MySQL, Redis, Vault oder mit anderen Containern im Cluster.

Der einfachste Weg zur Verbindung von zwei Contai-nern ist ein Link:

Wir erstellen hier (Listing 4) einen MySQL-Container mit dem Root-Passwort “root” und verbinden ihn mit einem WordPress-Container. MySQL kann vom Word-Press-Container aus nun mit dem Hostname mysql.my-wordpress erreicht werden.

-link <container-name>:alias ist das Format des Links, mit dem eine bidirektionale Kommunikation zwischen den beiden Containern ermöglicht wird; der

Alias definiert den Hostname, der dafür benutzt wird. Es können multiple Links erstellt werden – dafür muss man nur mehr -link-Optionen einfügen, wenn der run-Befehl ausgeführt wird.

Das Konzept der Links ist leicht verständlich, aller-dings ist es eine Legacy-Funktion. Seit Docker 1.9 steht das neue Konzept des Networks in stabiler, gebrauchs-fertiger Version zur Verfügung. Bevor wir uns nun ge-nau dieses ansehen, sollten wir aber einen Blick darauf werfen, wie Docker die Kommunikation zwischen Con-tainern handhabt (Abb. 11).

Docker erstellt standardmäßig ein neues Network Inter-face unter dem Namen docker0 und ein Bridge Network, das mit dem folgenden Befehl inspiziert werden kann:

Listing 4$ docker run --name mysql-service -e MYSQL_ROOT_PASSWORD=root -d mysql$ docker run --name my-wordpress \   -e WORDPRESS_DB_PASSWORD=root \   -e WORDPRESS_DB_HOST=mysql.my-wordpress \   --link mysql-service:mysql.my-wordpress \   -d wordpress

Listing 5$ docker network create wp-test$ docker run --name mysql-service -e MYSQL_ROOT_PASSWORD=root --network   wp-test -d mysql$ docker run --name my-wordpress \   -e WORDPRESS_DB_PASSWORD=root \   --network wp-test \   -d wordpress

Abb. 11: Kommunikation zwischen Containern in Docker

Abb. 11: Tabellarische Visualisierung der Außenkommunikation zwischen allen Containern darstellt.

Page 14: Grundkurs DOCKER - jaxenter.de · Grundkurs Docker 2 von Gianluca Arbezzano Es ist die Veränderung, die fortwährende, unaus-weichliche Veränderung, die den dominanten Faktor in

Grundkurs Docker

14

$ docker network inspect bridge

Dabei wird eine natürlich geordnete Tabelle erstellt (Abb. 12), die eine Außenkommunikation zwischen al-len Containern, die mit docker0 verbunden sind, und der Außenwelt erlaubt.

$ sudo iptables -L -n

Netzwerke stellen den besten Weg dar, um Verbindun-gen und den Traffic zwischen unseren Containern zu managen. Wir wissen bereits, dass Docker standardmä-ßig eine Bridge erstellt. Das bedeutet, dass jeder Con-tainer im Netzwerk frei mit jedem anderen Container darin kommunizieren kann.

Wir können unser letztes Beispiel (das mit dem Word-Press-Container) also konvertieren (siehe Listing 5).

Geändert hat sich nun, dass wir keinen Alias mehr haben, sondern stattdessen den Namen des Servers ver-wenden. Das liegt daran, weil Docker einen eingebetteten DNS-Server verwendet, der dieses Feature zur Verfügung stellt. Das Feature steht aber nicht in Verbindung mit der Standard-Network-Bridge zur Verfügung, sondern nur, wenn man ein eigenes Netzwerk erstellt (hier: wp-test).

FazitWir haben uns hier die Docker Engine angesehen, die in ein großes Ökosystem rund um die Container eingebet-tet ist. Jetzt wissen wir, was Docker ist und wie man mit dem Daemon interagiert und können darauf aufbauen.

Über den Autor

Gianluca Arbezzano hat als Software Engineer bereits mit vielen Sprachen gearbeitet, darunter PHP, Go und JavaScript. Er ist sehr an allem inter-essiert, das mit Automatisierung und der DevOps-Philosophie zu tun hat. Auch mit unterschiedlichen Cloud Providern hat er bereits umfangreiche Erfahrungen gesammelt, etwa mit AWS, dem OpenStack und DigitalOcean. Mobile und Web-basierte Anwendungen hat Gianluca mit Angular in der Vergangenheit ebenso realisiert, wie skalier-bare Infrastrukturen und Backends.

Seinen Weg als Entwickler begann er mit Open-Source-Produkten und -Frameworks wie Linux,

PHP, dem Zend Framework und Vagrant, wes-halb er in den unterschiedlichsten Open Source Communitys aktiv ist. Gianluca Arbezzano ist zudem Docker Captain und im Web unter ande-rem auf Twitter (@gianarb) zu finden; auf seiner Homepage (https://gianarb.it/) veröffentlicht er regelmäßig Artikel zu vielen Themen rund um Docker, Container und die Cloud.

Abseits vom Computer engagiert er sich bei un-terschiedlichen gemeinnützigen Organisationen, liebt Skifahren, Fußball und genießt es, die Zeit mit seinen Freunden bei gutem Essen und Trinken zu verbringen.