Schon oft gelesen und dann immer wieder abgestorben - so lief das Thema Docker für mich bisher. Dabei bin ich ja schon seit der ersten vmware Version ein großer Fan von Virtualisierung!
Aber jetzt - ein kleiner Vortrag über FHEM im Container hat mich bewogen es zu versuchen, wie so oft schreib ich hier mal mit, damit ich später weiß was ich gemacht habe.
Ich habe als Ergebnis des Artikels ein Setup Script auf Github abgelegt. Dieses Script
- installiert docker und docker-compose
- bringt den aktuellen user in die Gruppe docker
- richtet portainer als ersten Container ein
Umgebung bauen
Meine Testumgebung war der Raspberry und ein bisschen auch Windows mit WSL. Also erstmal docker beschaffen, auf dem RaspberryPi nimmt man das Convenience Script und befolgt was dann am Ende dort steht:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
Das Script installiert alle notwendigen Dinge und läuft eine ganze Weile auch mal still!
Hinweis: Das Script kann auch zum Update einer bereits laufenden Docker Umgebung verwendet werden, wenn diese mit dem Script installiert wurde! Allerdings wird docker auch bei apt full-upgrade aktualisiert.
Dann kann man den "Hello World" Container ausführen - ja und nun? Das bringt mich nicht sehr voran, ich will einfach Container verwenden und keine programmieren!
Docker Compose ist da das Zauberwort! Nach etwas planlosem herumirren habe ich heraus gefunden: Das ist nicht der zweite Schritt wie ich am Anfang dachte - das ist quasi für mich der Erste! Man braucht (in aller Kürze):
- kein sudo (Abschluss vom Setup beachten),
- docker-compose an sich (siehe auch Versionshinweise weiter unten)
- einen Arbeitspfad,
- eine docker-compose.yml Datei.
- eine .env Datei wenn man spezifische Informationen an die docker-compose.yml Datei geben will.
# Replace with the latest version from https://github.com/docker/compose/releases/latest
COMPOSE_VER="2.2.2"
# For 64-bit OS use:
COMPOSE_ARCH="aarch64"
# For 32-bit OS use:
COMPOSE_ARCH="armv7"
mkdir -p ~/.docker/cli-plugins/
curl -SL https://github.com/docker/compose/releases/download/v${COMPOSE_VER}/docker-compose-linux-${COMPOSE_ARCH} -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose
# Test
docker compose version
Man kann durch erneutes Ausführen der paar Zeilen auch eine aktuellere Version installieren (upgrade).
Arbeitspfad "docker stack" erzeugen.
mkdir dockertest
cd dockertest
Achtung: Ab hier bitte beachten v1 -> v2: docker-compose -> docker compose
Der erste Container zur Ergänzung der Spielwiese ist Portainer, den notwendigen Inhalt der compose Datei findet man hier. Für den Start laden wir die compose Datei einfach herunter. Link zur Referenz.
Hinweis: Mit dieser Vorgehensweise bekommt man die alte Portainer Version. Zur neuen Version siehe weiter unten!
wget https://downloads.portainer.io/docker-compose.yml
Die Datei läuft so wie sie ist, anschauen oder ergänzen geht z.B. mit nano. Danach ein Kommando zum bauen und starten.
nano docker-compose.yml
docker-compose up
Portainer hilft als Benutzerinterface beim Verstehen der neuen Containerumgebung
- Logs - Logfile des Containers anschauen (sieht man sonst bei compose up im Terminal)
- Inspect - Konfiguration anschauen
- Stats - Speicher-, CPU-, Netzwerkverwendung
- Exec Console - Shell im Container öffnen (man braucht keinen ssh Server im Container!)
- Add Container - neue Container zusammenstellen, konfigurieren und starten
- Stacks - unser dockertest ist quasi ein Stack (außerhalb erzeugt), man kann hier Neue erzeugen. Ich glaube aber die manuelle Erzeugung ist einfacher.
Die compose Datei im yaml Format kann leicht um ein Service/Container/Image erweitert werden, dazu kopiert man z.B. aus der Beispieldatei des fhem docker Images den passenden Abschnitt in den vorhandenen services Abschnitt. Für diesen minimalen Versuch kommentiert man dafür aus dem "Minimum Example" noch den network Abschnitt aus.
Nach einem erneuten docker-compose up hat man ein FHEM am Laufen.
Start der Containerumgebung
Ist die Umgebung fertig konfiguriert, schickt man die Console beim nächsten (finalen) Start einfach in den Hintergrund
docker-compose up -d
Muss man später etwas umkonfigurieren, wird mit diesem Befehl die neue Konfiguration aktiviert. Dabei werden nur die veränderten Container neu gestartet!
Der richtige Eintrag im services Abschnitt startet die Container in Zukunft automatisch mit dem System.
restart: always
Im Hintergund
Wird die Konfiguration eines Containers geändert, wird der alte zerstört und ein neuer erzeugt. Das Image bleibt dabei erhalten.
Container Transport
Das ist dann eben wirklich der Knaller: Man schnürt die Ladung zusammen (tar) und stellt den Container auf ein Schiff (scp).
docker-compose stop
cd ~
sudo tar -cvf docker.tar ./docker
scp docker.tar user@host:
Am Zielort lädt man das Schiff wieder aus:
sudo tar -xvf docker.tar
Dabei hat man hier nur die "eigenen" Daten transportiert. Führt man jetzt ein docker-compose up aus werden die eigentlichen Images wieder herunter geladen und danach alles neu gestartet.
Feinkonfiguration
Die Beispieldatei im fhem docker Image gibt einen ersten Eindruck über die mögliche Konfiguration von
- Netzwerk - per default sind alle Container eines Hosts gemeinsam in einem Netzwerk isoliert
- Ports - mit Ausnahme network: host müssen Ports explizit bekannt gemacht werden!
- Datenspeicherung - Daten müssen außerhalb des Containers gespeichert werden.
Docker verwendet für sein bridge Network die Adresse 172.17.0.1. Lässt man den Eintrag network: in der yml Datei frei - entsteht ein Netzwerk (Typ bridge) dockertest_default mit der Adresse 172.18.0.1
Hier ein Grundgerüst für eine FHEM Umgebung mit Portainer, FHEM (Alexa im fhem Image), Homebridge und Sonos.
Hinweis: Ich bin da eine Weile drüber gestolpert: man kann bestimmte Werte in der yml Datei entweder als Array (- variable=wert) oder Dictionary (variable: wert) angeben. Details findet man in der Doku. Es gibt online Tools zum Syntaxcheck (Beispiel) aber die sagen auch nur richtig oder falsch.
version: '3'
services:
portainer:
image: portainer/portainer
command: -H unix:///var/run/docker.sock
restart: always
ports:
- 9000:9000
- 8000:8000
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
fhem:
image: fhem/fhem:latest
restart: always
# networks:
# - net
ports:
- "8083:8083"
- "1883:1883"
volumes:
- "./fhem/:/opt/fhem/"
sonos:
image: svrooij/sonos2mqtt
restart: unless-stopped
ports:
- "6329:6329"
environment:
- SONOS2MQTT_DEVICE=192.168.56.207 # Service discovery doesn't work very well inside docker, so start with one device.
- SONOS2MQTT_MQTT=mqtt://192.168.56.121:1883 # mqtt2_server inside FHEM
# - SONOS2MQTT_DISTINCT=true # if your want distinct topics
- SONOS_LISTENER_HOST=192.168.56.121 # Docker host IP
#- SONOS_TTS_ENDPOINT=http://sonos-tts:5601/api/generate # If you deployed the TTS with the same docker-compose
homebridge:
image: oznu/homebridge:latest
restart: always
network_mode: host
environment:
- PGID=1000
- PUID=1000
- HOMEBRIDGE_CONFIG_UI=1
- HOMEBRIDGE_CONFIG_UI_PORT=8080
volumes:
- ./volumes/homebridge:/homebridge
volumes:
portainer_data:
Homebridge erfordert den Host Modus. Die notwendigen Informationen zum pairen der Homebridge findet man nach dem Start im Log des Containers.
Notizen zur Installation
Aktuell (Dezember 2021) funktionieren alle offiziellen Beschreibung hier für den Raspberry Pi (armv7l) nicht: https://docs.docker.com/compose/install/
Die Anleitung von hier, die mit pip3 arbeitet (Zeilen einzeln abarbeiten) funktioniert seit 18.12.2021 nicht mehr:
sudo apt-get install libffi-dev libssl-dev
sudo apt-get install python3 python3-pip
sudo pip3 install docker-compose
Docker-compose gibt es auch als Container für eine v1 Installation vielleicht zu bevorzugen.
Die Vorgehensweise aus der offiziellen Doku funktioniert für armhf Plattformen nicht. Ich habe hier eine Alternative gefunden. Der container hat auch Nachteile: startet langsamer, belegt mehr Platz, Installation dauert länger ...
Es wird ein Script heruntergeladen und ausführbar gemacht. Der Container wird bei beim Aufruf von docker-compose gestartet, beim ersten Aufruf wird das Image geladen.
sudo curl -L --fail https://raw.githubusercontent.com/linuxserver/docker-docker-compose/master/run.sh -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
Unter Windows installiert man Docker Desktop for Windows, da ist Docker Compose mit enthalten. Docker Desktop arbeitet mit WSL zusammen.
Vielleicht ist es besser Portainer in der aktuellen Version vorab direkt zu installieren?
docker volume create portainer_data
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce
Notizen zur Verwendung
Update eines Containers
Portainer hat mir gesagt es gibt eine aktuelle Version - wie macht denn ein Update?
Alles auf einmal ist in der Kommandozeile simpel:
docker-compose pull
docker-compose up -d
Oder man zieht einzelne Images per Kommandozeile, dabei passiert außer dem Download nichts an der laufenden Umgebung:
docker pull imagename
# oder
docker image pull imagename
Mit Portainer kann man das auch in der GUI machen:
Mit portainer selbst das neue Image ziehen portainer/portainer-ce:latest. Das steht dann als unused in der Liste. Danach einfach docker-compose up -d gestartet. Portainer meldet recreating und schon ist es passiert. Jetzt steht am alten Image: unused. Man kann das alte Image löschen.
Informationen des Host an die Container übergeben
Zugriffe aus dem Container heraus auf den Host sind "schwierig". Ich habe z.B. die Notwendigkeit, den Hostname des Hosts im Container zu verwenden. Man kann Informationen über Variablen zur Docker Konfiguration übertragen:
environment:
- HOST_HOSTNAME=${HOST_HOSTNAME}
Über das Konstrukt ${} wird eine - im Host existierende - Variable aufgelöst und die docker-compose Variable geschrieben (Die Namensgleichheit ist nicht Bedingung). Man kann/muss die Variable beim Aufruf des docker-compose Befehls setzen:
HOST_HOSTNAME=$(hostname) docker-compose up -d
Hier im Beispiel wird der Hostname per Befehl ermittelt in die Variable geschrieben und der docker-compose Konfiguration beim Start übergeben. Die Container werden mit dieser Information gestartet.
Wichtig: Die Namensauflösung muss funktionieren!
host $(hostname)
Ich finde dieses Verfahren noch nicht schön, ich habe aber nichts besseres
gefunden.
Eventuell doch:
Man kann bei docker-compose die environment Variablen durch die .env Datei ersetzen lassen. Prüfen kann man diese Funktion jederzeit mit docker-compose config. Allerdings hat die .env Datei auch keine bash Funktioninalität, um die Aufgabe zu lösen, muss man diese Datei Computer spezifisch erstellen!
echo HOST_HOSTNAME=$(hostname) > .env
Jetzt kann man jederzeit den docker-compose Befehl ohne extra vorangestellter Umgebungsvariable aufrufen. Die im Arbeitspfad liegende .env wird automatisch gelesen.
Guter Beitrag Otto!
AntwortenLöschenIch versuche mich gerade auch mit Docker. Ich hatte Nginx als Proxy, Fhem, ConfigDB, DBLog, Mysql, Homebridge, LEPresence, I2C, Collectord, Sonos, HMCCU, MQTT und viel mehr bisher auf dem Raspberry liegen. Jetzt hab ich günstig einen ThinkCentre bekommen und möchte nun gern umziehen. Docker ist ein großes Thema und es tauchen immer wieder Probleme auf die nicht 1:1 zum Raspberry gelöst werden können. Meine Nginx Konfiguration bekomme ich z.B. nicht zum Laufen. Bin gespannt was sich bei dir tut
Hallo Mark, danke fürs Lob. Ich arbeite ev. noch etwas an diesem Artikel, eventuell schiebe ich auch noch einen nach. Die Umgebung oben ist schnell erstellt - wenn man dann die vielen kleinen Dinge in Betrieb nehmen will, entstehen schnell wieder offene Punkte:
Löschen- mount von Serverlaufwerken
- lokal Bluetooth
- WOL
Was baut man in den Container? Braucht man noch einen extra? Macht man auch ein paar Dinge einfach auf dem Host - wie "clean" lässt man den?
Gerade kläre ich für mich noch das Thema Volume vs. Bindmount im Docker.
Also das Thema ist gerade erst angekratzt :)
Hi Otto,
AntwortenLöschenich sehe ich habe einen "Seelenverwandten" gefunden der auch über all diese Themen "stolpert". Sehr schön wie du alle Themen anreißt mit denen ich auch gekämpft habe bzw. mich gerade ran taste.
Ich habe eine Docker Umgebung mit Fhem, Raspmatic, Zigbee2mqtt und Portainer laufen. Ich arbeite gerade an der docker-compose config damit ich nicht immer alles per Hand in der richtigen Reihenfolge starten muss. Da gibt mir dein Projekt ein paar gute Tipps - danke dafür. Ich sehe es auch so wie du ... man kratzt an verschiedenen Oberflächen und darunter tun sich immer wieder große Themen auf.