Es gibt schier unzählige Webseiten mit Docker-Images. Neben dem offiziellen Docker Hub existieren noch eine ganze Menge weiterer Quellen im Netz. Doch nicht immer ist auch das Gesuchte dabei oder das vorhandene Image wurde schon länger nicht mehr aktualisiert. In so einem Fall muss man dann selbst Hand anlegen und sich sein eigenes Docker-Image erstellen.
Und das ist gar nicht so schwer, wie man anfangs denken würde. Ein Nginx-Webserver mit statischer Webseite lässt sich zum Beispiel mit einem 2-Zeiler erstellen. Auch eine NPM-APP kann das Licht der Welt in einem 10-zeiligen Dockerfile erblicken. Das ist wirklich erstaunlich, oder? Doch wie funktioniert das genau? In diesem kleinen Ratgeber werde ich es dir zeigen.
Was braucht man alles für den Start?
Jeder von uns, der mit Docker als Administrator arbeitet, hat Zugriff auf einen Docker-Host samt Texteditor. Das genügt auch schon vollends für diesen Artikel. Man macht sich das Leben aber deutlich leichter, wenn man eine IDE wie Visual Studio Code benutzt. Hier kann man dann nämlich Plugins installieren, die einem beim Erstellen des Dockerfiles helfen.
Ich weiß die Ratschläge in den aufpoppenden Boxen sehr zu schätzen, haben sie mich doch schon unzählige Male beim Entwerfen eines Dockerfiles unterstützt. Aber genug um den heißen Brei herumgeredet. Starten wir mit der Erstellung des Ordners Webapp auf unserem Gerät. Darin enthalten sind dann der HTML-Code für den späteren Container sowie das Dockerfile.
Ziel dieses Tutorial ist das Erstellen eines Webservers mit statischer Homepage. Im Unterordner html lege ich dafür eine Datei namens index.html an. Der Code ist sehr simpel gehalten und enthält im Endeffekt zwei Überschriften sowie einen kleinen Textblock. Weder gibt es Bilder noch eine ansehnliche Hintergrundfarbe. CSS-Code für das Styling fehlt also gleich vollends.
So schaut der fertige Programmcode aus:
<DOCTYPE html>
<html>
<body>
<h1>Geschafft...</h1>
<h2>Das eigene Container-Image funktioniert wirklich.</h2>
<p>Und das sogar in Kubernetes!</p>
</body>
</html>
Wie wird ein Dockerfile erstellt?
Der erste Schritt ist das Anlegen der gleichnamigen Datei im Hauptordner. Sobald das File geöffnet worden ist, kann es auch schon mit dem Coding losgehen. In Zeile 1 wird angegeben, auf welchem Image alles basiert. Man könnte von null an starten und ein Ubuntu-Image nutzen. Im späteren Verlauf würde man dann noch den Webserver in Form eines Nginx installieren.
FROM nginx:latest-alpine
COPY ./html /usr/share/nginx/html
EXPOSE 80
EXPOSE 443
CMD ["nginx", "-g", "daemon off;"]
Das ist aber kein besonders effizienter Ansatz. Deshalb verwende ich lieber das offizielle Nginx-Image aus dem Docker Hub. Da ich sehr kleine Images mag, nutze ich die dort die Variante mit Alpine Linux. Dabei handelt es sich um ein sehr schlank gehaltenes Linux-Betriebssystem, das in vielen Containern benutzt wird. Die Grundlage wäre damit bereits geschaffen, doch noch fehlt die Homepage.
Das holen wir mit dem COPY-Befehl aus Zeile 2 nach. Damit wird der HTML-Code schnurstracks in das Ausgabe-Verzeichnis des Nginx kopiert. Nun ist es auch schon fast geschafft. Aus dokumentarischen Gründen lege ich mittels des EXPOSE-Kommandos fest, dass der Container auf die beiden Ports 80 und 443 hört. Notwendig ist dieser Schritt aber nicht.
Das gilt übrigens auch für die Codezeile ganz unten im Dockerfile. Diese Direktive wird standardmäßig nämlich immer bei einem Nginx-Image ausgeführt. Ihre einzige Aufgabe ist es, den Nginx-Prozess zu starten. Man kann dieses Kommando also auch gleich ganz weglassen. Damit hätte man es außerdem geschafft, ein voll funktionsfähiges Docker-Image aus einem 2 Zeilen langen Dockerfile zu deployen.
Wie entsteht aus einem Dockerfile das fertige Container-Image?
Die meiste Arbeit haben wir bereits erfolgreich hinter uns gebracht. Lediglich das Container-Image muss noch gebaut werden. Weil sich Docker dabei an die Standards der OCI (Open Container Initiative) hält, kann dieses Image später sogar direkt in Kubernetes mit anderen Container Runtimes benutzt werden. Für unseren kleinen Abschlusstest bleiben wir aber auf dem Docker-Host.
In Schritt 1 des Prozesses wird das Container-Image selbst angefertigt. Dafür nutzt man den Befehl docker build . -t our_webapp Anzeigen lassen kann man sich das fertige Ergebnis mithilfe von docker images Nun möchte man natürlich noch prüfen, ob alles ordnungsgemäß funktioniert. Dafür muss man erstmal einen Container mit dem eigenen Image erstellen.
Das geht ganz einfach mit docker run -d -p 8066:80 –name webapp our_webapp Ob der Container aber auch wirklich stabil läuft, lässt sich so noch nicht feststellen. Erst das Kommando watch docker ps -a gibt uns Auskunft darüber. Sollte in der Ausgabe ein CrashLoopBackoff angezeigt werden, dann hilft fast immer ein Blick in die Logs.
Gemacht wird das mit docker logs webapp Weitere Einsichten für das Troubleshooting kann man direkt im laufenden Container sammeln. Dafür nutzt man den Befehl docker exec -it webapp /bin/sh Das Kommando öffnet nun die Shell-Umgebung im Nginx-Container. Eine Bash wird man dort übrigens vergebens suchen. Es gibt sie schlicht nicht im verwendeten Alpine Linux.
Was sollte man noch wissen?
Zum Abschluss des kleinen Ratgebers möchte ich noch die Datei .dockerignore erwähnen. Hiermit kann man festlegen, welche Inhalte beim COPY-Befehl übersprungen werden sollen. Praktisch ist das zum Beispiel, um unnütze Logfiles oder Passwortdateien von der Image-Erstellung auszuschließen. Viele Entwickler kennen dieses Vorgehen bereits von GitHub und der dortigen Datei .gitignore
Natürlich gibt es noch andere Wege, mit denen man ein Container-Image für Docker erstellen kann. Würde man ein Ubuntu-Image als Basis verwenden, müsste man mithilfe des RUN-Befehls noch Updates einspielen und natürlich den benötigten Webserver nginx nachinstallieren. Ein passendes Dockerfile würde dann so aussehen:
FROM ubuntu:latest
RUN apt update -y
RUN apt install nginx -y
COPY ./html /usr/share/nginx/html
EXPOSE 80
EXPOSE 443
CMD ["nginx", "-g", "daemon off;"]