Docker
Einzelne Prozesse werden in Containern vom Betriebssystem isoliert. Docker verhindert, dass die virtualisierten Prozesse auf das Hostsystem zugreifen. Entkopplung vom Hostsystem vereinfacht Migration: Es kann der gesamte Container auf einen anderen Server verschoben werden.
Grundbegriffe
Image
Vorbereitetes Abbild, aus dem ein Container erstellt werden kann, quasi ein “Installationspaket”. Images werden in der Regel aus einer Registry heruntergeladen. Wenn man einen Container erzeugt, wird immer angegeben, aus welchem Image er erzeugt werden soll.
Das Image beinhaltet immer das Grundsystem und meist auch die Software, die man virtualisieren möchte. Beispiele für Images:
alpine: “A minimal Docker image based on Alpine Linux with a complete package index and only 5 MB in size!”debian: “Debian is a Linux distribution that’s composed entirely of free and open-source software.”node: “Node.js is a JavaScript-based platform for server-side and networking applications.”nginx: “Nginx is an open source reverse proxy server”
Container
Laufendes Objekt, in dem ein Prozess aktiv ist, quasi “installierte Software”. Ein Container virtualisiert einen einzelnen Prozess, z. B. einen Webserver, eine Datenbank, einen DNS-Server, … Ein Container wird immer auf Basis eines Images erstellt.
Container sind “ephemeral”, also flüchtig: Ein Container muss jederzeit gelöscht werden können, ohne dass Daten verloren gehen. Man kann den Container also immer wieder aus dem Image erzeugen. Die Nutzdaten müssen in Volumes persistiert werden.
Volume
Speicher für Daten des Containers. Volumes werden genutzt, um Daten zu speichern, die den Container überleben (denn Container sind flüchtig). Hier muss also alles abgelegt werden, was eigene Konfiguration oder eigene Daten sind, die nicht standardmäßig zum Image oder Container gehören:
- Konfigurationsdateien
- vom Nutzer erstellte Daten, z. B. hochgeladene Bilder, Blogposts, …
Volumes werden in das Dateisystem des Container eingehängt. Der Container sieht also gar nicht, ob eine Datei/Ordner flüchtig lokal im Container liegt oder nach außen über Volumes persistiert wird.
Volumes können als echtes Docker Volume oder als Bind Mount angelegt werden:
- Docker Volume: Dateien werden intern in der Ordnerstruktur von Docker verwaltet. Es ist kein direkter Zugriff von außen möglich. Geeignet für größere und komplexe Daten, die alleine vom Container verwaltet werden, z. B. hochgeladene Dateien einer Nextcloud.
- Bind Mount: Dateien/Ordner des Hostsystems werden mit dem Container verknüpft. Geeignet für Konfigurationsdateien oder andere einfache Dateien, die von außerhalb des Containers bearbeitbar sein sollen.
Zusammenfassung
Ein Container beinhaltet einen virtualisierten Prozess. Er wird auf Basis eines Images erzeugt. Alle Daten des Containers, die individuell gespeichert werden sollen, müssen in Volumes abgelegt werden.
Root-Privilegien
Docker wird standardmäßig mit Root-Rechten auf dem System ausgeführt. Der Daemon hat also vollen Zugriff auf das Betriebssystem des Servers. Das ist ein potenzielles Angriffsziel, weil ein schadhafter Container Sicherheitslücken im Daemon ausnutzen könnte, um Zugriff auf das Hostsystem zu erlangen.
Es ist grundsätzlich möglich, Docker ohne Root-Rechte auszuführen: https://docs.docker.com/engine/security/rootless/
Persönliche Einschätzung: Für produktive Umgebungen, die öffentlich übers Internet erreicht und angegriffen werden können, sollte man Docker rootless laufen lassen. Für einen lokalen Server, der im privaten Netz läuft und nicht direkt übers Inetrnet erreichbar ist, sind Root-Rechte in Ordnung. Das vereinfacht die Verwaltung enorm!
Docker Compose
Mit dem Befehl docker können alle Aspekte des Systems verwaltet werden, zum Beispiel die Erstellung und der Start von Containern.
Alle benötigten Parameter müssen als Kommandozeilenargumente übergeben werden, z. B. p für weitergeleitete Netzwerkports oder v für Volumes. Das verwendete Image ist hier nextcloud:
docker run -d -p 8080:80 -v nextcloud:/var/www/html nextcloud
Großer praktischer Nachteil: Man muss diesen Befehl dokumentieren, um den Container später wieder genauso erzeugen zu können, zum Beispiel bei einem Update. Bei komplexeren Szenarien mit mehreren Containern, die miteinander interagieren (z. B. eine Nextcloud mit MySQL-Datenbank) müssen diese Einzelteile manuell zusammengebaut werden.
Docker Compose ist ein Werkzeug zur Orchestrierung von Containern. In einer Konfigurationsdatei wird angegeben, welche Container aus welchen Images und mit welchen Parametern (Ports, Volumes, etc.) erzeugt werden sollen. Mit lokalen Netzwerken kann die Kommunikation der Container untereinander gesteuert werden.
Der Containerverbund wird immer als Ganzes gestartet oder aktualisiert. Wenn man mehrere Projekte virtualisiert oder mehrere Container im Verbund betreibt, ist Docker Compose sehr zu empfehlen.
Dateistruktur
Der Verbund wird durch eine Datei docker-compose.yml konfiguriert.
Idealerweise legt man für jedes Projekt eine separate Compose-Datei in einem eigenen Ordner an.
Die Ordnerstruktur könnte dann so aussehen: für jedes Projekt ein Ordner mit docker-compose.yml. Daneben liegen die Ordner für die Volumes, die als Bind Mounts in den Container eingebunden sind:
/root/docker/
├─ homeassistant/
│ ├─ docker-compose.yml
│ ├─ config/
├─ pihole/
│ ├─ docker-compose.yml
│ ├─ etc-dnsmasq.d/
│ ├─ etc-pihole/
├─ mariadb/
│ ├─ docker-compose.yml
docker-compose.yml
In der Konfigurationsdatei wird unter anderem angegeben:
- welche Container gestartet werden sollen
- welches Image dafür verwendet wird
- welche Ports weitergeleitet werden sollen
- wie die Volumes konfiguriert sind
- Umgebungsvariablen
- und vieles mehr
Einfaches Beispiel: pi-hole
- Container basiert auf dem Image
pihole/pihole:latest - zwei lokale Ordner sind als Bind Mounts eingebunden (für Konfigurationsdateien)
- Ports für DNS und HTTP wurden nach außen geöffnet
- Umgebungsvariablen steuern den Container
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
ports:
- "53:53/tcp"
- "53:53/udp"
- "8080:80/tcp"
environment:
TZ: 'Europe/Berlin'
FTLCONF_LOCAL_IPV4: '192.168.1.10'
volumes:
- './etc-pihole:/etc/pihole'
- './etc-dnsmasq.d:/etc/dnsmasq.d'
restart: unless-stopped
Komplexeres Beispiel: Home Assistant
- mehrere Container
- Die Container eines Verbunds können grundsätzlich über das lokale Netzwerk miteinander kommunizieren, auch ohne freigegebene Ports.
homeassistantundphpmyadminkönnen also die Datenbankmariadbdirekt erreichen. - Config-Ordner für
homeassistantwird als Bind Mount eingebunden - Datenordner für die Datenbank wird als Docker Volume
mariadbxgespeichert - auskommentiert: phpMyAdmin zur Verwaltung der MySQL-Datenbank
services:
homeassistant:
container_name: homeassistant
image: "ghcr.io/home-assistant/home-assistant:stable"
volumes:
- ./config:/config
- /etc/localtime:/etc/localtime:ro
restart: unless-stopped
privileged: true
ports:
- 8123:8123
depends_on:
- mariadb
devices:
- /dev/ttyACM0
mariadb:
image: mariadb
container_name: mariadb
restart: unless-stopped
volumes:
- mariadbx:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
MYSQL_DATABASE: "${MYSQL_DATABASE}"
MYSQL_USER: "${MYSQL_USER}"
MYSQL_PASSWORD: "${MYSQL_PASSWORD}"
PUID: 1000
PGID: 1000
# phpmyadmin:
# image: phpmyadmin
# restart: unless-stopped
# environment:
# PMA_HOST: mariadb
# ports:
# - 8080:80
volumes:
mariadbx:
Verbund starten und stoppen
# starten
docker compose up
# starten und detachen (läuft im Hintergrund)
docker compose up -d
# beenden
docker compose down
# beendete Container löschen
docker compose rm
# aktuelle Versionen der Images herunterladen
docker compose pull
Regelmäßige Updates
Um Updates zu installieren, kann man regelmäßig ausführen:
docker compose pull
# falls etwas gepullt wurde, Container neustarten
docker compose down && docker compose up -d
Tipp: Converter von docker run zu docker compose
https://it-tools.tech/docker-run-to-docker-compose-converter