Ein Trend der letzten Jahre im Bereich der Softwareentwicklung ist das Zusammenführen von Entwicklung (Development) und Betrieb (Operator), der mit dem Begriff DevOps beschrieben wird. Passenderweise entstehen dazu auch weitere neue Konzepte und Werkzeuge, die mich als Entwickler (und jetzt auch DevOp) unterstützen.
Ein wichtiges Konzept ist dabei Infrastructure as Code, dass die Server-Infrastruktur als Programmcode revisioniert abgelegt wird und jederzeit automatisiert aufgesetzt werden kann. Die Virtualisierungssoftware Docker nutzt dieses Konzept und ermöglicht es Entwicklern, Server-Infrastruktur besonders einfach auf unterschiedlichsten Umgebungen bereitzustellen und zu betreiben.
Zunächst einmal ein kurzer Überblick zu den Vorteilen von Virtualisierung mit Docker und dann folgt ein kurzes Beispiel.
- einfach portierbare Docker-Container laufen auf unterschiedlichsten Umgebungen
- lokale Entwicklungsumgebung funktioniert identisch wie die produktive Umgebung
- Docker-Container lassen sich einfach ausrollen
- Anwendungen laufen in einer Docker-Sandbox isoliert
- Unterstützung vieler Cloud-Provider (Amazon ECS, Google Container Engine, MS Azure)
- aktuelle Docker-Images der Software-Hersteller: Ubuntu, WordPress, Java, MySQL, PostreSQL
- freie Software, umfangreiche Dokumentation und Werkzeuge
Überblick
Mit Docker betreibt man eine virtualisierte Linux-Umgebung. Als Host-System wird normalerweise Linux verwendet, wobei es seit diesem Sommer mit Docker for Mac auch unter MacOS lauffähig ist. Der vorherige Umweg mit boot2docker, Docker in einer VirtualBox zu starten, ist nur noch für Windows nötig.
Die wichtigsten Begriffe dabei sind:
- Docker-Image: eine Vorlage für ein Docker-Container
- Docker-Container: ein ausgeführtes Docker-Image und damit ein virtuelles Betriebssystem
- Docker-Registry: Registry für Docker-Images, z.B. Online-Dienst Docker Hub
- Dockerfile: Textdatei zur Beschreibung eines Docker-Images
- Docker-Host: Host, auf dem die Docker-Container ausgeführt werden
- Docker-Daemon: Deamon-Prozess im Docker-Host, der Container und Images auf dem Host steuert
- Docker-Client: Kommandozeilen-Software zur Kommunikation mit dem Docker-Deamon des Docker-Hosts
1. Beispiel: docker run hello-world
➜ ~ docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world c04b14da8d14: Pull complete Digest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9 Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal.
- Docker-Client ruft Docker-Deamon auf
- Docker-Deamon lädt Docker-Image
hello-world
von Docker-Registry: Docker Hub - Docker-Deamon erstellt Docker-Container von dem Image und führt ihn aus
- Docker-Deamon sendet die Ausgabe an Docker-Client und gibt sie aus
2. Beispiel: docker run -it ubuntu bash
➜ ~ docker run -it ubuntu bash Unable to find image 'ubuntu:latest' locally latest: Pulling from library/ubuntu 6bbedd9b76a4: Pull complete fc19d60a83f1: Pull complete de413bb911fd: Pull complete 2879a7ad3144: Pull complete 668604fde02e: Pull complete Digest: sha256:2d44ae143feeb36f4c898d32ed2ab2dffeb3a573d2d8928646dfc9cb7deb1315 Status: Downloaded newer image for ubuntu:latest root@0a3422f628bc:/# cat /etc/os-release NAME="Ubuntu" VERSION="16.04.1 LTS (Xenial Xerus)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 16.04.1 LTS" VERSION_ID="16.04" HOME_URL="http://www.ubuntu.com/" SUPPORT_URL="http://help.ubuntu.com/" BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" UBUNTU_CODENAME=xenial root@0a3422f628bc:/# exit
- Ubuntu-Image herunterladen
- Ubuntu-Container erstellen und starten
- Interaktiv den Befehl
bash
ausführen - Befehle ausführen, z.B.:
cat /etc/os-release
- Befehl
exit
beendet den Prozess
Befehle
Image-Befehle | |
---|---|
docker images |
Images anzeigen |
docker inspect IMAGE_ID |
Image-Informationen anzeigen |
docker history IMAGE_ID |
History eines Images anzeigen |
docker rmi IMAGE_ID |
Image löschen |
docker run IMAGE_NAME |
Container des angegebenen Images ausführen |
docker login |
Anmelden bei einer Docker-Registy |
docker pull IMAGE_NAME |
Image herunterladen aus Repository |
docker push IMAGE_NAME |
Image hochladen in Repository |
docker tag IMAGE_ID IMAGE_NAME |
Image-Tag erstellen |
docker build |
Image aus Dockerfile erstellen |
Container-Befehle | |
---|---|
docker ps |
Laufende Container anzeigen |
docker ps -a |
Alle Container anzeigen |
docker inspect CONTAINER_ID |
Container-Informationen anzeigen |
docker stop CONTAINER_ID |
Laufenden Container anhalten |
docker rm CONTAINER_ID |
Container löschen |
docker port CONTAINER_ID |
Port-Mappings des Containers anzeigen |
docker top CONTAINER_ID |
Prozesse des Containers anzeigen |
docker logs CONTAINER_ID |
Log-Ausgaben des Containers anzeigen |
docker exec CONTAINER_ID BEFEHL |
Befehl im laufenden Container ausführen |
Eigenes Images per Dockerfile erstellen
Man kann sehr einfach sein eigenes Docker-Image erstellen, indem man die benötigten Befehle in einer Datei ablegt. In meinem ersten Beispiel nutzen wir ein bestehendes Java-Image und erweitern es um die Installation von Git und Maven. Dieses Image wird in das Repository Docker Hub hochgeladen (kostenloser Account nötig) und dient als Basis für das zweite Beispiel.
echo 'FROM java:openjdk-8-jdk' > Dockerfile echo 'MAINTAINER Andreas Bruns <me4bruno@andreas-bruns.com>' >> Dockerfile echo 'RUN apt-get update' >> Dockerfile echo 'RUN apt-get install -y --no-install-recommends git' >> Dockerfile echo 'RUN apt-get install -y --no-install-recommends maven' >> Dockerfile docker build -t me4bruno/java-git-maven . docker login docker push me4bruno/java-git-maven
Jede Run-Zeile im Dockerfile erstellt eine neue Image-Schicht. Daher kann es sinnvoll sein, mehrere Befehle für eine Schicht zusammenzufassen. Das erstellte Image sollte mit einem Tag versehen werden, das zu dem Account bei Docker Hub passt.
Das java-git-maven-Image dient als Grundlage für ein Image mit einer Spring-Boot-Applikation. Das Github-Projekt Spring-Guides: gs-serving-web-content
wird per Git heruntergeladen, dann mit Maven gebaut und anschließend wird die Spring-Boot-Applikation gestartet. Sie ist dann unter dem Standard-Port 8080 innerhalb des Docker-Containers erreichbar, wobei der interne Port auf einen anderen Host-Port zur Verfügung gestellt werden kann (hier 9090).
Inhalt des Dockerfiles des 2. Beispiels:
FROM me4bruno/java-git-maven MAINTAINER Andreas Bruns <me4bruno@andreas-bruns.com> RUN apt-get install -y --no-install-recommends tree RUN env GIT_SSL_NO_VERIFY=true git clone https://github.com/spring-guides/gs-serving-web-content.git RUN cd gs-serving-web-content/complete && mvn package && tree ENTRYPOINT ["java", "-jar", "/gs-serving-web-content/complete/target/gs-serving-web-content-0.1.0.jar"]
Container des 2. Beispiels starten und testen:
docker build -t me4bruno/springboot-web-app . docker run -p 9090:8080 -d me4bruno/springboot-web-app docker exec -it CONTAINER_ID curl http://localhost:8080 curl http://localhost:9090
Fazit
Als DevOp sollte man sich auf jeden Fall mit Docker auseinandersetzen, weil es sich dank der Einfachheit in den letzten Jahren gegenüber viele andere Virtualisierung-Umgebungen behauptet hat. In diesem kleinen Artikel bin ich noch nicht darauf eingegangen, wie man mehrere Docker-Container miteinander verknüpft und mit persistenten Daten arbeitet. Das beschreibe ich wahrscheinlich mal in einem weiteren Artikel.
Für die lokale Entwicklung empfiehlt es sich übrigens, den Speicherplatz der Docker-Images gelegentlich zu prüfen. Bei „Docker for Mac“ mit dem folgenden Befehl:
ls -Al ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/