Hallo Folks,
ich wollte hier einmal grob festhalten, wie ich mein neues ioBroker Setup vom Grundsatz her aufgebaut habe.
Da ich damit sehr zufrieden bin und glaube, dass es auch für andere nützlich ist, die vor ähnlichen Fragen stehen, hier ein kleiner Exkurs von mir dazu. Ich gehe nicht auf jedes Detail ein, das "Drumherum" im heimischen Netzwerk (DHCP, DNS, IPv6, etc.) muss jeder selbst hinbekommen.
0. Voraussetzungen
- Proxmox installieren:
Ich gehe davon aus, dass Proxmox bereits auf einem x86_64 System installiert ist. Das geht so leicht und es gibt so viele Hilfen dazu im Netz, dass ich dazu nicht viel schreiben möchte.
Für mich war wichtig, dass ich beim Setup auf ZFS im RAID1 Modus als Dateisystem setzen kann. Das geht auch "zu Fuß", aber der Peace of Mind ist höher, wenn man dazu einfach zwei SSD Platten derselben Größe (deshalb idealerweise auch vom gleichen Hersteller und Typ) im System hat. Der Proxmox Installer kümmert sich dann um alles und man kann später den Speicherplatz wesentlich flexibler nutzen, weil man die virtuellen Festplatten großzügig anlegen kann, ohne dass der Speicherplatz blockiert ist oder man später Aufwand hat zu klein geratene Dateisysteme zu vergrößern. RAID1 ist für mich dabei ein netter Nebeneffekt, war aber keine spezielle Anforderung von mir.
Solch ein System läuft bei mir schon seit fast 10 Jahren, der Intel NUC ist Anno 2013 und schnurrt noch immer gut. Die SSDs habe ich einige Male getauscht, aber die Substanz ist super. Angesichts der hohen Preise für Embedded Systeme habe ich nun zum dritten Mal beide SSDs ersetzt und den NUC auch in ein lüfterloses Gehäuse umgebaut, anstatt den defekten CPU-Lüfter zu tauschen. Vielleicht noch etwas Inspiration am Rande auch für andere als Alternative zu einem überteuerten Raspberry Pi 4. Selbst dieser alte Intel NUC zieht bei mir nur zwischen 3 und 4 Watt, damit kann ich gut leben.
- Lokales Docker:
Du brauchst auf deiner Workstation zur Generierung der Initialisierungsdatei für Fedora CoreOS ein lokales Docker. Damit wird es am einfachsten aus der Butane YAML Konfigurationsdatei die für CoreOS eigentlich notwendige Ignition JSON Datei zu generieren. Damit konfiguriert sich CoreOS beim ersten Start selbst und kann auch während automatischer Updates sicherstellen, dass der gewünschte Zustand danach wieder zur Verfügung steht.
- Python 3:
Damit wir während der Installation die Konfigurationsdatei laden können, muss sie auf einem Webserver bereitgelegt werden. Ich starte dafür einfach einen über Python 3 auf meinem Mac. Für Windows gibt es sicher auch Methoden, einfach mal selbst googlen. Es kann natürlich auch dein eigener Webserver oder ein Hostingpaket im Internet sein, wir brauchen den nur kurz.
1. Fedora CoreOS Ignition Datei generieren
Lege eine Textdatei mit dem Namen coreos.bu
an und kopiere den folgenden Inhalt als Basis:
variant: fcos
version: 1.4.0
passwd:
users:
- name: core
ssh_authorized_keys:
- ssh-ed25519 AAAAC3..... me@example.tld
storage:
files:
- path: /etc/hostname
mode: 0644
contents:
inline: |
iobroker-dockerhost
- path: /etc/profile.d/systemd-pager.sh
mode: 0644
contents:
inline: |
# Tell systemd to not use a pager when printing information
export SYSTEMD_PAGER=cat
- path: /etc/sysctl.d/20-silence-audit.conf
mode: 0644
contents:
inline: |
# Raise console message logging level from DEBUG (7) to WARNING (4)
# to hide audit messages from the interactive console
kernel.printk=4
- path: /etc/NetworkManager/system-connections/${interface}.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=ens18
type=ethernet
interface-name=ens18
[ipv4]
address1=192.168.178.10/24,192.168.178.1
dhcp-hostname=demucvmkp01con
dns=192.168.178.1;
dns-search=home.arpa
may-fail=false
method=manual
- path: /etc/systemd/journald.conf
mode: 0644
overwrite: true
contents:
inline: |
[Journal]
SystemMaxUse=200M
SystemMaxFileSize=20M
- path: /etc/zincati/config.d/55-updates-strategy.toml
contents:
inline: |
[updates]
strategy="periodic"
[[updates.periodic.window]]
days=[ "Sat", "Sun" ]
start_time="22:30"
length_minutes=60
disks:
- device: /dev/sdb
wipe_table: false
partitions:
- size_mib: 0
start_mib: 0
label: docker-volumes
filesystems:
- path: /var/lib/docker/volumes
device: /dev/disk/by-partlabel/docker-volumes
format: xfs
with_mount_unit: true
mount_options:
- noatime
system:
units:
- name: serial-getty@ttyS0.service
dropins:
- name: autologin-core.conf
contents: |
[Service]
# Override Execstart in main unit
ExecStart=
# Add new Execstart with `-` prefix to ignore failure`
ExecStart=-/usr/sbin/agetty --autologin core --noclear %I $TERM
- name: rpm-ostree-install.qemu-guest-agent.service
enabled: true
contents: |-
[Unit]
Description=Layer qemu-guest-agent with rpm-ostree
Wants=network-online.target
After=network-online.target
# We run before `zincati.service` to avoid conflicting rpm-ostree
# transactions.
Before=zincati.service
ConditionPathExists=!/var/lib/%N.stamp
[Service]
Type=oneshot
RemainAfterExit=yes
# `--allow-inactive` ensures that rpm-ostree does not return an error
# if the package is already installed. This is useful if the package is
# added to the root image in a future Fedora CoreOS release as it will
# prevent the service from failing.
ExecStart=/usr/bin/rpm-ostree install --apply-live --allow-inactive qemu-guest-agent
ExecStart=/bin/touch /var/lib/%N.stamp
[Install]
WantedBy=multi-user.target
- name: docker.autoheal.service
enabled: true
contents: |-
[Unit]
Description=Docker Autoheal Container
After=docker. Service
Requires=docker.service network.target network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker stop %n
ExecStartPre=-/usr/bin/docker rm %n
ExecStartPre=-/usr/bin/docker pull willfarrell/autoheal
# Privileged mode is required for binding to local socket to work due to SELinux
ExecStart=/usr/bin/docker run --label portainer.hidden="true" --privileged=true -d --name %n --restart always -v /var/run/docker.sock:/var/run/docker.sock --network none willfarrell/autoheal
ExecStop=/usr/bin/docker stop -t 15 %n
[Install]
WantedBy=multi-user.target
- name: docker.portainer-agent.service
enabled: true
contents: |-
[Unit]
Description=Portainer Agent Container
After=docker.service
Requires=docker.service network.target network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker network create portainer_agent_network --attachable --internal
ExecStartPre=-/usr/bin/docker stop %n
ExecStartPre=-/usr/bin/docker rm %n
ExecStartPre=-/usr/bin/docker pull portainer/agent
# Privileged mode is required for binding to local socket to work due to SELinux
ExecStart=/usr/bin/docker run --label portainer.hidden="true" --privileged=true -d --network portainer_agent_network --name %n --restart always -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker/volumes:/var/lib/docker/volumes portainer/agent
ExecStop=/usr/bin/docker stop -t 15 %n
[Install]
WantedBy=multi-user.target
# - name: docker.portainer.service
# enabled: true
# contents: |-
# [Unit]
# Description=Portainer Community Edition Container
# Requires=docker.portainer-agent.service docker.service network.target network-online.target
#
# [Service]
# Type=oneshot
# RemainAfterExit=yes
# TimeoutStartSec=0
# ExecStartPre=-/usr/bin/docker stop %n
# ExecStartPre=-/usr/bin/docker rm %n
# ExecStartPre=-/usr/bin/docker pull portainer/portainer-ce
# ExecStartPre=/usr/bin/docker volume create portainer_data
# ExecStart=/usr/bin/docker run --label portainer.hidden="true" -d -p 8800:8000 -p 9443:9443 --name %n --restart always -v portainer_data:/data portainer/portainer-ce
# ExecStartPost=-/usr/bin/docker network connect portainer_agent_network %n
# ExecStop=/usr/bin/docker stop -t 15 %n
#
# [Install]
# WantedBy=multi-user.target
- name: docker.portainer.service
enabled: true
contents: |-
[Unit]
Description=Portainer Business Edition Container
Requires=docker.portainer-agent.service docker.service network.target network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker stop %n
ExecStartPre=-/usr/bin/docker rm %n
ExecStartPre=-/usr/bin/docker pull portainer/portainer-ee
ExecStartPre=/usr/bin/docker volume create portainer_data
ExecStart=/usr/bin/docker run --label portainer.hidden="true" -d -p 8800:8000 -p 9443:9443 --name %n --restart always -v portainer_data:/data portainer/portainer-ee
ExecStartPost=-/usr/bin/docker network connect portainer_agent_network %n
ExecStop=/usr/bin/docker stop -t 15 %n
[Install]
WantedBy=multi-user.target
- name: docker.builder-prune.service
enabled: true
contents: |-
[Unit]
Description=Docker Builder Prune
[Service]
Type=oneshot
ExecStart=/usr/bin/docker builder prune --all --force --keep-storage 5G
- name: docker.builder-prune.timer
enabled: true
contents: |-
[Unit]
Description=Run Docker Builder Prune
[Timer]
OnCalendar=20:30 Europe/Berlin
[Install]
WantedBy=multi-user.target
- name: docker.image-prune.service
enabled: true
contents: |-
[Unit]
Description=Docker Image Prune
[Service]
Type=oneshot
ExecStart=/usr/bin/docker image prune --all --force
- name: docker.image-prune.timer
enabled: true
contents: |-
[Unit]
Description=Run Docker Image Prune
[Timer]
OnCalendar=20:45 Europe/Berlin
[Install]
WantedBy=multi-user.target
Ich empfehle dir als Editor Visual Studio Code. Dort kannst du manuell auch das Syntax Highlighting für YAML einstellen, so dass die Datei für dich besser les- und editierbar wird.
Damit du dich später als Benutzer core
in der virtuellen Maschine per SSH einloggen kannst, musst du ganz oben zunächst den Public Key deines SSH Schlüssels hinterlegen.
Auch die Netzwerkeinstellungen wie Hostname, statische IP-Adresse, Gateway, DNS-Search etc. solltest du deinen lokalen Gegebenheiten nach anpassen.
In der Mitte etwa sind drei systemd-Dienste definiert.
Der erste Dienst startet einen Docker Autoheal Container, der alle unhealthy Container automatisch neu startet, die das Label autoheal=true
tragen.
Die anderen beiden Dienste sorgen für die Bereitstellung von Portainer als Frontend. Der auskommentierte Dienst ist für die Community Edition. Solltest du diese benutzen wollen, dann musst du diesen Teil wieder einkommentieren und dafür den Dienst für die Enterprise Edition wieder auskommentieren! Ich benutze hier die Enterprise Edition, bei der man problemlos eine kostenfreie Lizenz für bis zu 5 Nodes anfordern kann. Das reicht für mich und ich kann die Komfortfunktion, Images automatisch zu pullen, wieder benutzen, die vor einiger Zeit wohl aus der Community Edition entfernt wurde.
Portainer wird hier nach draußen über die Ports 8800 statt 8000, 9900 statt 9000 und 9443 erreichbar gemacht. Außerdem wird auch der Portainer Agent installiert, welcher anstelle des Ports 9001 den Port 9901 benutzt. Damit haben wir später keine Kollision mit dem ioBroker und können dort ohne viel Aufwand die Standardeinstellung beibehalten.
Wenn du die Datei fertig hast, dann öffne ein Terminalfenster und wechsle in das Verzeichnis, wo du die Datei gespeichert hast.
Dort generierst du dann die Fedora CoreOS Ignition JSON Datei coreos.ign
mit dem folgenden Befehl:
docker run -i --rm quay.io/coreos/butane:release < coreos.bu > coreos.ign
Nun starten wir noch einen temporären Webserver über Python auf meiner Workstation:
python3 -m http.server
Alternativ kannst du die coreos.ign
auch irgendwo anders hochladen. Wir müssen sie später nur über cURL irgendwo runterladen können.
3. Fedora CoreOS installieren
-
ISO Image laden:
Lade das aktuelle Stable Image von Fedora CoreOS auf den Proxmox Server. Das geht inzwischen auch über die GUI auf dem Local Storage unter ISO Images --> Download from URL.
CoreOS ist eine hervorragende Basis, um Docker auf Proxmox zu ergänzen. Es ist sehr wartungsarm, denn es ist dafür gebaut ein Unterbau für Container zu sein und sich selbstständig aktuell zu halten. Die Installation ist etwas umständlich, aber dafür erhalten wir hier auch echte Infrastructure-as-Code, wie man neudeutsch so schön sagt. Das fügt sich super in das Konzept von Docker mit ein, wo Images über das Festtackern an bestimmte Versionen bzw. Tags auch eine verlässliche Reproduzierbarkeit erreicht wird.
-
Virtuelle Maschine erstellen:
Auch das Erstellen einer virtuellen Maschine ist wohl kaum der Rede wert. Es gibt nur wenige Punkte zu beachten:
- Weise der VM 2 Festplatten zu. Bei der ersten Festplatte stellst du ein, dass sie vom Backup ausgenommen ist. Dort liegt später nur das System bzw. die Docker Container und die werden im Falle eines kompletten Restore sehr einfach von Grund auf wiederhergestellt. Du kannst das anders handhaben, aber ich mache das momentan so.
- Lass den QEMU Guest Agent noch deaktiviert.
- Gib das Fedora CoreOS ISO Image für die Installationsquelle an.
-
Fedora Core OS auf die Festplatte kopieren:
Starte jetzt die VM und Fedora CoreOS wird als Live-Umgebung direkt aus dem ISO Image heraus gestartet. Passe den folgenden Befehl an deine Umgebung an, um die zuvor generierte Fedora CoreOS Ignition Datei zu laden:
curl -O 192.168.178.100:8000/coreos.ign
Du kannst noch prüfen, ob die Datei auch erfolgreich geladen wurde, indem du kurz hineinschaust:
cat coreos.ign
Ist alles in Ordnung, dann installierst du CoreOS nun auf der ersten Festplatte /dev/sda
und schaltest das System anschließend aus:
sudo coreos-installer install /dev/sda --ignition-file coreos.ign
sudo poweroff
Füge nun einen Serial Port zur VM hinzu, entferne das ISO Image aus dem virtuellen CD-Laufwerk der VM in Proxmox, und starte die VM wieder. Wenn du dich jetzt mit der Proxmox Console über xterm.js statt noVNC verbindest, wirst du über den seriellen Port statt über die emulierte Grafikkarte verbunden und direkt als Benutzer core
eingeloggt. Mit sudo
kannst du lokale Docker befehle ausführen. Das ist eine praktische Alternative zum SSH Login.
Läuft alles gut, dann sollte auch nach kurzer Zeit auf der von dir eingestellten IP-Adresse und Port 9443 der Portainer zur weiteren Konfiguration erreichbar sein. Unter Environments kannst du dabei auch statt der lokalen Docker Verbindung den Portainer Agent Dienst angeben. Nutze dafür einfach die IP-Adresse deines CoreOS Host und den Port 9901. Damit stehen weitere Funktionen in Portainer zur Verfügung, beispielsweise kannst du auf die Inhalte der Docker Volumes über den Browser zugreifen.
CoreOS hat auch automatisch die zweite Festplatte eingebunden und dort werden nun die Docker Volumes abgelegt, die wir auch wie oben beschrieben über Proxmox backupen können. Beim ersten Start wird auch der QEMU Guest Agent nachinstalliert, so dass du ihn dann in Proxmox aktivieren kannst. Dafür musst du die Maschine nochmals komplett ausschalten, die Einstellung in Proxmox ändern, und die VM wieder starten.
Glückwunsch! Wir haben jetzt ein sauberes Docker Basissystem, was sich selbst aktuell hält und wartet, so dass wir uns nun vollkommen auf die eigentlichen Dienste konzentrieren können.
4. Docker Stack hochladen und starten
Es gibt bereits unzählige Beschreibungen, wie man seine Docker Umgebung aufbaut. Über Portainer kannst du unter dem Menüpunkt Stacks
eine Docker Compose Datei hochladen bzw. dort definieren. Ein Beispiel, wie das im kleinen Ausbaustil aussehen könnte, möchte ich abschließend noch kurz zeigen:
version: '3.8'
services:
influxdb:
image: influxdb:2.2-alpine
restart: always
container_name: influxdb
ports:
- 8086:8086
environment:
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: mySecretInfluxAdminToken
DOCKER_INFLUXDB_INIT_BUCKET: iobroker
DOCKER_INFLUXDB_INIT_CLI_CONFIG_NAME: default
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_ORG: MyHome
DOCKER_INFLUXDB_INIT_PASSWORD: myEvenMoreSecretInfluxAdminPassword
DOCKER_INFLUXDB_INIT_RETENTION: 52w
DOCKER_INFLUXDB_INIT_USERNAME: admin
volumes:
- influxdb:/var/lib/influxdb2
- influxdb_conf:/etc/influxdb2
iobroker:
image: buanet/iobroker:latest-v7
restart: always
container_name: iobroker
hostname: iobroker
network_mode: host
environment:
AVAHI: "true"
PACKAGES: avahi-daemon libpam0g-dev
volumes:
- iobroker:/opt/iobroker
labels:
autoheal: true
volumes:
influxdb:
influxdb_conf:
iobroker:
Ich lasse ioBroker hier im Netzerk Host-Modus laufen, damit es keine Probleme bei den Adaptern Yahka und Sonos gibt. Kann aber auch anders aussehen, wer hätte das gedacht
Viel Spaß beim nachkochen!
—Julian