Andreas Bruns

Softwareentwicklung für Oldenburg und Bremen

Virtualisierung für DevOps mit Docker

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.
  1. Docker-Client ruft Docker-Deamon auf
  2. Docker-Deamon lädt Docker-Image hello-world von Docker-Registry: Docker Hub
  3. Docker-Deamon erstellt Docker-Container von dem Image und führt ihn aus
  4. 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
  1. Ubuntu-Image herunterladen
  2. Ubuntu-Container erstellen und starten
  3. Interaktiv den Befehl bash ausführen
  4. Befehle ausführen, z.B.: cat /etc/os-release
  5. 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/

Kommentare sind geschlossen.