Docker - Adrian Mouat - E-Book

Docker E-Book

Adrian Mouat

0,0

Beschreibung

Docker-Container bieten eine einfache, schnelle und robuste Möglichkeit, Software zu entwickeln, zu verteilen und laufen zu lassen – besonders in dynamischen und verteilten Umgebungen. Mit diesem praktischen Leitfaden lernen Sie, warum Container so wichtig sind, was durch den Einsatz von Docker möglich ist und wie Sie es in Ihren Entwicklungsprozess einbinden. Dieses Buch ist aktuell zu Docker 1.12 und ideal für Entwickler, Operations-Techniker und Administratoren – insbesondere, wenn Sie einen DevOps-Ansatz verfolgen. Es nimmt Sie mit auf eine Reise von den Grundlagen bis zum Ausführen Dutzender Container auf einem Multi-Host-System mit Networking und Scheduling. Im Verlauf des Buches erfahren Sie, welche Schritte zum Entwickeln, Testen und Bereitstellen einer Webanwendung mit Docker notwendig sind. • Beginnen Sie mit Docker, indem Sie eine einfache Webanwendung entwickeln und bereitstellen. • Nutzen Sie Techniken aus dem Continuous Deployment, um Ihre Anwendung mehrmals pro Tag in die Produktivumgebung zu bringen. • Lernen Sie Optionen und Techniken kennen, um mehrere Container gleichzeitig zu protokollieren und zu überwachen. • Befassen Sie sich mit dem Erkennen im Netzwerk und mit Services: Wie finden sich Container gegenseitig und wie verbinden Sie sie? • Orchestrieren und clustern Sie Container, um Load Balancing zu ermöglichen, Ihr System skalierbar zu machen sowie Failovers und Scheduling umzusetzen. • Sichern Sie Ihr System, indem Sie den Prinzipien der "Defense in Depth" und dem Konzept der geringsten Rechte folgen. • Setzen Sie Container ein, um eine Microservices-Architektur aufzubauen.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern
Kindle™-E-Readern
(für ausgewählte Pakete)

Seitenzahl: 491

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Adrian Mouat ist Chief Scientist bei Container Solutions – einem europaweit vertretenen Serviceunternehmen, das sich auf Docker und Mesos spezialisiert hat. Zuvor war er Anwendungsberater bei EPCC, das zur University of Edinburgh gehört.

Zu diesem Buch – sowie zu vielen weiteren dpunkt.büchern – können Sie auch das entsprechende E-Book im PDF-Format herunterladen. Werden Sie dazu einfach Mitglied bei dpunkt.plus+:

www.dpunkt.de/plus

Docker

Software entwickeln und deployen mit Containern

Adrian Mouat

Adrian Mouat

Übersetzung: Thomas Demmig

Überarbeitung und Aktualisierung: Peter Roßbach

Lektorat: René Schönfeldt

Copy-Editing: Annette Schwarz, Ditzingen

Satz: III-satz, www.drei-satz.de

Herstellung: Nadine Thiele

Umschlaggestaltung: Helmut Kraus, www.exclam.de

Druck und Bindung: M.P. Media-Print Informationstechnologie GmbH, Paderborn

Bibliografische Information der Deutschen Nationalbibliothek

Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

ISBN:

Buch   978-3-86490-384-7

PDF   978-3-96088-036-3

ePub   978-3-96088-037-0

mobi   978-3-96088-038-7

1. Auflage 2016

Copyright © 2016 dpunkt.verlag GmbH

Wieblinger Weg 17

69123 Heidelberg

Authorized German translation of the English edition of Using Docker ISBN 9781491915769

© 2016 Adrian Mouat. This translation is published and sold by permission of O’Reilly Media, Inc., which owns or controls all rights to publish and sell the same.

Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.

Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.

Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autor noch Verlag können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.

5 4 3 2 1 0

Für alle, die es versuchen – ob mit oder ohne Erfolg

Geleitwort zur deutschen Übersetzung

Build, Ship and Run,

Any App,

Anywhere

Docker Mantra

Container-Technologie, Clouds, Microservices und die Anforderungen an Zuverlässigkeit und schnelle Reaktion verändern gerade die IT-Welt. Information werden als eigenständiger Wert gehandelt. Diese Entwicklung nimmt seit 20 Jahren durch das Internet ihren Lauf. Erst belächelt, dann immer schneller verwendet, ist es heute essenzieller Bestandteil des Lebens in unserer Gesellschaft. Nur durch die gigantischen Server-Farmen im Hintergrund und die Bereitstellung des Betriebsökosystems, ist das möglich. Immer leistungsfähigere Soft- und Hardware ist im Einsatz.

Ende 2013 entdeckte ich Docker und hatte ein Schlüsselerlebnis: endlich, nach fast 30 Jahren IT, eine simple Lösung, um komplexe Software einfach in kleine Containern zu verpacken, sinnvoll zu kombinieren und dann auch noch zwischen Maschinen austauschbar zu machen. Nach all den vorangegangenen Erfahrungen – manuell, mit Skripten, Packages, Betriebssystem-Images, Konfigurationsmanagement, kryptischer Isolierung von Prozessen – nun eine einfache Lösung! Docker vereint großartig die bestehende Linux-Virtualisierung, Ressourcen-Isolierung, Sicherheits-Features, Netzwerke und Storage-Lösungen in einem einfachen Ansatz.

Die Verbreitung von Docker ist die schnellste Technologie Adaption, die ich je beobachtet habe. Ende 2014 auf der DockerCon in Amsterdam, lernte ich Adrian Mouat und viele andere Docker-Enthusiasten kennen und schätzen. Zu diesem Zeitpunkt war in jedem Gespräch deutlich zu spüren, dass Docker unsere IT-Welt unumkehrbar veränderte und noch mehr zu erwarten war. Seit diesem Zeitpunkt promote ich Container-Technologien des Docker-Ökosystems, gebe Trainings, organisiere Meetings und Konferenzen, schreibe Artikel und lerne jeden Tag etwas Neues über IT-Infrastruktur.

Ein Buch über Docker zu schreiben – eine Technologie, die sich enorm schnell verändert – ist mutig. Adrian ist dies 2015 vorbildlich gelungen. Anschaulich beschreibt er die Grundlagen von Docker, das sich darum entwickelnde Öko-system sowie aktuelle Trends. Mit einem durchgängigen Beispiel bietet er einen praktischen Einstieg und zeigt, wie Sie erste Schritte mit Docker gehen.

Die Idee einer deutschen Übersetzung betrachtete ich anfangs allerdings skeptisch: Konnte sie überhaupt die zahlreichen Neuerungen des Docker-Ökosystems seit Erscheinen des englischen Originals angemessen darstellen? Zwischen Version 1.8 und der heute aktuellen Version 1.12 von Docker gab es ja einige Erweiterungen und Veränderungen, z.B. was Netzwerke und die Orchestrierung von Containern angeht.

Nachdem ich mich im Auftrag des Verlages dann im Detail mit dem Manuskript beschäftigte, war ich allerdings erst erleichtert und dann begeistert: Adrians Text war so angelegt, dass eigentlich nichts falsch oder komplett umzubauen war. Nur an wenigen Stellen mussten Textstellen leicht umformuliert sowie Hinweise auf Neuerungen hinzugefügt werden, damit das Buch auch weiterhin einen gelungenen praktischen Einstieg in die Technologie von Docker bietet. Die Grundlagen von Docker sind stabile und müssen erlernt werden. Ohne gute Kenntnisse der Docker Basics kann man die großartigen Neuerungen in den Bereichen Netzwerk, Storage, Clustering und Betrieb von Docker nicht wirklich sicher nutzen.

Lieben Dank an den Übersetzer Thomas Demmig sowie René Schönfeldt und sein Verlagsteam, dass sie mir die Chance gegeben haben, Teil dieses Projektes zu sein.

Viel Spaß beim Lesen, Probieren, Übertragen und Einsetzen von Docker!

August 2016Peter Roßbach

Vorwort

Container sind eine schlanke und portable Möglichkeit, beliebige Anwendungen und ihre Abhängigkeiten zu verpacken und transportabel zu machen.

Wenn man das so aufschreibt, klingt es ziemlich trocken und langweilig. Aber die Verbesserungen im Prozess, die durch Container möglich werden, sind das genaue Gegenteil davon – korrekt eingesetzt können Container wegweisend sein. Die Verlockungen der dadurch möglich werdenden Architekturen und Arbeitsabläufe sind so überzeugend, dass man glauben mag, spätestens in einem Jahr hat sich in jeder größeren IT-Firma der Status von »Docker? Nie gehört!« zu »Klar, schauen wir uns gerade an und setzen wir auch schon probehalber ein« geändert hat.

Der Aufstieg von Docker ist erstaunlich. Ich kann mich an keine Technologie erinnern, die so schnell so tiefgreifende Auswirkungen auf die IT-Branche hatte. Dieses Buch ist mein Versuch, Ihnen zu zeigen, warum Container so wichtig sind, was es Ihnen bringt, Ihre Umgebung zu containerisieren, und – am wichtigsten – wie Sie das erreichen.

Wer dieses Buch lesen sollte

Dieses Buch versucht, Docker ganzheitlich anzugehen, die Gründe für dessen Einsatz zu erklären und zu zeigen, wie man es nutzt und in einen Softwareentwicklungsworkflow integriert. Es behandelt den gesamten Software-Lifecycle – von der Entwicklung über die Produktion bis zur Wartung.

Ich habe versucht, möglichst wenige Annahmen über Sie als Leser zu treffen, und gehe nur davon aus, dass Sie grundlegende Kenntnisse von Linux und der Softwareentwicklung im Allgemeinen haben. Zielgruppe sind vor allem Softwareentwickler, Administratoren und Systemverwalter (insbesondere solche, die einen DevOps-Ansatz verfolgen wollen). Aber auch technisch affine Manager und andere Interessierte sollten ebenfalls Gewinn aus diesem Buch ziehen.

Warum ich dieses Buch geschrieben habe

Ich befand mich in der glücklichen Lage, Docker kennenzulernen und einzusetzen, während es noch ganz am Anfang seines kometenhaften Aufstiegs stand. Als sich die Gelegenheit ergab, dieses Buch zu schreiben, ergriff ich sie mit beiden Händen. Wenn mein Geschreibsel einem von Ihnen dabei hilft, die Containerisierungs-Community zu verstehen und das Beste daraus zu machen, dann habe ich mehr erreicht als in all den Jahren der Softwareentwicklung zuvor.

Ich hoffe wirklich, dass Ihnen das Lesen dieses Buches Spaß macht und es Ihnen dabei hilft, beim Einsatz von Docker in Ihrer Umgebung vorwärtszukommen.

Wie dieses Buch aufgebaut ist

Dieses Buch ist in drei Abschnitte unterteilt:

Teil I beginnt mit der Erklärung, was Container sind und warum Sie an ihnen interessiert sein sollten. Dann folgt ein Tutorial-Kapitel mit den ersten Schritten beim Einsatz von Docker. Der Abschnitt endet mit einem langen Kapitel mit den zugrunde liegenden Konzepten und Technologien, die hinter Docker stecken, einschließlich eines Überblicks über die diversen Docker-Befehle.

Teil II erklärt, wie Sie Docker in einem Softwareentwicklungs-Lifecycle einsetzen. Zuerst wird gezeigt, wie Sie eine Entwicklungsumgebung einrichten, bevor Sie eine einfache Webanwendung erstellen, die für den Rest dieses Abschnitts als Beispiel dient. Die Kapitel drehen sich um Entwicklung, Testen und Integration, aber auch darum, wie Sie Container ausrollen und ein Produktivsystem effektiv überwachen und protokollieren.

In Teil III geht es um die fortgeschrittenen Details und die Tools und Techniken, die Sie brauchen, um Multihost-Cluster mit Docker-Containern sicher und zuverlässig zu betreiben. Haben Sie Docker schon im Einsatz und müssen herausfinden, wie Sie damit wachsen können oder Netzwerk- und Sicherheitsprobleme lösen, ist das Ihr Abschnitt.

Welchen Konventionen dieses Buch folgt

Die folgenden typografischen Konventionen werden in diesem Buch genutzt:

Kursiv

für neue Begriffe, URLs, E-Mail-Adressen, Dateinamen und Dateierweiterungen

Nichtproportionalschrift

für Programmlistings, aber auch für Codefragmente in Absätzen, wie zum Beispiel Variablen- oder Funktionsnamen, Datenbanken, Datentypen, Umgebungsvariablen, Anweisungen und Schlüsselwörter

fette Nichtproportionalschrift

für Befehle und anderen Text, der genau so vom Benutzer eingegeben werden sollte

kursive Nichtproportionalschrift

für Text, der vom Benutzer durch eigene Werte ersetzt werden sollte

Dieses Symbol steht für einen Tipp oder Vorschlag.

Dieses Symbol steht für einen allgemeinen Hinweis.

Dieses Symbol steht für eine Warnung oder Vorsichtsmaßnahme.

Codebeispiele

Zusätzliches Material (Codebeispiele, Übungen und so weiter) finden Sie (auf Englisch) zum Herunterladen auf https://github.com/using-docker.

Dieses Buch soll Ihnen bei der Arbeit helfen. Die Codebeispiele in diesem Buch können Sie im Allgemeinen in Ihren Programmen und Ihrer Dokumentation nutzen. Sie müssen uns nicht um Erlaubnis fragen, sofern Sie nicht einen signifikanten Anteil des Codes veröffentlichen. So erfordert zum Beispiel das Schreiben eines Programms, das diverse Codeschnipsel aus diesem Buch nutzt, keine Erlaubnis. Das Verkaufen oder Bereitstellen einer CD-ROM mit Beispielen aus diesem Buch benötigt hingegen eine Erlaubnis. Beantworten Sie eine Frage, indem Sie dieses Buch zitieren und Beispielcode daraus nutzen, benötigen Sie keine Erlaubnis. Übernehmen Sie einen deutlichen Teil des Beispielcodes für Ihre Produktdokumentation, müssen Sie dieses Vorhaben von uns genehmigen lassen.

Wir freuen uns über eine Quellenangabe, verlangen sie aber nicht unbedingt. Zu einer Quellenangabe gehören normalerweise Titel, Autor, Verlag und ISBN (zum Beispiel: »Adrian Mouat: Docker – Software entwickeln und deployen mit Containern. dpunkt.verlag 2016, ISBN 978-3-86490-384-7«).

Wenn Sie das Gefühl haben, dass Ihr Einsatz der Codebeispiele über die Grenzen des Erlaubten hinausgeht, können Sie uns über [email protected] erreichen.

Danksagungen

Ich bin für all die Hilfe, Ratschläge und Kritik unglaublich dankbar, die ich während des Schreibens dieses Buches erhalten habe. Wenn ich einen Namen in der folgenden Liste vergessen habe, bitte ich um Verzeihung – ich wusste alle Beiträge zu schätzen, auch wenn ich nicht darauf reagiert habe.

Für allgemeines Feedback möchte ich mich bei Ally Hume, Tom Sugden, Lukasz Guminski, Tilaye Alemu, Sebastien Goasguen, Maxim Belooussov, Michael Boelen, Ksenia Burlachenko, Carlos Sanchez, Daniel Bryant, Christoffer Holmstedt, Mike Rathbun, Fabrizio Soppelsa, Yung-Jin Hu, Jouni Miikki und Dale Bewley bedanken.

Mein Dank für technische Diskussionen und Erläuterungen zu bestimmten Technologien geht an Andrew Kennedy, Peter White, Alex Pollitt, Fintan Ryan, Shaun Crampton, Spike Curtis, Alexis Richardson, Ilya Dmitrichenko, Casey Bisson, Thijs Schnitger, Sheng Liang, Timo Derstappen, Puja Abbassi, Alexander Larsson und Kelsey Hightower. Für die Erlaubnis, monsterid.js nutzen zu dürfen, bedanke ich mich bei Kevin Gaudin.

Ein großer Dank für all die Hilfe geht auch an die O’Reilly-Mitarbeiter, insbesondere an meinen Lektor Brian Anderson und an Meghan Blanchette, die das Ganze erst ins Rollen gebracht hat.

Diogo Mónica und Mark Coleman – vielen Dank an euch beide für das Beantworten meines Hilferufs in letzter Sekunde.

Zwei Firmen sollen besonders hervorgehoben werden: Container Solutions und CloudSoft. Jamie Dobson und Container Solutions sorgten dafür, dass ich mit Bloggen und Vorträgen beschäftigt war, und brachten mich mit vielen Leuten zusammen, die Einfluss auf dieses Buch hatten. CloudSoft erlaubten es mir freundlicherweise, in ihren Räumen an diesem Buch zu arbeiten. Zudem organisierten sie das Edinburgh Docker Meetup, das ebenfalls sehr wichtig für mich war.

Dafür, dass Ihr mein Gejammer über dieses Buch und meine Obsession ausgehalten habt, möchte ich mich bei all meinen Freunden und meiner Familie bedanken – Ihr wisst, wen ich meine (und werdet diese Zeilen sehr wahrscheinlich sowieso nicht lesen).

Schließlich möchte ich den DJs von BBC 6 Music danken, die den Soundtrack zu diesem Buch geliefert haben – unter anderem Lauren Laverne, Radcliffe and Maconie, Shaun Keaveny und Iggy Pop.

Inhaltsverzeichnis

Teil I   Hintergrund und Grundlagen

1          Was Container sind und warum man sie nutzt

1.1       Container versus VMs

1.2       Docker und Container

1.3       Eine Geschichte von Docker

1.4       Plugins und Plumbing

1.5       64-Bit-Linux

2          Installation

2.1       Docker auf Linux installieren

2.1.1       SELinux im Permissive Mode ausführen

2.1.2       Ohne sudo starten

2.2       Docker auf Mac OS oder Windows installieren

2.3       Ein schneller Check

3          Erste Schritte

3.1       Ihr erstes Image ausführen

3.2       Die grundlegenden Befehle

3.3       Images aus Dockerfiles erstellen

3.4       Mit Registries arbeiten

3.4.1       Private Repositories

3.5       Das offizielle Redis-Image verwenden

3.6       Zusammenfassung

4          Grundlagen von Docker

4.1       Die Architektur von Docker

4.1.1       Zugrunde liegende Technologien

4.1.2       Zugehörige Technologien

4.1.3       Docker Hosting

4.2       Wie Images gebaut werden

4.2.1       Der Build Context

4.2.2       Imageschichten

4.2.3       Caching

4.2.4       Basis-Images

4.2.5       Anweisungen im Dockerfile

4.3       Container mit der Außenwelt verbinden

4.4       Container verlinken

4.5       Daten mit Volumes und Datencontainern verwalten

4.5.1       Daten gemeinsam nutzen

4.5.2       Datencontainer

4.6       Häufig eingesetzte Docker-Befehle

4.6.1       Der Befehl run

4.6.2       Container verwalten

4.6.3       Docker-Info

4.6.4       Container-Info

4.6.5       Arbeit mit Images

4.6.6       Die Registry verwenden

4.7       Zusammenfassung

Teil II   Der Software-Lebenszyklus mit Docker

5          Docker in der Entwicklung einsetzen

5.1       Sag »Hallo Welt!«

5.2       Mit Compose automatisieren

5.2.1       Der Compose-Workflow

5.3       Zusammenfassung

6          Eine einfache Webanwendung erstellen

6.1       Eine einfache Webseite erstellen

6.2       Auf vorhandene Images zurückgreifen

6.3       Caching ergänzen

6.4       Microservices

6.5       Zusammenfassung

7          Bereitstellen von Images

7.1       Namensgebung für Images und Repositories

7.2       Der Docker Hub

7.3       Automatisierte Builds

7.4       Private Distribution

7.4.1       Eine eigene Registry betreiben

7.4.2       Kommerzielle Registries

7.5       Die Imagegröße verringern

7.6       Herkunft eines Image

7.7       Zusammenfassung

8          Continuous Integration und Testen mit Docker

8.1       identidock mit Unit-Tests versehen

8.2       Einen Jenkins-Container erstellen

8.2.1       Builds triggern

8.3       Das Image pushen

8.3.1       Sinnvolles Taggen

8.3.2       Staging und Produktion

8.3.3       Image Sprawl

8.3.4       Jenkins Slaves durch Docker betreiben

8.4       Backups für Jenkins

8.5       Gehostete CI-Lösungen

8.6       Testen und Microservices

8.6.1       Im Produktivumfeld testen

8.7       Zusammenfassung

9          Container deployen

9.1       Ressourcen mit Docker Machine aufsetzen

9.2       Einen Proxy verwenden

9.3       Ausführungsoptionen

9.3.1       Shell-Skripten

9.3.2       Einen Process Manager einsetzen (oder systemd, sie alle zu knechten)

9.3.3       Ein Tool zum Configuration Management einsetzen

9.4       Host-Konfiguration

9.4.1       Ein Betriebssystem wählen

9.4.2       Einen Storage-Treiber wählen

9.5       Spezialisierte Hosting-Möglichkeiten

9.5.1       Triton

9.5.2       Google Container Engine

9.5.3       Amazon EC2 Container Service

9.5.4       Giant Swarm

9.6       Persistente Daten und Produktivcontainer

9.7       Gemeinsame Geheimnisse

9.7.1       Geheimnisse im Image ablegen

9.7.2       Geheimnisse in Umgebungsvariablen übergeben

9.7.3       Geheimnisse in Volumes übergeben

9.7.4       Einen Key/Value-Store einsetzen

9.8       Vernetzen

9.9       Produktiv-Registry

9.10     Continuous Deployment/Delivery

9.11     Zusammenfassung

10        Protokollieren und Überwachen

10.1     Protokollieren

10.1.1     Standard-Logging von Docker

10.1.2     Logs zusammenfassen

10.1.3     Mit ELK loggen

10.1.4     Docker-Logging mit syslog

10.1.5     Logs aus Dateien auslesen

10.2     Überwachen und Benachrichtigen

10.2.1     Mit den Docker-Tools überwachen

10.2.2     cAdvisor

10.2.3     Cluster-Lösungen

10.3     Kommerzielle Monitoring- und Logging-Lösungen

10.4     Zusammenfassung

Teil III   Tools und Techniken

11        Vernetzung und Service Discovery

11.1     Ambassadors

11.2     Service Discovery

11.2.1     etcd

11.2.2     SkyDNS

11.2.3     Consul

11.2.4     Registrieren

11.2.5     Andere Lösungen

11.3     Networking-Optionen

11.3.1     Bridge

11.3.2     Host

11.3.3     Container

11.3.4     None

11.4     Neues Docker-Networking

11.4.1     Netzwerktypen und Plugins

11.5     Vernetzungslösungen

11.5.1     Overlay

11.5.2     Weave

11.5.3     Flannel

11.5.4     Project Calico

11.6     Zusammenfassung

12        Orchestrieren, Clustering und Verwaltung

12.1     Clustering- und Orchestrierungstools

12.1.1     Swarm

12.1.2     fleet

12.1.3     Kubernetes

12.1.4     Mesos und Marathon

12.2     Container-Management-Plattformen

12.2.1     Rancher

12.2.2     Clocker

12.2.3     Tutum

12.3     Zusammenfassung

13        Container sichern und beschränken

13.1     Worüber Sie sich Gedanken machen sollten

13.2     Verteidigung in der Tiefe

13.2.1     Least Privilege

13.3     identidock absichern

13.4     Container nach Host trennen

13.5     Updates anwenden

13.5.1     Nicht unterstützte Treiber vermeiden

13.6     Imageherkunft

13.6.1     Docker Digests

13.6.2     Docker Content Trust

13.6.3     Reproduzierbare und vertrauenswürdige Dockerfiles

13.7     Sicherheitstipps

13.7.1     Einen Benutzer setzen

13.7.2     Netzwerkzugriffe von Containern beschränken

13.7.3     setuid/setgid-Binaries entfernen

13.7.4     Den Speicher begrenzen

13.7.5     Den CPU-Einsatz beschränken

13.7.6     Neustarts begrenzen

13.7.7     Zugriffe auf die Dateisysteme begrenzen

13.7.8     Capabilities einschränken

13.7.9     Ressourcenbeschränkungen (ulimits) anwenden

13.8     Einen gehärteten Kernel ausführen

13.9     Linux Security Modules

13.9.1     SELinux

13.9.2     AppArmor

13.10   Auditing

13.11   Reaktion auf Zwischenfälle

13.12   Zukünftige Features

13.13   Zusammenfassung

Index

Teil IHintergrund und Grundlagen

Im ersten Teil dieses Buches schauen wir uns zunächst an, was Container sind und warum sie sich so großer Beliebtheit erfreuen. Darauf folgt eine Einführung in Docker und die Schlüsselkonzepte, die Sie verstehen müssen, um Container wirklich sinnvoll einzusetzen.

1 Was Container sind und warum man sie nutzt

Container ändern die Art und Weise, wie wir Software entwickeln, verteilen und laufen lassen, grundlegend. Entwickler können Software lokal bauen, weil sie wissen, dass sie auch woanders genauso laufen wird – sei es ein Rack in der ITAbteilung, der Laptop eines Anwenders oder ein Cluster in der Cloud. Administratoren können sich auf die Netzwerke, Ressourcen und die Uptime konzentrieren und müssen weniger Zeit mit dem Konfigurieren von Umgebungen und dem Kampf mit Systemabhängigkeiten verbringen. Der Einsatz von Containern wächst in der gesamten Branche mit einer erstaunlichen Geschwindigkeit – von den kleinsten Startups bis hin zu großen Unternehmen. Entwickler und Administratoren sollten davon ausgehen, dass sie innerhalb der nächsten Jahre Container regelmäßig einsetzen werden.

Container sind eine Verkapselung einer Anwendung und ihrer Abhängigkeiten. Auf den ersten Blick scheint das nur eine abgespeckte Version einer virtuellen Maschine (VM) zu sein – wie eine VM findet sich in einem Container eine isolierte Instanz eines Betriebssystems (Operating System, OS), mit dem wir Anwendungen laufen lassen können.

Container haben aber eine Reihe von Vorteilen, durch die Anwendungsfälle möglich werden, welche mit klassischen VMs schwierig oder unmöglich zu realisieren wären:

Container teilen sich Ressourcen mit dem Host-Betriebssystem, wodurch sie um eine wesentliche Größenordnung effizienter sind als virtuelle Maschinen. Container können im Bruchteil einer Sekunde gestartet und gestoppt werden. Anwendungen, die in Containern laufen, verursachen wenig bis gar keinen Overhead im Vergleich zu Anwendungen, die direkt auf dem Host-Betriebs-system gestartet werden.

Die Portierbarkeit von Containern besitzt das Potenzial, eine ganze Klasse von Bugs auszumerzen, die durch subtile Änderungen in der Laufzeitumgebung entstehen – sie könnte sogar die seit Anbeginn der Softwareentwicklung bestehende Litanei der Entwickler »Aber bei mir auf dem Rechner lief es doch!« beenden.

Die leichtgewichtige Natur von Containern sorgt dafür, dass Entwickler dutzende davon zur gleichen Zeit laufen lassen können, wodurch das Emulieren eines produktiv nutzbaren, verteilten Systems möglich wird. Administratoren können viel mehr Container auf einer einzelnen Host-Maschine laufen lassen, als dies mit VMs möglich wäre.

Container haben zudem Vorteile für Endanwender und Entwickler außerhalb des Bereitstellens in der Cloud. Benutzer können komplexe Anwendungen herunterladen und laufen lassen, ohne sich Stunden mit Konfiguration und Installation herumzuschlagen oder über die Änderungen Sorgen machen zu müssen, die am System notwendig wären. Umgekehrt brauchen sich die Entwickler solcher Anwendungen nicht mehr um solche Unterschiede in den Benutzerumgebungen und um eventuelle Abhängigkeiten Gedanken machen.

Wichtiger ist noch, dass sich die grundlegenden Ziele von VMs und Containern unterscheiden – eine VM ist dafür gedacht, eine fremde Umgebung vollständig zu emulieren, während ein Container Anwendungen portabel und in sich abgeschlossen macht.

1.1 Container versus VMs

Obwohl Container und VMs auf den ersten Blick sehr ähnlich wirken, gibt es einige wichtige Unterschiede, die sich am einfachsten über ein Schaubild aufzeigen lassen.

Abb. 1–1 Drei VMs laufen auf einem Host.

In Abbildung 1–1 sind drei Anwendungen zu sehen, die auf einem Host in getrennten VMs laufen. Der Hypervisor1 wird dazu benötigt, VMs zu erstellen und laufen zu lassen, den Zugriff auf das zugrunde liegende Betriebssystem und die Hardware zu steuern und bei Bedarf Systemaufrufe umzusetzen. Jede VM erfordert eine vollständige Kopie des Betriebssystems für sich, dazu die gewünschte Anwendung und alle Bibliotheken, die dafür notwendig sind.

Abb. 1–2 Drei Container laufen auf einem Host.

Im Gegensatz dazu sehen Sie in Abbildung 1–2, wie die gleichen drei Anwendungen in einem containerisierten System laufen könnten. Anders als bei VMs wird der Kernel des Host2 von den laufenden Containern gemeinsam genutzt. Sie sind also immer darauf beschränkt, den gleichen Kernel zu nutzen wie der Host. Die Anwendungen Y und Z verwenden die gleichen Bibliotheken, und sie müssen dafür keine identischen Kopien davon haben, sondern können auf die gleichen Dateien zugreifen. Die Container Engine ist für das Starten und Stoppen von Containern genauso verantwortlich wie der Hypervisor bei einer VM. Aber Prozesse, die innerhalb von Containern laufen, entsprechen nativen Prozessen auf dem Host, und es kommt kein Overhead durch die Ausführung des Hypervisors hinzu.

Sowohl VMs wie auch Container können genutzt werden, um Anwendungen von anderen Anwendungen zu isolieren, die auf dem gleichen Host laufen. VMs haben durch den Hypervisor eine weitgehendere Isolation, und es handelt sich bei ihnen um eine vertraute und durch Erfahrung gehärtete Technologie. Container sind verglichen damit recht neu, und viele Firmen scheuen sich, den Isolations-Features von Containern zu trauen, bevor diese ihr Können gezeigt haben. Aus diesem Grund findet man häufig Hybridsysteme mit Containern, die innerhalb von VMs laufen, um die Vorteile beider Technologien vereinen zu können.

1.2 Docker und Container

Container sind ein altes Konzept. Schon seit Jahrzehnten gibt es in UNIX-Systemen den Befehl chroot, der eine einfache Form der Dateisystem-Isolation bietet. Seit 1998 gibt es in FreeBSD das Jail-Tool, welches das chroot-Sandboxing auf Prozesse erweitert. Solaris Zones boten 2001 eine recht vollständige Technologie zum Containerisieren, aber diese war auf Solaris OS beschränkt. Ebenfalls 2001 veröffentlichte Parallels Inc. (damals noch SWsoft) die kommerzielle Container-technologie Virtuozzo für Linux, deren Kern später (im Jahr 2005) als Open Source unter dem Namen OpenVZ bereitgestellt wurde.3 Dann startete Google die Entwicklung von CGroups für den Linux-Kernel und begann damit, seine Infrastruktur in Container zu verlagern. Das Linux Containers Project (LXC) wurde 2008 initiiert, und in ihm wurden (unter anderem) CGroups, Kernel-Namensräume und die chroot-Technologie zusammengeführt, um eine vollständige Containerisierungslösung zu bieten. 2013 lieferte Docker schließlich die fehlenden Teile für das Containerisierungspuzzle, und die Technologie begann, den Mainstream zu erreichen.

Docker nahm die bestehende Linux-Containertechnologie auf und verpackte und erweiterte sie in vielerlei Hinsicht – vor allem durch portable Images und eine benutzerfreundliche Schnittstelle –, um eine vollständige Lösung für das Erstellen und Verteilen von Containern zu schaffen. Die Docker-Plattform besteht vereinfacht gesagt aus zwei getrennten Komponenten: der Docker Engine, die für das Erstellen und Ausführen von Containern verantwortlich ist, sowie dem Docker Hub, einem Cloud Service, um Container-Images zu verteilen.

Die Docker Engine bietet eine schnelle und bequeme Schnittstelle für das Ausführen von Containern. Zuvor waren für das Laufenlassen eines Containers mit einer Technologie wie LXC umfangreiches Wissen und viel manuelle Arbeit nötig. Auf dem Docker Hub finden sich unglaublich viele frei verfügbare Container-Images zum Herunterladen, so dass Anwender schnell loslegen können und es vermeiden, Arbeit doppelt zu erledigen, die andere schon gemacht hatten. Zu weiteren Tools, die von Docker entwickelt wurden, gehören der Clustering Manager Swarm, die GUI Kitematic für die Arbeit mit Containern und das Befehlszeilentool Machine für die Erzeugung von Docker Hosts.

Durch das Bereitstellen der Docker Engine als Open Source konnte Docker eine große Community aufbauen und auf deren Hilfe bei Bugfixes und Verbesserungen zählen. Das massive Wachstum von Docker hat dazu geführt, dass es ein De-facto-Standard wurde, und das wiederum sorgte für Druck aus der Branche, einen unabhängigen, formalen Standard für die Runtime und das Format der Container zu entwickeln. 2015 schließlich wurde dafür die Open Container Initiative4 gegründet – eine Initiative, die von Docker, Microsoft, CoreOS und vielen weiteren wichtigen Gruppen und Firmen unterstützt wird. Ihre Mission ist das Entwickeln solch eines Standards. Das Containerformat und die Runtime von Docker dienen dabei als Ausgangsbasis Seit der Version 1.11 basiert die Docker Engine auf dem RunC-Kern, der eine Implementierung des Open-Container-Standards ist.

Die wachsende Verbreitung von Containern geht vor allem auf Entwickler zurück, die nun erstmals Tools zur Verfügung hatten, um Container effektiv zu nutzen. Die kurze Startzeit von Docker-Containern hat für die Entwickler einen hohen Stellenwert, weil sie natürlich schnelle und iterative Entwicklungszyklen bevorzugen, in denen sie die Ergebnisse von Codeänderungen möglichst direkt sehen können. Die Portierbarkeits- und Isolationsgarantien von Containern vereinfachen die Zusammenarbeit mit anderen Entwicklern und Administratoren: Entwickler können sicher sein, dass ihr Code in allen Umgebungen laufen wird, während sich die Administratoren auf das Hosten und Orchestrieren von Containern konzentrieren können, statt sich mit dem Code herumschlagen, der darin läuft.

Die Änderungen, die Docker angestoßen hat, sorgen für eine deutlich unterschiedliche Art und Weise, wie wir Software entwickeln. Ohne Docker würden Container noch für eine lange Zeit bei der IT vergessen sein.

Die Fracht-Metapher

Die Docker-Philosophie wird häufig mit einer Frachtcontainer-Metapher erläutert, was auch den Namen »Docker« erklärt. Meist liest sich das wie folgt:

Wenn Güter transportiert werden, sind dafür sehr verschiedene Hilfsmittel notwendig, zum Beispiel Lastwagen, Gabelstapler, Kräne, Züge und Schiffe. All diese Hilfsmittel müssen sehr unterschiedliche Güter mit unterschiedlichen Größen und Anforderungen bewegen können (zum Beispiel Kaffeesäcke, Fässer mit gefährlichen Chemikalien, Kisten mit Elektronik, teure Autos oder Rollwagen mit gefrorenem Lammfleisch). Früher war das ein aufwendiger und teurer Prozess, für den viel manuelle Arbeit notwendig war, zum Beispiel durch Dockarbeiter, um die Güter per Hand an jeder Umladestelle aus- und wieder einzuladen (siehe Abbildung 1–3).

Das Transportgewerbe wurde durch die Einführung der intermodalen Container revolutioniert. Diese Container gibt es in Standardgrößen, und sie sind so entworfen, dass sie mit minimalem manuellen Aufwand zwischen den Verkehrsmitteln umgeladen werden können. Alle entsprechenden Hilfsmittel sind darauf ausgerichtet – Gabel-stapler und Kräne, Lastwagen, Züge und Schiffe. Es gibt Kühl- und Isoliercontainer für das Transportieren temperaturempfindlicher Güter, wie zum Beispiel Lebensmittel oder Arzneimittel. Die Vorteile der Standardisierung wurden auch auf zugehörige Systeme ausgeweitet, wie zum Beispiel das Beschriften und Versiegeln der Container. So können sich die Produzenten der Güter um die Inhalte und die Transportunternehmen um das Verschicken und Lagern der Container kümmern.

Abb. 1–3 Dockarbeiter (»Dockers«) in Bristol im Jahr 1940 (Ministry of Information Photo Division Photographer)

Das Ziel von Docker ist, die Vorteile der Containerstandardisierung in die IT zu übertragen. In den letzten Jahren ist die Zahl der unterschiedlichen Softwaresysteme massiv gestiegen. Es ist lange her, dass es ausgereicht hat, einen LAMP-Stack5 auf einem einzelnen Rechner laufen zu lassen. Zu einem typischen modernen System können zum Beispiel JavaScript-Frameworks, NoSQL-Datenbanken, Message Queues, REST-APIs und Backends gehören, die in unterschiedlichsten Programmiersprachen geschrieben wurden. Dieser Stack muss dann teilweise oder vollständig auf einer Vielzahl unterschiedlicher Hardware laufen – vom Laptop des Entwicklers über die Inhouse-Testing-Cluster bis hin zum Cloud Provider für das Produktivsystem. Jede dieser Umgebungen ist anders, auf jeder läuft ein anderes Betriebssystem mit anderen Bibliotheksversionen und anderer Hardware. Kurz gesagt: Wir haben ein ähnliches Szenario wie im Transportgewerbe – fortlaufend ist viel manuelle Arbeit notwendig, um den Code zwischen den Umgebungen zu transportieren.

So wie die intermodalen Container das Transportieren von Gütern vereinfacht haben, vereinfachen Docker-Container den Transport von Softwareanwendungen. Entwickler können sich darauf konzentrieren, die Anwendung zu bauen und sie in die Test- und Produktivumgebung zu verschieben, ohne sich um Unterschiede in den Umgebungen oder bei den Abhängigkeiten Gedanken machen zu müssen. Die Administratoren können sich auf die wichtigsten Elemente des Ausführens der Container konzentrieren – das Beschaffen von Ressourcen, das Starten und Stoppen von Containern und das Migrieren zwischen den Servern.

1.3 Eine Geschichte von Docker

2008 gründete Solomon Hykes dotCloud, um ein sprachunabhängiges Platform-as-a-Service-(PaaS-)Angebot aufzubauen. Diese Sprachunabhängigkeit war das Alleinstellungsmerkmal für dotCloud – bestehende PaaS waren an bestimmte Sprachen gebunden (so unterstützte Heroku zum Beispiel Ruby und die Google App Engine Java und Python). 2010 beteiligte sich dotCloud am Y Combinator Accelerator Program, wo es mit neuen Partnern zusammengebracht wurde und damit begann, ernsthafte Investitionen einzuwerben. Die entscheidende Wende kam im März 2013, als dotCloud Docker als Open Source bereitstellte – den zentralen Baustein von dotCloud. Während manche Firmen Angst davor gehabt hätten, ihre Geheimnisse preiszugeben, erkannte dotCloud, dass Docker sehr stark davon profitieren würde, wenn es ein durch eine Community unterstütztes Projekt würde.

Frühe Versionen von Docker waren nicht mehr als ein Wrapper für LXC, kombiniert mit einem geschichteten Dateisystem (Union-Dateisystem). Aber die Entwicklung ging ausgesprochen schnell voran: Innerhalb von sechs Monaten gab es mehr als 6700 Stars bei GitHub und 175 Contributors, die keine Mitarbeiter waren. Das führte dazu, dass dotCloud seinen Namen in Docker Inc. änderte und sein Geschäftsmodell anpasste. Docker 1.0 wurde im Juni 2014 veröffentlicht, nur 15 Monate nach der Version 0.1. Docker 1.0 stand für einen großen Schritt in Bezug auf Stabilität und Zuverlässigkeit – und wurde jetzt als »Production Ready« bezeichnet (in vielen Firmen hatte man es sogar schon zuvor tatsächlich produktiveingesetzt, wie zum Beispiel bei Spotify und Baidu). Gleichzeitig eröffnete Docker den Docker Hub – ein öffentliches Repository für Container. Damit war Docker nicht mehr nur eine einfache Container-Engine, sondern begann, sich zu einer vollständigen Plattform zu entwickeln.

Andere Firmen sahen schnell das Potenzial von Docker. Red Hat wurde im September 2013 ein wichtiger Partner und nutzte Docker, um sein OpenShift-Cloud-Angebot zu unterstützen. Google, Amazon und DigitalOcean boten bald Docker-Support für ihre Clouds an, und eine Reihe von Startups spezialisierte sich auf das Docker Hosting, wie zum Beispiel StackDock. Im Oktober 2014 gab Microsoft bekannt, dass zukünftige Versionen des Windows Server eine Unterstützung für Docker enthalten würden – was eine große Veränderung für eine Firma war, die klassischerweise mit aufgeblasener Firmensoftware in Verbindung gebracht wurde.

Auf der DockerConEU wurde im Dezember 2014 Docker Swarm angekündigt – ein Clustering Manager für Docker – sowie Docker Machine (Letzteres ein CLI-Tool für das Vorbereiten von Docker Hosts). Das war ein deutliches Signal für die Ziele von Docker: eine vollständige und integrierte Lösung für das Ausführen von Containern anzubieten und nicht nur selbst auf die Docker Engine beschränkt zu sein.

Im gleichen Monat kündigte CoreOS an, ihre eigene Container-Runtime rkt zu entwickeln und die appc-Containerspezifikation-S zu erstellen. Im Juni 2015 gaben Solomon Hykes von Docker und Alex Polvi von CoreOS auf der DockerCon in San Francisco die Gründung der Open Container Initiative bekannt (zunächst noch als Open Container Project bezeichnet), um einen gemeinsamen Standard für Containerformate und Runtimes zu entwickeln.

Ebenfalls im Juni 2015 kündigte das FreeBSD-Projekt an, dass Docker nun von FreeBSD unterstützt würde, wobei ZFS und der Linux Compatibility Layer zum Einsatz kommen. Im August 2015 veröffentlichten Docker und Microsoft ein »Tech Preview« der Docker Engine für Windows Server.

Mit dem Release 1.8 wurde von Docker das Content Trust Feature eingeführt, mit dem die Integrität und die Herausgeber von Docker-Images überprüft werden können. Content Trust ist eine entscheidende Komponente für den Aufbau vertrauenswürdiger Arbeitsabläufe, die auf aus Docker Registries stammenden Images basieren.

1.4 Plugins und Plumbing

Docker Inc. war sich schnell bewusst, dass es einen Großteil seines Erfolgs dem Ökosystem um sich herum zu verdanken hatte. Während sich Docker Inc. auf das Bereitstellen einer stabilen, produktiv einsetzbaren Version der Container-Engine konzentrierte, arbeiteten andere Firmen wie CoreOS, WeaveWorks und ClusterHQ an angrenzenden Bereichen, wie zum Beispiel dem Orchestrieren und Vernetzen von Containern. Es wurde aber schnell klar, dass Docker Inc. plante, eine vollständige Plattform zu liefern – mit Networking-, Storage- und Orchestrierungsmöglichkeiten. Um das Ökosystem aber weiter wachsen lassen zu können und um sicherzustellen, dass den Anwendern Lösungen für möglichst viele Szenarien zur Verfügung stehen, verkündete Docker Inc., dass es ein modulares, erweiterbares Framework für Docker schaffen würde, bei dem Komponenten durch entsprechende Elemente von dritter Seite ersetzbar oder erweiterbar wären. Docker Inc. nannte diese Philosophie »Batteries Included, But Replaceable«: Es würde eine vollständige Lösung angeboten, Teile ließen sich aber austauschen.6

Im Laufe der bisherigen Entwicklung hat sich dadurch eine umfangreiche Plugin-Infrastruktur entwickelt. So gab es z.B. schon früh eine Reihe von Plugins, um Container zu vernetzen und die Daten zu verwalten. Stetig wächst das PluginÖkosystem weiter und wird mit der Docker-Version 1.12 nun mit dem Befehl docker plugin verwaltbar. Der neue Docker Store wird zudem dafür sorgen, dass neben dem Open-Source-Angebot zukünftig auch kommerzielle Erweiterungen bereitstehen.

Docker folgt darüber hinaus dem sogenannten »Infrastructure Plumbing Manifesto«, das sich dazu bekennt, wann immer möglich bestehende Infrastrukturkomponenten wiederzuverwenden und zu verbessern und der Community wiederverwendbare Komponenten bereitzustellen, wenn neue Tools gebraucht werden. Das führte dazu, dass der Low-Level-Code für das Ausführen von Containern in das Projekt runC ausgelagert wurde, das vom OCI betreut wird und als Basis für andere Containerplattformen genutzt werden kann.

1.5 64-Bit-Linux

Bis Redaktionsschluss dieser Übersetzung (Docker 1.12) ist die einzige stabile, produktiv nutzbare Plattform für Docker ein 64-Bit-Linux. Auf Ihrem Computer muss also eine 64-Bit-Linux-Distribution laufen, und all Ihre Container werden ebenfalls 64-Bit-Linux nutzen. Sind Sie ein Windows- oder Mac-OS-Anwender, können Sie Docker in einer VM laufen lassen. Übrigens: Eine Version für den Raspberry Pi auf 32-Bit-Basis steht ebenfalls zur Verfügung: http://blog.hypriot.com/getting-started-with-docker-on-your-arm-device7

Die Unterstützung für andere native Container auf anderen Plattformen, unter anderem BSD, Solaris und Windows Server, steckt in verschiedenen Entwicklungsstadien. Da Docker keine native Virtualisierung bietet, müssen Container immer zum Host-Kernel passen – ein Windows-Server-Container kann nur auf einem Windows-Server-Host laufen und ein 64-Bit-Linux-Container nur auf einem 64-Bit-Linux-Host.

Microservices und Monolithen

Einer der größten Anwendungsfälle und die stärkste treibende Kraft hinter dem Aufstieg von Containern sind Microservices.

Microservices sind ein Weg, Softwaresysteme so zu entwickeln und zu kombinieren, dass sie aus kleinen, unabhängigen Komponenten bestehen, die untereinander über das Netz interagieren. Das steht im Gegensatz zum klassischen, monolithischen Weg der Softwareentwicklung, bei dem es ein einzelnes, großes Programm gibt, das meist in C++ oder Java geschrieben ist.

Wenn solch ein Monolith dann skaliert werden muss, kann man sich meist nur dazu entscheiden, vertikal zu skalieren (scale up), zusätzliche Anforderungen werden in Form von mehr RAM und mehr Rechenleistung bereitgestellt. Microservices sind dagegen so entworfen, dass sie horizontal skaliert werden können (scale out), indem zusätzliche Anforderungen durch mehrere Rechner verarbeitet werden, auf die die Last verteilt werden kann. In einer Microservices-Architektur ist es möglich, nur die Ressourcen zu skalieren, die für einen bestimmten Service benötigt werden, und sich damit auf die Flaschenhälse des Systems zu beschränken. In einem Monolith wird alles oder gar nichts skaliert, was zu verschwendeten Ressourcen führt.

Bezüglich Komplexität sind Microservices allerdings ein zweischneidiges Schwert. Jeder einzelne Microservice sollte leicht verständlich und einfach zu verändern sein. Aber in einem System, das aus dutzenden oder hunderten solcher Services besteht, steigt die Gesamtkomplexität aufgrund der Interaktion zwischen den einzelnen Komponenten.

Die leichtgewichtige Natur und Geschwindigkeit von Containern bedeutet, dass sie besonders gut dafür geeignet sind, mit ihnen eine Microservices-Architektur zu betreiben. Verglichen mit VMs sind Container deutlich kleiner und schneller ausrollbar, so dass Microservices-Architekturen möglichst wenig Ressourcen nutzen und schnell auf Anforderungsänderungen reagieren können.

Mehr Informationen zu Microservices finden Sie in Microservices – Grundlagen flexibler Softwarearchitekturen von Eberhard Wolff (dpunkt.verlag 2015, ISBN 978-3-86490-313-7, http://dpunkt.de/buecher/5026/9783864903137-microservices-12181.html).

2 Installation

Dieses Kapitel wird kurz die Schritte beschreiben, mit denen Sie Docker installieren. Es gibt abhängig vom verwendeten Betriebssystem ein paar Fallstricke, aber mit ein bisschen Glück sollte das Ganze problemlos ablaufen. Haben Sie schon eine neuere Version von Docker installiert (zum Beispiel 1.8 oder höher), können Sie dieses Kapitel ohne Bedenken überspringen.

2.1 Docker auf Linux installieren

Am besten installieren Sie Docker auf Linux über das von Docker bereitgestellte Installationsskript. Obwohl die meisten großen Linux-Distributionen eigene Pakete anbieten, hängen diese den Docker-Releases doch meist hinterher. Angesichts der Geschwindigkeit, mit der Docker weiterentwickelt wird, ist das durchaus kritisch.

Anforderungen von Docker

Docker hat keine großen Anforderungen, aber Sie müssen einen halbwegs aktuellen Kernel nutzen (für die grundlegenden Docker-Features momentan mindestens Version 3.18 oder noch besser 4.4 oder neuer, wenn Sie alle Features von Docker nutzen wollen). Sie können dies durch den Aufruf von uname -r überprüfen. Wenn Sie RHEL oder CentOS einsetzen, benötigen Sie Version 7 oder neuer.

Denken Sie auch daran, dass Sie eine 64-Bit-Architektur nutzen müssen. Das können Sie durch den Aufruf von uname -m herausfinden – das Ergebnis sollte x86_64 sein.

Sie sollten das Skript einsetzen können, das Sie auf https://get.docker.com finden, um Docker automatisch zu installieren. Die offizielle Anleitung wird Ihnen erklären, dass es reicht, einfach curl -sSL https://get.docker.com | sh oder wget -q0-https://get.docker.com | sh aufzurufen, und Sie dürfen das auch gerne tun, aber ich empfehle, sich das Skript vor dem Ausführen genauer anzuschauen, damit Sie auch sicher sind, dass Sie mit den vorgenommenen Änderungen an Ihrem System einverstanden sind:

$ curl-sSL https://get.docker.com > /tmp/install.sh$ cat /tmp/install.sh...$ chmod +x /tmp/install.sh$ /tmp/install.sh...

Das Skript führt ein paar Prüfungen durch und installiert dann Docker mit dem passenden Paket für Ihr System. Zudem installiert es ein paar zusätzliche Abhängigkeiten zu Sicherheits- und Dateisystem-Features, falls diese noch fehlen.

Wollen Sie den Installer nicht nutzen oder möchten Sie eine andere Version von Docker einsetzen als die, für die der Installer gedacht ist, können Sie auch ein Binary von der Docker-Website herunterladen. Der Nachteil dieses Vorgehens ist, dass keine Abhängigkeiten geprüft werden und dass Sie Updates manuell installieren müssen. Mehr Informationen und Links zu den Binaries finden Sie auf der Docker-Binary-Seite https://docs.docker.com/installation/binaries.

Mit Docker 1.8 getestet

Der englischen Originalausgabe dieses Buchs liegt die Docker-Version 1.8 zugrunde. Alle Befehle wurden mit dieser Version getestet.

2.1.1 SELinux im Permissive Mode ausführen

Arbeiten Sie auf einer auf Red Hat basierenden Distribution (einschließlich RHEL, CentOS oder Fedora), wird bei Ihnen vermutlich das SELinux-Sicherheitsmodul installiert sein.

Wenn Sie in Docker einsteigen, empfehle ich Ihnen, SELinux im Permissive Mode laufen zu lassen, bei dem Fehler protokolliert und nicht ausgegeben werden. Lassen Sie SELinux im Enforcing Mode laufen, werden Sie beim Ausprobieren der Beispiele in diesem Buch eine Reihe kryptischer »Permission Denied«-Fehler erhalten.

Um den SELinux-Modus zu prüfen, rufen Sie sestatus auf und schauen sich die Ausgabe an. Zum Beispiel:

$ sestatusSELinux status:                enabledSELinuxfs mount:               /sys/fs/selinuxSELinux root directory:        /etc/selinuxLoaded policy name:            targetedCurrent mode:                  enforcing  ➊Mode from config file:         error (Success)Policy MLS status:             enabledPolicy deny_unknown status:    allowedMax kernel policy version:     28

➊ Steht hier »enforcing«, dann ist SELinux aktiviert und setzt die Regeln auch um.

Um SELinux in den Permissive Mode zu wechseln, rufen Sie einfach sudo seten-force 0 auf.

Mehr Informationen zu SELinux und warum Sie sich überlegen sollten, es zu aktivieren, sobald Sie mehr Erfahrung mit Docker haben, finden Sie in Abschnitt 13.9.1.

2.1.2 Ohne sudo starten

Da Docker ein privilegiertes Binary ist, müssen wir den Befehlen standardmäßig sudo voranstellen, um sie ausführen zu können. Das wird schnell nervtötend. Wir können das aber umgehen, indem wir unseren Benutzer zur Gruppe docker hinzufügen. Unter Ubuntu sollten Sie Folgendes ausführen können:

$ sudo usermod -aG docker

Damit wird die Gruppe docker erstellt (wenn nicht schon vorhanden) und der aktuelle Benutzer hinzugefügt. Sie müssen sich nun ab- und wieder anmelden. Auf anderen Linux-Distributionen sollte es ähnlich funktionieren.

Auch müssen Sie den Docker-Service neu starten, was ebenfalls von der Distribution abhängt. Unter Ubuntu:

$ sudo service docker restart

Zur Vereinfachung wird in diesem Buch das sudo vor allen Docker-Befehlen weggelassen.

Wenn Sie einen Anwender zur Gruppe docker hinzufügen, entspricht das dem Ausstatten mit root-Privilegien. Das hat Folgen für die Sicherheit, deren Sie sich bewusst sein sollten – insbesondere, wenn Sie den Rechner zusammen mit anderen verwenden. Weitere Informationen dazu erhalten Sie auf der Sicherheitsseite von Docker (https://docs.docker.com/engine/security).

2.2 Docker auf Mac OS oder Windows installieren

Wenn Sie Windows oder Mac OS einsetzen, brauchen Sie irgendeine Form der Virtualisierung, um Docker laufen lassen zu können.1 Sie können entweder eine vollständige VM-Lösung herunterladen und den Anweisungen für Linux folgen, um Docker zu installieren, oder Sie richten sich die Docker Toolbox (https://www.docker.com/toolbox) ein, welche die minimale VM boot2docker sowie weitere Docker-Tools enthält, die wir in diesem Buch einsetzen, wie zum Beispiel Compose und Swarm. Nutzen Sie Homebrew, um auf Ihrem Mac Anwendungen zu installieren, gibt es ein Braurezept für boot2docker, aber im Allgemeinen empfehle ich den Einsatz der offiziellen Toolbox-Installation, um Probleme zu vermeiden.

Ist die Toolbox installiert, können Sie auf Docker zugreifen, indem Sie das Docker Quickstart Terminal öffnen.2 Alternativ können Sie ein bestehendes Terminal durch Eingabe folgender Befehle konfigurieren:

$ docker-machine start defaultStarting VM...Started machines may have new IP addresses. You may need to rerun the `docker-machine env` command.$ eval $(docker-machine env default)

So wird Ihre Umgebung mit den notwendigen Einstellungen versehen, um auf die in der VM laufende Docker Engine zugreifen zu können.

Achten Sie auf Folgendes, wenn Sie die Docker Toolbox einsetzen:

In den Beispielen in diesem Buch gehe ich davon aus, dass Docker auf dem Host-Rechner läuft. Nutzen Sie die Docker Toolbox, ist das nicht der Fall. Insbesondere müssen Sie Verweise auf localhost durch die IP-Adresse der VM austauschen. Zum Beispiel wird

$ curl localhost:5000

zu so etwas:

$ curl 192.168.99.100

Sie können die IP der VM problemlos herausfinden, indem Sie docker-machine ip default aufrufen, was eine gewisse Automatisierung ermöglicht:

$ curl $(docker-machine ip default):5000

Gemappte Volumes zwischen Ihrem lokalen Betriebssystem und dem Docker-Container müssen in der VM crossmounted werden. Die Docker Toolbox automatisiert das zu einem gewissen Grad, aber achten Sie darauf, dass dies im Hintergrund passiert, wenn Sie Probleme beim Einsatz von Docker Volumes haben.

Eventuell müssen Sie Einstellungen innerhalb der VM anpassen, wenn Sie besondere Anforderungen haben. Die Datei /var/lib/boot2docker/profile in der boot2docker-VM besitzt eine Reihe von Einstellungen, einschließlich der Konfiguration der Docker Engine. Sie können nach der VM-Initialisierung auch Ihre eigenen Skripten laufen lassen, indem Sie die Datei /var/lib/boot2-docker/bootlocal.sh anpassen. Weitere Informationen finden Sie im boot2docker GitHub-Repository unter https://github.com/boot2docker/boot2docker.

Haben Sie Probleme, die Beispiele in diesem Buch nachzuvollziehen, versuchen Sie, sich mit docker-machine ssh default direkt auf der VM anzumelden und die Befehle dort auszuführen.

Docker Experimental Channel

Neben dem normalen, stabilen Build bietet Docker auch einen experimentelle Build an, der zu Testzwecken die neuesten Features enthält. Da diese Features noch diskutiert und entwickelt werden, ist es recht wahrscheinlich, dass es deutliche Änderungen an ihnen gibt, bevor sie es in den stabilen Build schaffen. Der experimentelle Build sollte nur genutzt werden, wenn man neue Features ausprobieren möchte, bevor sie offiziell bereitgestellt werden. Nutzen Sie ihn nie in einer produktiven Umgebung.

Der experimentelle Build kann unter Linux mit folgendem Skript installiert werden:

$ curl -sSL https://experimental.docker.com/ | sh

Alternativ können Sie eine Binary-Version von der Docker-Website unter http://bit.ly/1Q8g39C herunterladen.

2.3 Ein schneller Check

Um sicherzustellen, dass alles sauber installiert ist und läuft, versuchen Sie, den Befehl docker version auszuführen. Sie sollten so etwas wie das Folgende erhalten:

$ docker versionClient:  Version:     1.8.1  API version: 1.20  Go version:  go1.4.2  Git commit:  d12ea79  Built:       Thu Aug 13 02:35:49 UTC 2015  OS/Arch:     linux/amd64Server:  Version:     1.8.1  API version: 1.20  Go version:  go1.4.2  Git commit:  d12ea79  Built:       Thu Aug 13 02:35:49 UTC 2015  OS/Arch:     linux/amd64

Wenn das funktioniert hat, ist alles für das nächste Kapitel bereit. Erhalten Sie dagegen etwas wie das Folgende:

$ docker versionClient:  Version:     1.8.1  API version: 1.20  Go version:  go1.4.2  Git commit:  d12ea79  Built:       Thu Aug 13 02:35:49 UTC 2015  OS/Arch:     linux/amd64Get http:///var/run/docker.sock/v1.20/version: dial unix /var/run/docker.sock: no such file or directory.* Are you trying to connect to a TLS-enabled daemon without TLS?* Is your docker daemon up and running?

dann bedeutet das, dass der Docker Daemon nicht läuft (oder der Client nicht darauf zugreifen kann). Um das Problem zu analysieren, versuchen Sie, den Docker Daemon manuell zu starten, indem Sie sudo docker daemon ausführen – dabei sollten Sie Informationen erhalten, was für ein Problem vorliegt, und Sie erhalten Stichworte für die Suche nach einer Lösung. (Beachten Sie, dass dies nur für einen Linux-Host funktioniert. Nutzen Sie die Docker Toolbox oder etwas Ähnliches, werden Sie in der Dokumentation stöbern müssen.)

3 Erste Schritte

In diesem Kapitel werden Sie Ihre ersten Schritte mit Docker unternehmen. Wir beginnen mit dem Starten und dem Einsatz von ein paar einfachen Containern, um Ihnen ein Gefühl dafür zu vermitteln, wie Docker funktioniert. Dann wechseln wir zu den Dockerfiles – den grundlegenden Bausteinen von Docker-Containern – und Docker Registries, welche das Ausrollen von Containern unterstützen. Das Kapitel endet mit einem Blick auf den Einsatz eines Containers, bei dem ein Key/Value-Store mit persistentem Storage gehostet wird.

3.1 Ihr erstes Image ausführen

Um zu prüfen, ob Docker korrekt installiert wurde, geben Sie Folgendes ein:

$ docker run debian echo "Hallo Welt"

Das wird – abhängig von Ihrer Internetverbindung – ein bisschen dauern, aber schließlich werden Sie eine Ausgabe wie die folgende erhalten:

Unable to find image 'debian' locallydebian:latest: The image you are pulling has been verified511136ea3c5a: Pull complete638fd9704285: Pull complete61f7f4f722fb: Pull completeStatus: Downloaded newer image for debian:latestHallo Welt

Was ist hier passiert? Wir haben den Befehl docker run gestartet, der dafür zuständig ist, Container zu starten. Das Argument debian ist der Name des Image,1 das wir nutzen wollen – in diesem Fall eine abgespeckte Version der Linux-Distribution Debian. Die erste Zeile der Ausgabe teilt uns mit, dass wir keine lokale Kopie des Debian-Image haben. Docker schaut dann online auf dem Docker Hub nach und lädt die neueste Version des Image herunter. Ist das Image da, verwandelt Docker es in einen laufenden Container und führt den angegebenen Befehl – echo"Hallo Welt" – darin aus. Das Ergebnis dieses Befehls ist dann als letzte Zeile der Ausgabe zu sehen.

Führen Sie den gleichen Befehl erneut aus, wird der Container direkt gestartet, ohne ihn vorher herunterzuladen. Das Ausführen sollte nur eine Sekunde dauern – was erstaunlich ist, wenn Sie bedenken, was alles passiert: Docker hat unseren Container vorbereitet und gestartet, unseren echo-Befehl ausgeführt und den Container dann wieder heruntergefahren. Wenn Sie so etwas mit einer klassischen VM probieren, warten Sie vermutlich einige Sekunden, wenn nicht sogar Minuten.

Wir können Docker bitten, uns mit folgendem Befehl eine Shell im Container bereitzustellen:

$ docker run -i -t debian /bin/bashroot@622ac5689680:/# echo "Hallo aus dem Container-Land!"Hallo aus dem Container-Land!root@622ac5689680:/# exitexit

Sie erhalten so einen neuen Befehls-Prompt innerhalb des Containers – so als ob Sie sich per ssh mit einem entfernten Rechner verbinden. In diesem Fall informieren die Flags -i und -t Docker darüber, dass wir eine interaktive Sitzung mit einem tty verknüpft haben wollen. Der Befehl /bin/bash liefert uns eine bash-Shell. Verlassen Sie die Shell, wird der Container angehalten – Container laufen nur so lange wie ihr Hauptprozess.

3.2 Die grundlegenden Befehle

Versuchen wir, Docker ein bisschen besser zu verstehen, indem wir einen Container starten und sehen, was die verschiedenen Befehle und Aktionen für Auswirkungen haben. Beginnen wir mit dem Starten eines neuen Containers – dieses Mal geben wir ihm aber mit dem Flag -h einen neuen Hostnamen:

$ docker run -h CONTAINER -i -t debian /bin/bashroot@CONTAINER:/#

Was passiert, wenn wir in einen Container löschen oder kaputt machen?

root@CONTAINER:/# mv /bin /basketroot@CONTAINER:/# lsbash: ls: command not found

Wir haben das Verzeichnis /bin verschoben und den Container damit ziemlich unbrauchbar gemacht – zumindest temporär.2 Bevor wir diesen Container wegschmeißen, wollen wir schauen, was ps, inspect und diff uns darüber erzählen können. Öffnen Sie ein neues Terminal (lassen Sie die Containersitzung laufen) und geben Sie für den Host docker ps ein. Sie werden eine Ausgabe wie diese erhalten:

CONTAINER ID  IMAGE   COMMAND      ...  NAMES00723499fdbf  debian  "/bin/bash"  ...  stupefied_turing

Das sagt uns ein paar Dinge über die aktuell laufenden Container. Der größte Teil der Ausgabe sollte selbsterklärend sein, beachten Sie aber, dass Docker dem Container einen lesbaren Namen verpasst hat, den Sie nutzen können, um ihn auf dem Host identifizieren zu können – in diesem Fall über »stupefied_turing«.3 Mehr Informationen zu einem bestimmten Container erhalten wir über den Aufruf von docker inspect mit dem Namen oder der ID des Containers:

$ docker inspect stupefied_turing[{    "Id": "00723499fdbfe55c14565dc53d61452519deac72e18a8a6fd7...",    "Created": "2015-09-14T09:47:20.2064793Z",    "Path": "/bin/bash",    "Args": [],    "State": {        "Running": true,...

Sie finden hier viele wertvolle Informationen, aber die Ausgabe ist nicht wirklich einfach zu parsen. Wir können grep oder das Argument --format verwenden (das ein Go-Template4 erwartet), um die für uns interessanten Informationen zu filtern. Zum Beispiel:

$ docker inspect stupefied_turing | grep IPAddress        "IPAddress": "172.17.0.4",        "SecondaryIPAddresses": null,$ docker inspect --format {{.NetworkSettings.IPAddress}} stupefied_turing172.17.0.4

Beide Befehle liefern uns die IP-Adresse des laufenden Containers. Aber jetzt wollen wir uns einem anderen Befehl zuwenden, nämlich docker diff:

$ docker diff stupefied_turingC /.wh..wh.plnkA /.wh..wh.plnk/101.715484D /binA /basketA /basket/bashA /basket/catA /basket/chaclA /basket/chgrpA /basket/chmod...

Wir sehen hier die Liste der Dateien, die sich im laufenden Container geändert haben – in diesem Fall das Löschen von /bin und das Hinzufügen von allem in /basket, aber auch das Erstellen von ein paar Dateien, die mit dem Storage-Treiber zu tun haben. Docker nutzt ein Union File System (UFS) für Container, bei dem mehrere Dateisysteme in einer Hierarchie gemountet werden können, die dann als ein einzelnes Dateisystem erscheinen. Das Dateisystem aus dem Image wurde als Read-Only-Layer gemountet, alle Änderungen im laufenden Container geschehen auf einem Read-Write-Layer, der darüber gemountet wurde. Daher muss Docker nur auf den obersten Read-Write-Layer schauen, um die Änderungen herauszufinden, die am laufenden System vorgenommen wurden.

Das Letzte, was ich Ihnen zeigen möchte, bevor wir mit diesem Container fertig sind, ist der Befehl docker logs. Lassen Sie diesen Befehl mit dem Namen Ihres Containers laufen, erhalten Sie eine Liste von allem, was innerhalb des Containers passiert ist:

$ docker logs stupefied_turingroot@CONTAINER:/# mv /bin /basketroot@CONTAINER:/# lsbash: ls: command not found

Wir brauchen unseren kaputten Container jetzt nicht mehr, also verwerfen wir ihn. Zuerst verlassen wir die Shell:

root@CONTAINER: /# exitexit$

Damit wird auch der Container gestoppt, da die Shell der einzige laufende Prozess war. Rufen Sie docker ps auf, sollten Sie keine laufenden Container mehr sehen.

Aber das ist nicht die ganze Geschichte. Geben Sie docker ps -a ein, erhalten Sie eine Liste aller Container – einschließlich der gestoppten (die offiziell als »exited« bezeichnet werden). Ein gestoppter Container kann durch docker start wieder neu gestartet werden (wobei wir allerdings die Pfade in diesem Container kaputtgemacht haben, er sich also nicht starten lassen wird). Um den Container loszuwerden, nutzen Sie den Befehl docker rm:

$ docker rm stupefied_turingstupefied_turing

Gestoppte Container aufräumen

Wollen Sie all Ihre gestoppten Container loswerden, können Sie die Ausgabe von docker ps -aq -f status=exited nutzen, die Ihnen die IDs aller gestoppten Container liefert. Zum Beispiel:

$ docker rm -v $(docker ps -aq -f status=exited)

Da es sich dabei um eine häufiger genutzte Operation handelt, werden Sie sie vielleicht in ein Shell-Skript oder einen Alias verpacken. Beachten Sie, dass das Argument -v alle von Docker verwalteten Volumes löscht, die nicht von anderen Containern referenziert werden.

Sie können vermeiden, dass sich gestoppte Container bei Ihnen stapeln, indem Sie docker run das Flag --rm mitgeben. Dadurch werden der Container und das zugehörige Dateisystem gelöscht, sobald der Container verlassen wird.

Okay, schauen wir mal, wie wir einen neuen, nützlichen Container bauen können, den wir tatsächlich behalten wollen.5 Wir werden eine dockerisierte cowsayAnwendung erstellen. Wenn Sie nicht wissen, was cowsay ist, schlage ich vor, sich damit vertraut zu machen. Beginnen wir damit, einen Container zu starten und ein paar Pakete zu installieren:

$ docker run -it --name cowsay --hostname cowsay debian bashroot@cowsay:/# apt-get update...Reading package lists... Doneroot@cowsay:/# apt-get install -y cowsay fortune...root@cowsay:/#

Dann mal los!

root@cowsay:/# /usr/games/fortune | /usr/games/cowsay _____________________________________/ Writing is easy; all you do is sit  \| staring at the blank sheet of paper || until drops of blood form on your   || forehead.                           ||                                     |\ -- Gene Fowler                      / -------------------------------------        \   ^__^        \   (oo)\_______            (__)\       )\/\                ||----w |                ||     ||

Toll! Diesen Container wollen wir aufheben.6 Um ihn in ein Image zu verwandeln, können wir einfach den Befehl docker commit nutzen. Es ist egal, ob der Container läuft oder gestoppt wurde. Wir müssen dem Befehl den Namen des Containers mitgeben (»cowsay«), dazu einen Namen für das Image (»cowsayimage«) und den Namen des Repository, in dem es gespeichert werden soll (»test«):

root@cowsay:/# exitexit$ docker commit cowsay test/cowsayimaged1795abbc71e14db39d24628ab335c58b0b45458060d1973af7acf113a0ce61d

Der zurückgegebene Wert ist die eindeutige ID unseres Image. Nun haben wir ein Image, in dem cowsay installiert ist und das wir ausführen können:

$ docker run test/cowsayimage /usr/games/cowsay "Muh" _____< Muh > -----      \   ^__^       \  (oo)\_______          (__)\       )\/\              ||----w |              ||     ||

Großartig! Es gibt allerdings ein paar Probleme. Müssen wir etwas ändern, müssen wir alle Schritte von Beginn an wiederholen. Wollen wir zum Beispiel ein anderes Basisimage nutzen, müssten wir ganz von vorne anfangen. Wichtiger noch: Es ist nicht leicht wiederholbar – die Schritte zum Erstellen des Image sind nur schwierig und möglicherweise fehlerbehaftet an andere weitergebbar. Die Lösung dazu ist der Einsatz eines Dockerfile, um einen automatischen Build für das Image zu erstellen.

3.3 Images aus Dockerfiles erstellen

Ein Dockerfile ist einfach eine Textdatei mit einer Reihe von Schritten, die genutzt werden können, um ein Docker-Image zu erzeugen. Legen Sie für dieses Beispiel zunächst einen neuen Ordner und eine Datei an:

$ mkdir cowsay$ cd cowsay$ touch Dockerfile

Nun fügen Sie den folgenden Inhalt in Dockerfile ein:

FROM debian:wheezyRUN apt-get update && apt-get install -y cowsay fortune

Die FROM-Anweisung legt fest, welches Basisimage zu verwenden ist (wie zuvor debian, aber dieses Mal haben wir angegeben, dass wir die Version haben wollen, die mit »wheezy« getaggt ist). Alle Dockerfiles müssen als erste Anweisung (die kein Kommentar ist) eine FROM-Anweisung besitzen. RUN-Anweisungen geben einen Shell-Befehl an, der im Image ausgeführt werden soll. In diesem Fall installieren wir nur cowsay und fortune – und zwar genauso, wie wir das zuvor manuell getan haben.

Wir können jetzt das Image erstellen, indem wir den Befehl docker build in diesem Verzeichnis ausführen:

$ lsDockerfile$ docker build -t test/cowsay-dockerfile .Sending build context to Docker daemon 2.048 kBStep 0 : FROM debian:wheezy ---> f6fab3b798beStep 1 : RUN apt-get update && apt-get install -y cowsay fortune ---> Running in 29c7bd4b0adc...Setting up cowsay (3.03+dfsg1-4) ... ---> dd66dc5a99bdRemoving intermediate container 29c7bd4b0adcSuccessfully built dd66dc5a99bd

Dann können wir das Image wie zuvor ausführen:

$ docker run test/cowsay-dockerfile /usr/games/cowsay "Muh"

Images, Container und das Union File System

Um die Beziehung zwischen Images und Containern zu verstehen, müssen wir ein Kernelement der Technologie erläutern, die Docker erst ermöglicht – das UFS (manchmal einfach als Union Mount bezeichnet). Union File Systems erlauben es, mehrere Dateisysteme so zu überlagern, dass sie dem Anwender als ein einzelnes Dateisystem erscheinen. Ordner können Dateien aus mehreren Dateisystemen enthalten, aber wenn zwei Dateien genau den gleichen Pfad besitzen, wird die zuletzt gemountete Datei alle vorigen Dateien verbergen. Docker unterstützt eine Reihe von UFS-Implementierungen, unter anderem AUFS, Overlay, devicemapperOverlay2, DM, BTRFS, VFS und ZFS. Es hängt vom System ab, welche Implementierung verwendet wird – Sie können dies mithilfe von docker info prüfen, wo es unter »Storage Driver« aufgeführt wird. Es ist möglich, das Dateisystem zu ändern, aber das empfiehlt sich nur, wenn Sie wissen, was Sie tun, und sich der Vor- und Nachteile bewusst sind.

Docker-Images bestehen aus mehreren Schichten. Jede dieser Schichten ist ein Read-Only-Dateisystem. Eine Schicht wird für jede Anweisung in einem Dockerfile erstellt, und diese sitzt auf den vorigen Schichten. Wird ein Image in einen Container verwandelt (mit den Befehlen docker run oder docker create), nimmt sich die Docker Engine das Image und fügt noch ein Read-Write-Dateisystem als Abschluss hinzu (neben dem Initialisieren einer Reihe von Einstellungen wie der IP-Adresse, dem Namen, der ID und den Ressourcengrenzen).

Da unnötige Schichten Images nur aufblähen (und das AUFS-Dateisystem eine feste Obergrenze von 127 Schichten besitzt), werden Sie feststellen, dass viele Dockerfiles versuchen, die Anzahl an Schichten zu minimieren, indem mehrere UNIX-Befehle in einer einzelnen RUN-Anweisung verpackt werden.

Ein Container kann sich in einem von mehreren Zuständen befinden: Created, Restarting, Running, Paused oder Exited. Ein »Created«-Container ist einer, der mit dem Befehl docker create initialisiert, aber noch nicht gestartet wurde. Der Status »Exited« wird meist als »Gestoppt/Stopped« bezeichnet und gibt an, dass es keine laufenden Prozesse innerhalb des Containers gibt (das gilt auch für »Created«-Container, aber ein gestoppter Container ist schon mindestens einmal gestartet worden. Ein Container wird gestoppt, wenn seine Hauptprozesse beendet sind. Ein gestoppter Container kann mit dem Befehl docker start wieder neu gestartet werden. Ein gestoppter Container ist nicht das Gleiche wie ein Image – er behält seine Änderungen an seinen Einstellungen, den Metadaten und dem Dateisystem bei, einschließlich der Laufzeitkonfiguration wie der IP-Adresse, was nicht in Images gespeichert ist. Dem Status »Restarting« begegnet man in der Praxis nur selten, und er tritt vor allem dann auf, wenn die Docker Engine versucht, einen fehlerhaften Container neu zu starten.

Aber wir können die Sache für den Anwender noch weiter vereinfachen, indem wir auf die Dockerfile-Anweisung ENTRYPOINT zurückgreifen. Mit ENTRY-POINT können wir eine ausführbare Datei angeben, an die alle Argumente weitergereicht werden, welche an docker run übergeben sind.

Fügen Sie die folgende Zeile am Ende des Dockerfiles hinzu:

ENTRYPOINT ["/usr/games/cowsay"]

Wir können nun das Image neu bauen und beim Aufruf den cowsay-Befehl weglassen:

$ docker build -t test/cowsay-dockerfile ....$ docker run test/cowsay-dockerfile "Muh"...

Viel einfacher! Jetzt haben wir allerdings nicht mehr die Möglichkeit, den fortune-Befehl im Container zu verwenden, um diesen als Eingabe an cowsay weiterzureichen. Das lässt sich beheben, indem wir unser eigenes Skript für ENTRYPOINT angeben – ein gebräuchliches Muster beim Erstellen von Dockerfiles. Legen Sie also eine Datei entrypoint.sh mit dem folgenden Inhalt an und speichern Sie sie im gleichen Verzeichnis wie das Dockerfile:7

#!/bin/bashif [ $# -eq 0 ]; then    /usr/games/fortune | /usr/games/cowsay  else    /usr/games/cowsay "$@"fi

Machen Sie die Datei mit chmod +x entrypoint.sh ausführbar.

Dieses Skript leitet nur die Eingabe von fortune weiter nach cowsay, wenn es ohne Argumente aufgerufen wird, andernfalls ruft es cowsay direkt mit den Argumenten auf. Wir müssen jetzt noch das Dockerfile anpassen, damit das Skript mit in das Image wandert, und es mit der ENTRYPOINT-Anweisung aufrufen. Passen Sie das Dockerfile so an, dass es wie folgt aussieht:

FROM debianRUN apt-get update && apt-get install -y cowsay fortuneCOPY entrypoint.sh /  ➊ENTRYPOINT ["/entrypoint.sh"]

➊ Die COPY-Anweisung kopiert einfach eine Datei vom Host in das Dateisystem des Image, wobei das erste Argument die Datei auf dem Host und das zweite der Zielpfad ist – also ganz ähnlich wie bei cp.

Versuchen Sie, ein neues Image zu bauen und den Container dann mit und ohne Argumente laufen zu lassen:

$ docker build -t test/cowsay-dockerfile .... Schnipp ...$ docker run test/cowsay-dockerfile ____________________________________/ The last thing one knows in        \| constructing a work is what to put || first.                             ||                                    |\ -- Blaise Pascal                   / ------------------------------------       \ ^__^        \ (oo)\_______          (__)\       )\/\              ||----w |              ||     ||$ docker run test/cowsay-dockerfile Hallo Muh ____________< Hallo Muh > ------------       \ ^__^        \ (oo)\_______          (__)\       )\/\              ||----w |              ||     ||

3.4 Mit Registries arbeiten

Nachdem wir schon etwas so Tolles geschaffen haben – wie können wir es da mit anderen teilen? Als wir das Debian-Image am Anfang des Kapitels starteten, wurde es aus der offiziellen Docker Registry heruntergeladen – dem Docker Hub. Genauso können wir unsere eigenen Images in den Docker Hub hochladen, damit andere sie wieder laden und einsetzen können.

Der Docker Hub kann sowohl über die Befehlszeile als auch über die Website angesprochen werden. Zum Suchen nach bestehenden Images nutzen Sie den search-Befehl von Docker oder Sie rufen https://hub.docker.com auf.

Registries, Repositories, Images und Tags

Es gibt ein hierarchisches System für das Speichern von Images. Dabei wird die folgende Terminologie verwendet:

Registry

Ein Service, der für das Hosten und Verteilen von Images zuständig ist. Die Standard-Registry ist der Docker Hub.

Repository

Eine Sammlung zusammengehöriger Images (oft handelt es sich um verschiedene Versionen der gleichen Anwendung oder des gleichen Service)

Tag

Eine alphanumerische Kennung, die mit den Images in einem Repository verknüpft ist (zum Beispiel 14.04 oder stable)

Der Befehl docker pull amouat/revealjs:latest lädt daher das Image mit dem Tag latest im Repository amouat/revealjs von der Docker Hub Registry herunter.

Um Ihr cowsay-Image hochzuladen, müssen Sie einen Account beim Docker Hub erstellen (entweder online oder mit dem Befehl docker login). Nachdem Sie das getan haben, müssen wir das Image nur noch in ein passendes Repository taggen und es mit docker push in den Docker Hub hochladen. Zuerst wollen wir aber im Dockerfile noch eine MAINTAINER-Anweisung ergänzen, durch das einfach die Kontaktinformationen des Autors angegeben werden:

FROM debianMAINTAINER John Smith <[email protected]>RUN apt-get update && apt-get install -y cowsay fortuneCOPY entrypoint.sh /ENTRYPOINT ["/entrypoint.sh"]

Jetzt wollen wir das Image neu bauen und in den Docker Hub hochladen. Dieses Mal müssen Sie einen Repository-Namen verwenden, der mit Ihrem Benutzernamen auf dem Docker Hub beginnt (in meinem Fall amouat), gefolgt von einem / und einem beliebigen Namen für Ihr Image. Zum Beispiel:

$ docker build -t amouat/cowsay ....$ docker push amouat/cowsayThe push refers to a repository [docker.io/amouat/cowsay] (len: 1)e8728c722290: Image successfully pushed5427ac510fe6: Image successfully pushed4a63ead8b301: Image successfully pushed73805e6e9ac7: Image successfully pushedc90d655b99b2: Image successfully pushed30d39e59ffe2: Image successfully pushed511136ea3c5a: Image successfully pushedlatest: digest: sha256:bfd17b7c5977520211cecb202ad73c3ca14acde6...

Da ich nach dem Repository-Namen kein Tag angegeben habe, wird automatisch latest zugewiesen. Um ein Tag mitzugeben, fügen Sie es einfach, getrennt durch einen Doppelpunkt, nach dem Repository-Namen, ein (zum Beispiel docker build -t amouat/cowsay:stable .).

Ist der Upload abgeschlossen, kann die ganze Welt Ihr Image über den Befehl docker pull herunterladen (zum Beispiel docker pull amouat/cowsay).

3.4.1 Private Repositories

Natürlich wollen Sie eventuell nicht, dass die ganze Welt auf Ihr Image zugreifen kann. In diesem Fall stehen Ihnen mehrere Optionen zur Verfügung. Sie können für ein gehostetes privates Repository bezahlen (auf dem Docker Hub oder ähnlichen Services wie quay.io) oder Sie betreiben Ihre eigene Registry. Mehr Informationen zu privaten Repositories und Registries finden Sie in Kapitel 7.

Imagenamensräume

Es gibt drei Namensräume, zu denen hochgeladene Docker-Images gehören können und die sich über den Imagenamen erkennen lassen:

Namen, vor denen ein String und ein / stehen, wie zum Beispiel amouat/revealjs, gehören zum »User«-Namensraum. Dabei handelt es sich um Images auf dem Docker Hub, die von einem Benutzer hochgeladen wurden. So ist amouat/revealjs