Files
unraid-podman/docs/ARCHITECTURE.md
T
maggesandClaude Sonnet 5 2b79411b68 Replace vendored docker-compose with podman-compose
podman-compose and docker-compose aren't discovered the same way by
`podman compose` - verified live (a fake-binary test reading podman's own
provider-search error output) that docker-compose is searched for by
exact path across a fixed list of CLI-plugin directories, while
podman-compose is instead looked up as a plain command on $PATH. This
package installs to /usr/local/bin/podman-compose accordingly, not under
any cli-plugins/ directory.

Unlike docker-compose (a single static Go binary), podman-compose is a
Python script with two runtime dependencies neither of which ship with
Unraid's own Python3 - PyYAML and python-dotenv, vendored here as plain
pure-Python source (no C extension build; PyYAML's own fallback handles
its optional C accelerator being absent).

Verified end-to-end on a real host: with the previous docker-compose
binary temporarily moved aside to confirm podman-compose was actually the
one invoked, `podman compose up/ps/down` ran a real compose project
correctly, including a live HTTP check against the started service.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 18:35:38 +00:00

671 lines
39 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# unraid-podman — Architekturplanung
Natives Unraid-Plugin zur vollständigen Integration von Podman (rootful, Phase 1) als
gleichberechtigte Alternative zu Docker, mit dem langfristigen Ziel, Docker optional
abschaltbar zu machen.
Status: Planungsdokument, kein Code. Dient als Grundlage für Implementierungs-Tickets.
---
## 0. Ziele & Nicht-Ziele
**Ziele (Phase 1 / MVP):**
- Podman rootful lauffähig unter Unraid, überlebt Reboots (Config, Images, Container, Volumes, Netzwerke).
- Koexistenz mit Docker ohne Konflikte (Storage, Netzwerk, Ports, iptables-Ketten).
- Start/Stop/Autostart analog zum bestehenden `rc.docker`-Muster, ohne systemd.
- Sauberer Update-/Rollback-Pfad für das Plugin selbst.
- Grundlegendes Logging & Fehlerdiagnose, das GUI-Meldungen von Unraid nutzt.
- Vorbereitung (nicht Umsetzung) einer WebUI-Anbindung.
**Nicht-Ziele (bewusst verschoben):**
- Rootless Podman (Phase 2+, andere Storage-/UID-Mapping-Anforderungen).
- Podman Pods / Kubernetes-YAML-Support (Phase 3, siehe Roadmap).
- 1:1-Ersatz der Community-Applications-Vorlagen (erfordert eigenen Übersetzungslayer, separat geplant).
- Abschalten von Docker (erst wenn Podman-Pfad stabil ist, siehe Abschnitt 17).
---
## 1. Rahmenbedingungen von Unraid, die die Architektur erzwingen
| Eigenschaft | Konsequenz für das Plugin |
|---|---|
| Unraid läuft komplett im RAM (SquashFS + tmpfs-Overlay via `unionfs`) | Alles, was `/`, `/etc`, `/usr`, `/var` betrifft, ist bei jedem Reboot verloren. Nichts darf implizit dort persistiert werden. |
| Persistenter Speicher nur unter `/boot` (USB-Stick, FAT32, klein, langsam, schreibintensiv riskant) und `/mnt/*` (Array/Cache/Pools) | Konfiguration klein & textbasiert auf `/boot/config/plugins/...`; große/volatile Daten (Images, Container-Storage, Logs) auf Array/Cache. |
| Kein systemd, sondern BSD-artige `/etc/rc.d/rc.*`-Skripte, angestoßen über `/boot/config/go` | Eigenes `rc.podman`-Init-Skript im klassischen `start|stop|restart|status`-Stil, keine `.service`-Units, keine `systemctl`-Aufrufe irgendwo im Code. |
| Slackware-Basis, Pakete als `.txz` (pkgtools: `installpkg`/`upgradepkg`/`removepkg`) | Podman + Abhängigkeiten müssen als Slackware-kompatible `.txz` gebaut werden, nicht als RPM/DEB. |
| Plugins werden über `.plg`-XML installiert (lädt Pakete, führt `<FILE>`-Blöcke mit Pre-/Post-Scripts aus) | Das `.plg` ist der zentrale Installations-/Update-/Deinstallations-Mechanismus, vergleichbar mit einem deklarativen Installer. |
| `/mnt/user` (shfs/FUSE) hat Einschränkungen für Overlay-Filesysteme (fehlendes `d_type` in manchen Konstellationen, FUSE-Overhead) | Podman-Storage (`overlay`-Driver) nicht direkt auf `/mnt/user/...` legen, sondern auf ein dediziertes Loopback-Image oder einen direkt gemounteten Pool/Disk-Pfad (Analogie zu `docker.img` bzw. Docker-„Directory“-Modus seit 6.12). |
| Array kann offline sein / erst spät im Boot verfügbar (`emhttpd`, Array-Start ist ein User-Trigger, kein automatischer Boot-Schritt) | `rc.podman` darf nicht blind beim Boot starten, sondern muss auf Array-/Pool-Verfügbarkeit warten bzw. vom `dynamix`-Event „array started“ getriggert werden, wie es `rc.docker` heute schon tut. |
| Unraid pflegt eigene Firewall-/NAT-Logik (`iptables`) für Docker-Netzwerke (`docker0`, custom bridges, macvlan) | Podman-Netzwerke (netavark) müssen eigene, klar abgegrenzte iptables-Ketten/Chains verwenden, um nicht mit Dockers Regeln zu kollidieren. |
---
## 2. Gesamtarchitektur (Überblick)
```
┌───────────────────────────────────────────┐
│ /boot (USB, FAT32) │
│ config/plugins/podman/ │
│ ├─ podman.cfg (Settings) │
│ ├─ autostart (Flat-File) │
│ ├─ network.json │
│ ├─ containers.conf / storage.conf (Vorlagen)
│ └─ backup/ (vorherige Paketversionen) │
└───────────────────┬─────────────────────────┘
│ beim Boot kopiert nach /etc, /var/lib
┌────────────────────────────────────────────────────────────────────┐
│ RAM-Root (tmpfs/unionfs) │
│ /etc/rc.d/rc.podman (Init-Skript) │
│ /etc/containers/* (aus /boot kopiert) │
│ /usr/bin/podman, conmon, crun, netavark, aardvark-dns, ... │
│ /usr/local/emhttp/plugins/podman/ (WebUI-Stub, PHP) │
└───────────────────────────┬───────────────────────────────────────┘
│ bind-mount / Storage-Root zeigt auf
┌────────────────────────────────────────────────────────────────────┐
│ Persistenter Bereich (Cache-Pool empfohlen) │
│ /mnt/cache/system/podman/ │
│ ├─ podman.img (Loopback, XFS/BTRFS, für overlay-Storage) │
│ │ → gemountet auf /var/lib/containers/storage │
│ ├─ logs/ │
│ └─ networks/ (netavark state, falls nicht im Image) │
└────────────────────────────────────────────────────────────────────┘
```
Kernprinzip: **Trennung von „Konfiguration“ (klein, Text, `/boot`) und „Nutzdaten“
(groß, binär, Array/Cache)** — exakt das bestehende Unraid-Muster, das auch
`dynamix.docker.manager` verwendet.
---
## 3. Pluginstruktur
### 3.1 Repository-Layout (Build-/Source-Repo, nicht das, was auf Unraid landet)
```
unraid-podman/
├── podman.plg # Haupt-Plugin-Manifest (XML)
├── packages/ # Slackware .txz Build-Rezepte
│ ├── podman/
│ ├── conmon/
│ ├── crun/
│ ├── netavark/
│ ├── aardvark-dns/
│ ├── containers-common/
│ ├── fuse-overlayfs/
│ └── passt/
├── source/
│ ├── rc.podman # Init-Skript
│ ├── podman-preflight.sh # Startup-Checks
│ ├── podman-autostart.sh
│ ├── podman-backup.sh
│ └── config-templates/
│ ├── containers.conf
│ ├── storage.conf
│ └── registries.conf
├── emhttp/
│ └── plugins/podman/ # WebUI (PHP/JS), Phase 2+
├── scripts/
│ ├── build-packages.sh
│ └── release.sh
└── CHANGELOG.md
```
### 3.2 `.plg`-Manifest — Verantwortlichkeiten
Das `.plg` ist eine deklarative XML-Datei, die von Unraids `plugin`-Kommando
interpretiert wird. Verantwortlich für:
- **Metadaten**: `<plugin name="podman" version="..." pluginURL="..." min="..." />` inkl.
Mindest-Unraid-Version (Kernel-/glibc-Kompatibilität der Podman-Binaries).
- **Entity-Variablen** (XML-Entities) für Basis-URL, Version, MD5-Summen — ermöglicht
Update-Checks über den Plugin-Manager der GUI.
- **`<FILE>`-Blöcke**:
1. Download & Installation aller acht `.txz`-Pakete (die sieben Komponenten
plus das plugin-eigene `unraid-podman`-Scaffolding-Paket) einheitlich per
`upgradepkg --install-new --reinstall` — funktioniert unverändert für
Erstinstallation und Update (bestätigtes Muster echter Unraid-Plugins,
siehe unten).
2. `<INLINE>`-Postinstall-Skript: Setzen von Berechtigungen, Anlegen der
Verzeichnisstruktur unter `/boot/config/plugins/podman/` (nur beim ersten
Install, idempotent; Configs nur kopiert, wenn nicht vorhanden — kein
Überschreiben bestehender Nutzerkonfiguration bei Updates), Schreiben der
Versions-Manifest-Datei, erster `rc.podman start`.
- **Array-Start/Stop-Hook**: **kein** Eintrag in `/boot/config/go`. Stattdessen
der offizielle Unraid-Plugin-Event-Mechanismus:
`/usr/local/emhttp/plugins/podman/event/disks_mounted` (startet `rc.podman`,
sobald Cache-Pools/Disks gemountet sind) und `.../event/stopping` (stoppt
`rc.podman` synchron als allererster Schritt der Shutdown-Sequenz, bevor
Unraid die zugrundeliegende Disk unmountet). Dieses Verzeichnis-Konvention
wurde gegen den echten Quellcode etablierter Plugins verifiziert (u. a.
`unassigned.devices`, das exakt dieselben `event/`-Hooks für
`disks_mounted`/`stopping_svcs` verwendet) — kein `/boot/config/go`-Edit
nötig, dadurch auch beim Deinstallieren nichts manuell rückgängig zu machen.
- **`<FILE Run="/bin/bash" Method="remove">`-Block für Deinstallation**: ruft
zuerst `podman-uninstall-cleanup.sh` auf (stoppt `rc.podman`, räumt
Laufzeitzustand auf), dann `removepkg` für alle acht Pakete. Lässt Nutzdaten
unter `/mnt/*` und `/boot/config/plugins/podman/` standardmäßig **stehen**
(explizite Option „Restlose Deinstallation inkl. Daten“ via
`--purge-data`-Flag, kein automatisches Löschen von Nutzdaten).
### 3.3 Namens- und Versionskonventionen
- Plugin-Name: `podman` (Namespace-Kollisionen mit CA-Plugins prüfen).
- Paket-Versionierung getrennt von Plugin-Versionierung: `podman.plg` trägt eine eigene
SemVer (`1.2.0`), referenziert aber exakte Upstream-Versionen der Binärpakete
(`podman-5.x.x`, `netavark-1.x.x`, ...) — erlaubt Plugin-Patches (z. B. am
Init-Skript) ohne Podman-Versionssprung.
---
## 4. Verzeichnislayout (vollständig)
### 4.1 Auf dem Flash-Device (`/boot`, persistent, klein)
```
/boot/config/plugins/podman/
├── podman.cfg # Key=Value, analog docker.cfg (Storage-Pfad, Enable, Optionen)
├── autostart # eine Container-ID/-Name pro Zeile, Reihenfolge = Startreihenfolge
├── autostart-delay # optionale Wartezeiten pro Container (Key=Sekunden)
├── containers.conf # Vorlage, wird nach /etc/containers/ kopiert
├── storage.conf # Vorlage, referenziert podman.img-Pfad
├── registries.conf
├── policy.json
├── networks/ # persistierte netavark-Netzwerkdefinitionen (JSON)
├── backup/
│ ├── packages/<version>/*.txz # vorherige Paketversionen für Rollback
│ └── config/<timestamp>/ # Config-Snapshots vor Updates
└── plugin.log # Install-/Update-Log des Plugins selbst
```
`/boot/config/plugins/podman/` ist bewusst analog zu
`/boot/config/plugins/dynamix.docker.manager/` gehalten (Wiedererkennung für
erfahrene Unraid-Nutzer, Support-Vergleichbarkeit).
### 4.2 Laufzeit (RAM-Root, bei jedem Boot neu aufgebaut)
```
/etc/rc.d/rc.podman # Init-Skript (Symlink-Ziel oder direkt kopiert)
/etc/containers/{containers,storage,registries}.conf # aus /boot kopiert
/etc/containers/policy.json
/etc/cni/ (falls CNI-Fallback statt netavark benötigt)
/usr/bin/{podman,conmon,crun,fuse-overlayfs,passt,pasta}
/usr/libexec/podman/{netavark,aardvark-dns} # matches upstream's own layout
/usr/local/emhttp/plugins/podman/ # WebUI-Stub (statisch aus Paket)
/var/run/podman/podman.sock # API-Socket (rootful)
/var/log/podman -> /mnt/cache/system/podman/logs # Symlink, siehe Abschnitt 13
```
### 4.3 Persistente Nutzdaten (Cache-Pool bevorzugt, Array als Fallback)
```
/mnt/cache/system/podman/ # analog /mnt/user/system/docker/
├── podman.img # Loopback-Datei, XFS (ftype=1) oder BTRFS
│ # → gemountet auf /var/lib/containers/storage
├── logs/
│ ├── containers/<id>.log
│ └── podman-service.log
└── networks/ # falls netavark-State nicht im Image liegt
```
**Warum Cache-Pool statt `/mnt/user`:** Der `overlay`-Storage-Driver benötigt native
Dateisystemsemantik (d_type, xattrs, Hardlinks) ohne den FUSE-Layer von `shfs`. Das
Docker-Vorbild löst das identisch mit `docker.img`/Directory-Modus auf einem echten
Mountpoint. Nutzer ohne Cache-Pool müssen einen Array-Disk-Pfad (`/mnt/diskX/system/podman`)
wählen können — mit deutlicher GUI-Warnung bzgl. Performance und Spin-up-Verhalten.
---
## 5. Paketmanagement
### 5.1 Zu paketierende Komponenten
| Paket | Zweck | Bemerkung |
|---|---|---|
| `podman` | Kern-Binary | Statisch gegen möglichst wenige glibc-Versionen bauen oder gegen Unraid-Slackware-Base kompilieren |
| `conmon` | Container-Monitor-Prozess | Pflicht |
| `crun` (empfohlen) / `runc` | OCI-Runtime | `crun` bevorzugt (leichter, cgroup v2-freundlich, kompatibel zu Unraids Kernel-Config) |
| `netavark` + `aardvark-dns` | Netzwerk-Backend + DNS | Ersetzt CNI-Plugins als Default seit Podman 4.x |
| `containers-common` | Default-Configs (`containers.conf`, `seccomp.json`, `registries.conf`) | Wird als Vorlage übernommen, nicht als aktive Config |
| `fuse-overlayfs` | Fallback-Storage-Driver | Für Rootless-Phase 2 vorbereitet, in Phase 1 optional |
| `passt`/`pasta` | Rootless-Networking | Nachfolger von slirp4netns, Phase 2, aber Paket schon mitbauen (geringe Kosten) |
| `catatonit` oder `tini` | Init-Prozess in Containern (optional, falls von Templates genutzt) | |
| `nftables` | Firewall-Backend für `netavark` | Pflicht seit netavark 2.0 (iptables-Treiber entfernt); Unraid liefert kein `nft` mit — als offizielles Slackware-Paket vendored, nicht selbst gebaut |
| `podman-compose` | External-Compose-Provider für `podman compose` | `podman compose` hat keine eigene Compose-Implementierung, sondern sucht ein Kommando namens `podman-compose` auf `$PATH` (live verifiziert — anders als das ältere, ebenfalls unterstützte `docker-compose`, das stattdessen in festen CLI-Plugin-Pfaden gesucht wird); ohne dieses Paket schlägt jede Compose-Panel-Aktion auf einem frischen Unraid-Install fehl. Python-Skript, vendored zusammen mit PyYAML/python-dotenv als reines Python-Source (kein C-Build) |
### 5.2 Build-Strategie
- Eigene Build-Pipeline (containerisiert, z. B. in einem Slackware-kompatiblen
Build-Container) statt manuellem Cross-Compile auf laufenden Unraid-Systemen.
- Zielarchitektur: `x86_64` (Unraid unterstützt aktuell keine anderen Architekturen
produktiv) — vereinfacht Matrix erheblich.
- Statisch oder minimal dynamisch gelinkt (Go-Binaries von Podman/netavark/aardvark-dns
sind ohnehin größtenteils statisch), um Abhängigkeits-Drift gegenüber der jeweils
aktuellen Unraid-Slackware-Basis zu minimieren.
- Jedes Paket als eigenständiges `.txz` mit Slackware-Standard-Metadaten
(`slack-desc`, `doinst.sh`), damit `removepkg`/`upgradepkg` korrekt funktionieren.
- Versions-Pinning: Das `.plg` referenziert exakte Paketversionen + MD5, kein
„latest“-Pull zur Laufzeit — reproduzierbare Installationen, Voraussetzung für Rollback.
### 5.3 Abhängigkeitsprüfung
- Preflight-Check im `.plg`-Postinstall: Kernel-Version, `cgroup v2` aktiv,
vorhandene `iptables`/`nftables`-Binaries, freier Platz auf Zielpfad für
`podman.img`. Bei Nichterfüllung: Installation abbrechen mit klarer GUI-Meldung
statt eines halb-funktionsfähigen Zustands.
---
## 6. Start-/Stop-Skripte
### 6.1 `rc.podman` — Design analog zu `rc.docker`
Klassisches Slackware-BSD-Init-Skript mit Case-Dispatch:
```
rc.podman start # Preflight → Storage mounten → API-Service starten → Autostart-Liste abarbeiten
rc.podman stop # Container geordnet stoppen (Timeout) → API-Service stoppen → Storage unmounten
rc.podman restart
rc.podman status # Health-Check-Ausgabe für GUI/CLI
```
Kernschritte von `start` (konzeptionell, kein Code):
1. **Preflight** (`podman-preflight.sh`): prüft, ob konfigurierter Storage-Pfad
(Cache-Pool/Disk) gemountet ist; falls `podman.img` fehlt → anlegen (Erstinstallation)
mit konfigurierter Größe; falls vorhanden → per Loopback mounten.
2. **Config-Sync**: Kopiert `/boot/config/plugins/podman/*.conf` nach
`/etc/containers/` (Boot-Config ist „Source of Truth“, Laufzeit-Kopie ist Cache).
3. **Socket-Service starten**: `podman system service` im Hintergrund (rootful Unix-Socket
unter `/var/run/podman/podman.sock`), damit spätere WebUI/API-Konsumenten und CLI
dieselbe Instanz sehen (statt pro Aufruf neuer Podman-Fork-Prozesse ohne gemeinsamen State
— State liegt zwar in `/var/lib/containers`, ein Service vereinfacht aber Health-Checks,
Event-Streaming und spätere WebUI-Anbindung).
4. **Netzwerke wiederherstellen**: netavark-Netzwerkdefinitionen aus
`/boot/config/plugins/podman/networks/` nach `/etc/containers/networks/` synchronisieren.
5. **Autostart** (`podman-autostart.sh`, siehe Abschnitt 10).
6. **Statusdatei** schreiben (für GUI-Polling), Event ins Unraid-Log (`logger`).
`stop` in umgekehrter Reihenfolge, mit konfigurierbarem Stop-Timeout pro Container
(analog Dockers „Stop timeout“-Einstellung), danach `podman system service` beenden,
zuletzt Loopback-Storage sauber unmounten (verhindert Dateisystemfehler im `podman.img`).
### 6.2 Boot-Integration
- **Offizieller Unraid-Plugin-Event-Mechanismus**, kein `/boot/config/go`-Edit:
Unraids `emhttpd` führt bei jedem Array-/Boot-Event automatisch jedes
ausführbare Skript unter `/usr/local/emhttp/plugins/<name>/event/<eventname>`
aus, sofern vorhanden. Dieses Verhalten wurde gegen den echten Quellcode
etablierter Plugins verifiziert (`unassigned.devices` nutzt exakt dieselbe
Konvention für seine `disks_mounted`/`started`/`stopping_svcs`-Hooks).
unraid-podman nutzt zwei Events:
- `event/disks_mounted` — feuert, sobald Array-Disks **und** Cache-Pools
gemountet sind (Unraids Event-Reihenfolge: `starting``array_started`
`disks_mounted``svcs_restarted``docker_started``libvirt_started`
`started`). Ruft `rc.podman start` im Hintergrund auf (`& disown`),
damit der Array-Start nicht blockiert.
- `event/stopping` — der **erste** Schritt der Shutdown-Sequenz (vor
`stopping_docker`, `stopping_svcs`, `unmounting_disks`, `stopping_array`).
Ruft `rc.podman stop` **synchron** auf, damit Container gestoppt und
`podman.img` sauber unmountet sind, bevor Unraid die zugrundeliegende
Disk/den Cache-Pool unmountet.
Dadurch kein Start vor Verfügbarkeit von Cache/Array, und beim Deinstallieren
ist nichts manuell rückgängig zu machen — die Hook-Skripte verschwinden
automatisch mit `removepkg` des `unraid-podman`-Pakets (siehe Abschnitt 3.2).
---
## 7. Persistenz-Strategie (Zusammenfassung der Prinzipien)
| Datenkategorie | Ablageort | Synchronisationsrichtung |
|---|---|---|
| Plugin-Settings (Storage-Pfad, Feature-Flags) | `/boot/config/plugins/podman/podman.cfg` | Boot → Laufzeit (read-only Kopie) |
| Container-/Netzwerk-/Registry-Config | `/boot/config/plugins/podman/*.conf` | Boot → Laufzeit bei jedem Start |
| Autostart-Liste & Reihenfolge | `/boot/config/plugins/podman/autostart` | Boot → Laufzeit; GUI schreibt zurück nach `/boot` |
| Images, Container, named Volumes, Layer-Cache | `podman.img` auf Cache-Pool | Laufzeit-only, Backup optional via Snapshot |
| Logs | Cache-Pool `logs/` | Laufzeit-only, Rotation |
| Netzwerk-Definitionen (netavark JSON) | Boot-Kopie + Laufzeit-Kopie | bidirektional bei Änderung über GUI/CLI (Watcher oder expliziter „Apply“-Schritt) |
Grundregel: **Jede Änderung, die ein Nutzer über CLI/GUI an persistenzrelevanten
Objekten vornimmt (neues Netzwerk, geänderte Autostart-Reihenfolge), muss explizit
oder per Watcher nach `/boot/config/plugins/podman/` zurückgeschrieben werden** —
sonst geht sie beim nächsten Reboot verloren. Das ist der Kernunterschied zu
Standard-Linux-Distributionen und der häufigste Fehlerquell-Kandidat bei Docker-artigen
Unraid-Plugins.
---
## 8. Netzwerke
- **Backend**: `netavark` + `aardvark-dns` (Podman-Default seit 4.x), kein CNI in Phase 1.
- **Firewall-Treiber**: `netavark` >= 2.0 hat seinen `iptables`-Treiber ersatzlos entfernt —
nur noch `nftables` oder `firewalld` (Letzteres braucht systemd/dbus, hat Unraid nicht).
`nftables` ist also zwingend, aber Unraid OS bringt das `nft`-Binary selbst nicht mit
(nur das ältere `iptables`/`iptables-nft`) — muss vom Plugin bereitgestellt werden
(verifiziert per Live-Test: `podman run` mit Port-Publishing scheitert ohne `nft`
mit „Must provide a valid firewall backend“).
- **Default-Bridge**: eigene Bridge `podman0` (nicht `docker0`), eigener privater
Adressraum (konfigurierbar, Default-Vorschlag außerhalb von Dockers Default-Range,
um Kollisionen bei Parallelbetrieb zu vermeiden).
- **Custom Networks**: analog Docker-für-Unraid „Custom Networks“-Feature — GUI-Verwaltung
in Phase 2, Config-Persistenz-Mechanismus aber bereits in Phase 1 vorsehen (Abschnitt 7).
- **macvlan/ipvlan**: gleiche Kernel-Voraussetzungen wie bei Docker; bekannte Unraid-Falle
„macvlan call trap“ (Kernel-Panic bei bestimmten NIC-Treibern) muss dokumentiert und im
Preflight als Warnung ausgegeben werden, wenn macvlan-Netzwerke angelegt werden.
- **iptables/nftables-Koexistenz mit Docker**: netavark legt eigene Chains an
(`NETAVARK-*` Präfix); explizit verifizieren, dass diese nicht mit Dockers
`DOCKER`-Chains oder Unraids eigenen Firewall-Regeln interferieren. Firewall-Reload
durch Docker (bei dessen Start/Stop) darf Podman-Regeln nicht löschen und umgekehrt —
ggf. eigenes `iptables-restore`-Hook nach jedem `rc.podman start`.
- **DNS**: `aardvark-dns` für Container-zu-Container-Namensauflösung innerhalb
benutzerdefinierter Netzwerke, analog Dockers eingebettetem DNS-Server.
- **Port-Publishing**: Kollisionsprüfung mit bereits von Docker/anderen Diensten
belegten Ports als GUI-seitige Validierung (spätere Phase), technisch bereits
durch Kernel/`iptables` ohnehin erzwungen.
---
## 9. Volumes
- **Named Volumes**: liegen innerhalb von `podman.img` unter
`.../storage/volumes/`, damit sie vom selben Backup-/Rollback-Mechanismus wie Images
erfasst werden.
- **Bind-Mounts** auf Unraid-Shares (`/mnt/user/appdata/...`): empfohlener Standardweg
für Nutzerdaten (analog Dockers „Path“-Mappings in Community-Applications-Templates),
da `/mnt/user/appdata` bereits vom Nutzer im Backup-/Mover-/Cache-Konzept berücksichtigt
wird. Bind-Mounts direkt gegen `/mnt/user` (FUSE) sind für Applikationsdaten unkritisch
(kein Overlay-Storage-Anspruch), nur der Storage-Graph selbst braucht den echten Mount.
- **Permissions/UID-Mapping**: Phase 1 rootful → Container laufen mit denselben
UID/GID-Semantiken wie Docker rootful heute; keine zusätzliche User-Namespace-Remapping-
Komplexität. Wird in Phase 2 (rootless) relevant.
- **Migration-Hilfe (spätere Phase, nicht MVP)**: Werkzeug zum Referenzieren bestehender
Docker-Bind-Mount-Pfade beim Anlegen äquivalenter Podman-Container, um Parallelbetrieb
mit denselben Appdata-Verzeichnissen zu erleichtern (mit klarer Warnung vor
gleichzeitigem Schreibzugriff durch zwei Runtimes).
---
## 10. Images
- **Storage-Driver**: `overlay` (native, kein `fuse-overlayfs`) auf dem gemounteten
`podman.img` — Performance-Parität mit Dockers Standard-Setup.
- **Registries**: Default `registries.conf` mit Docker Hub + ggf. `ghcr.io`, `quay.io`
vorkonfiguriert, kurz-Namen-Auflösung (`unqualified-search-registries`) explizit
gesetzt statt interaktivem Prompt (der in einer daemonless/Skript-Umgebung nicht
funktioniert).
- **Pull/Push**: Standard-Podman-CLI-Semantik, keine Besonderheiten; Fortschrittsanzeige
später über API-Service-Events für die WebUI nutzbar.
- **Image-Größenmanagement**: `podman.img` ist eine feste/dynamisch wachsende
Loopback-Datei — GUI muss Füllstand anzeigen und Vergrößern anbieten (analog Dockers
„docker.img full“-Problem, das in der Community bekannt und schmerzhaft ist; hier von
Anfang an mit klarer Fehlermeldung statt stillem Fehlschlag lösen, siehe Abschnitt 16).
- **Community-Applications-Kompatibilität**: kein automatischer Import von
Docker-Templates in Phase 1 (eigenes, separates Vorhaben); Podman-Images werden
zunächst über CLI/eigene, minimale Template-Definition verwaltet.
- **Signatur-Policy** (`config/policy.json`): `insecureAcceptAnything` als Default —
akzeptiert Images ohne Signaturprüfung, entspricht Dockers heutigem
Standard-Vertrauensmodell auf Unraid. Vor einem 1.0-Release erneut bewerten, falls
signierte Images ein Ziel werden. Die Datei selbst darf keine Kommentarfelder
enthalten (`containers/image`s Policy-Parser lehnt unbekannte Top-Level-Keys wie
`_comment` strikt ab) — Begründung lebt deshalb hier, nicht in der Datei.
---
## 11. Container (Lifecycle-Management)
- Lifecycle-Operationen (`create`, `start`, `stop`, `restart`, `remove`, `exec`,
`logs`, `inspect`) laufen über den in Abschnitt 6.1 gestarteten Podman-API-Service,
nicht über Ad-hoc-CLI-Aufrufe aus der GUI heraus — ein gemeinsamer Service reduziert
Race Conditions und ermöglicht Event-Streaming.
- **Labels für GUI-Integration**: eigenes Label-Schema (z. B. `net.unraid.podman.icon`,
`net.unraid.podman.webui`), angelehnt an, aber getrennt von Dockers
`net.unraid.docker.*`-Konventionen, um spätere GUI-Icons/WebUI-Links analog zur
Docker-Tab-Darstellung zu ermöglichen, ohne Namensraum-Kollisionen.
- **Update-Strategie für Container-Images**: „Check for Updates“-Mechanismus
(Digest-Vergleich gegen Registry) als spätere GUI-Funktion, technisch schon in
Phase 1 per `podman image inspect`/`skopeo`-Vergleich vorbereitbar.
---
## 12. Autostart
Da kein systemd existiert, wird Autostart **explizit vom Plugin verwaltet**, nicht von
Podman selbst (Podman hat kein natives „restart on boot“ ohne generierte Unit-Files):
- Flache Datei `/boot/config/plugins/podman/autostart`, eine Zeile pro Container
(Name oder ID), Reihenfolge = Zeilen-Reihenfolge = Startreihenfolge — direktes
Äquivalent zu Dockers `/boot/config/plugins/dynamix.docker.manager/docker-autostart`
bzw. der DB-gestützten Nachfolgelösung.
- Optionale, separate Delay-Datei für Wartezeiten zwischen Starts (Abhängigkeiten
zwischen Containern, z. B. DB vor App).
- `rc.podman start` liest die Liste sequenziell, startet jeden Container über den
API-Service, protokolliert Erfolg/Fehler pro Eintrag, bricht bei Einzelfehlern
**nicht** die gesamte Kette ab (ein defekter Container darf nicht alle anderen
blockieren).
- GUI-Checkbox „Autostart“ pro Container (spätere Phase) schreibt direkt in diese
Datei zurück — kein separates DB-Layer in Phase 1, um Komplexität niedrig zu halten
(kann bei Bedarf später durch strukturierteres Format, z. B. JSON, ersetzt werden).
---
## 13. Updates
### 13.1 Plugin-Update (Podman-Version, Init-Skripte)
- Über den regulären Unraid-Plugin-Update-Mechanismus: `.plg` mit neuer Version +
neuen Paket-URLs/MD5s wird erneut ausgeführt.
- Vor jedem Update: automatisches Backup (siehe Abschnitt 14) der aktuellen
`.txz`-Pakete und Config-Dateien.
- **Container laufen während des Plugin-Updates weiter**, sofern nur Binaries
ausgetauscht werden (laufende `conmon`/Container-Prozesse sind vom
Dateisystem-Austausch unter `/usr/bin` isoliert, solange der Prozess nicht neu
exec't wird) — erst ein nachfolgender `rc.podman restart` übernimmt die neue
Podman-Version für den API-Service. Klar kommunizieren: Update ≠ sofortiger
Container-Neustart.
- Config-Migrationen (z. B. neues Feld in `containers.conf`) über ein
Versions-gestempeltes Migrationsskript, das beim Postinstall geprüft wird
(`podman.cfg` enthält `CONFIG_SCHEMA_VERSION`).
### 13.2 Container-Image-Updates
- Getrennt vom Plugin-Update zu betrachten — reine Podman-Funktionalität
(`podman pull` + Recreate), GUI-Komfortfunktion für Phase 2.
---
## 14. Rollback
- **Paket-Rollback**: Vor jedem Update werden die aktuell installierten `.txz` nach
`/boot/config/plugins/podman/backup/packages/<alte-version>/` kopiert. Ein
„Rollback“-Kommando (Postinstall-Skript, später GUI-Button) installiert diese
Pakete erneut per `upgradepkg --reinstall`.
- **Config-Rollback**: Vor jeder strukturellen Config-Änderung (Plugin-Update mit
Schema-Migration) wird ein Snapshot nach
`/boot/config/plugins/podman/backup/config/<timestamp>/` geschrieben; Rollback
kopiert diesen Snapshot zurück und setzt `CONFIG_SCHEMA_VERSION` entsprechend zurück.
- **Storage-Rollback (Images/Container) ist explizit außerhalb des automatischen
Rollbacks**: `podman.img` wird nicht bei jedem Plugin-Update gesichert (zu groß,
zu I/O-intensiv). Stattdessen: dokumentierte, manuell auslösbare Snapshot-Funktion
(z. B. `cp --reflink` auf BTRFS/ZFS-Cache-Pools, wo Reflinks verfügbar sind) als
optionales Feature, kein Zwang.
- Rollback-Historie begrenzen (z. B. letzte 3 Versionen), um den knappen
Flash-Speicher nicht zu erschöpfen.
---
## 15. Logging
- **Problem**: `/var/log` liegt im RAM und ist bei Reboot weg; `journald` existiert
nicht, daher kann Podmans `journald`-Log-Driver nicht Default sein.
- **Lösung**: Default-Log-Driver `k8s-file` (JSON-per-Zeile, Podman-nativ) mit
Zielverzeichnis unter `/mnt/cache/system/podman/logs/containers/`, per
Symlink von `/var/log/podman` dorthin erreichbar (einheitlicher Pfad für Tools,
die `/var/log` erwarten, ohne RAM-Verlust).
- **Log-Rotation**: eigener Cron-Eintrag (Unraid nutzt `cron` bereits für andere
Plugins über `/boot/config/plugins/dynamix/`-Muster bzw. `/etc/cron.d`) mit
`logrotate`-Konfiguration oder einfachem Größen-/Alter-basiertem Skript, da
Flash/Cache-Platz begrenzt ist.
- **Service-Log** (`podman system service`, `rc.podman`-Ausführung) getrennt von
Container-Logs, ebenfalls auf Cache-Pool, zusätzlich Kurzfassung wichtiger
Ereignisse (Start/Stop/Fehler) über `logger` in Unraids zentrales Syslog, damit
sie in der Standard-„System Log“-GUI sichtbar sind.
- **GUI-Zugriff (spätere Phase)**: Log-Viewer analog Dockers Container-Log-Fenster,
liest direkt aus den `k8s-file`-Logs bzw. streamt über den API-Service.
---
## 16. Fehlerbehandlung
### 16.1 Startup-Robustheit
- **Preflight-Checks** vor jedem `rc.podman start` (siehe 6.1): Storage-Mount,
Speicherplatz auf `podman.img`, Kernel-Voraussetzungen, Socket-Verfügbarkeit
(kein verwaister Socket von vorherigem Absturz).
- **Storage-Korruption**: Falls `podman.img` beim Mount-Versuch Dateisystemfehler
zeigt (`xfs_repair`/`btrfs check` schlägt fehl oder wird nicht automatisch
ausgeführt) → Start abbrechen, klare GUI-Fehlermeldung mit Handlungsempfehlung
(Recovery-Tool ausführen, Backup einspielen), **kein** automatisches
Neuanlegen/Formatieren ohne Nutzerbestätigung (Datenverlustrisiko).
- **Autostart-Fehler pro Container**: einzelner fehlgeschlagener Container wird
geloggt und übersprungen, blockiert nicht die restliche Kette (siehe Abschnitt 12);
nach N gescheiterten Autostart-Versuchen in Folge wird der Container automatisch
aus der Autostart-Kette pausiert („Safe-Mode“ pro Container) mit GUI-Hinweis,
um Boot-Loops/Ressourcenverschwendung zu vermeiden.
### 16.2 Laufzeitfehler
- **Health-Check des API-Service**: `rc.podman status` prüft Socket-Erreichbarkeit
und meldet Diskrepanzen (Prozess läuft, Socket tot o. ä.) verständlich.
- **Firewall-/Netzwerk-Konflikte**: bei bekannten Fehlerbildern (z. B. Chain-Konflikt
mit Docker, Port bereits belegt) definierte, für Menschen lesbare Fehlermeldungen
statt roher `iptables`/Go-Stacktraces — Fehlerkatalog mit Klartext-Ursache +
nächstem Schritt.
- **Ressourcenerschöpfung** (`podman.img` voll): aktive Prüfung im Preflight und
periodisch (Cron), GUI-Warnung bei > 85 % Füllstand, klare Anleitung zum
Vergrößern des Loopback-Images (bekannte Docker-für-Unraid-Schwachstelle, hier
von Anfang an proaktiv statt reaktiv lösen).
- **Unraid-Benachrichtigungssystem**: kritische Fehler (Storage-Mount fehlgeschlagen,
Autostart-Kette größtenteils fehlgeschlagen, `podman.img` > Schwellwert) werden
über Unraids Standard-Notify-Mechanismus (`/usr/local/emhttp/webGui/scripts/notify`)
ausgelöst, damit sie in GUI-Toasts, optional E-Mail/Pushover (falls vom Nutzer
konfiguriert) erscheinen — kein Parallel-Notify-System erfinden.
---
## 17. Docker-Parallelbetrieb & spätere Deaktivierung
### Phase 1 — Parallelbetrieb (Pflicht)
- Getrennte Storage-Roots (`docker.img` vs. `podman.img`), getrennte Default-Bridges,
getrennte iptables-Chain-Präfixe, getrennte Log-Verzeichnisse — keine gemeinsam
genutzten mutable Ressourcen außer read-only Kernel-Features (cgroups, netfilter).
- Ressourcen-Konkurrenz (CPU/RAM/Disk-IO) ist erwartet und wird nicht technisch
verhindert, aber in der GUI/Doku transparent gemacht.
- Beide Runtimes dürfen gleichzeitig laufen, ohne dass Start/Stop des einen den
anderen beeinflusst (insbesondere Firewall-Reloads, siehe Abschnitt 8).
### Phase 2+ — Docker optional deaktivierbar
- Neuer Schalter (z. B. in den bestehenden Docker-Settings oder einem neuen
„Container Engine“-Auswahlbereich): „Docker aktiviert / Podman aktiviert / beide“.
- Deaktivierung von Docker bedeutet: `rc.docker stop` + Entfernen des
Autostart-Hooks aus `/boot/config/go` für Docker, **kein** Deinstallieren des
Docker-Plugins selbst (reversibel, Datenerhalt).
- Voraussetzung für diese Phase: Podman-Pfad muss funktional äquivalent zu den von
Nutzern tatsächlich genutzten Docker-Features sein (mind. Custom Networks, Autostart,
Log-Viewer, Update-Check) — technisch als Gate, nicht nur als Empfehlung, im
Freigabeprozess verankern.
- CA-Kompatibilität (Community Applications erwartet aktuell Docker) ist der
wahrscheinlich größte Blocker für „Docker komplett aus“ und sollte als eigenes
Arbeitspaket (Template-Übersetzung Docker→Podman) vor Freigabe dieser Option
behandelt werden.
---
## 18. Zukünftige WebUI
### 18.1 Architektur (Zielbild, Umsetzung nach MVP)
- Eigene GUI-Seite unter `/usr/local/emhttp/plugins/podman/`, PHP + JS im
bestehenden Unraid-„Dynamix“-Stil (Konsistenz mit restlicher GUI, wiederverwendbare
CSS-/JS-Assets), analog zur Docker-Tab-Struktur (`dynamix.docker.manager`).
- Backend-Kommunikation **nicht** über Shell-Exec von `podman`-CLI-Aufrufen aus PHP
(fragil, schwer zu parallelisieren), sondern über den in Abschnitt 6.1 laufenden
Podman-API-Service (Unix-Socket, REST, Docker-kompatible Teilmenge der API) —
ermöglicht später auch Wiederverwendung bestehender Docker-API-kompatibler
Frontend-Bibliotheken.
- Echtzeit-Updates (Container-Status, Logs) über Server-Sent Events oder Polling
gegen den API-Service, kein WebSocket-Zwang in Phase 1 der WebUI (geringerer
Implementierungsaufwand, ausreichend für Status-Refresh im Sekundenbereich).
### 18.2 Funktionsumfang, gestaffelt
1. **Stufe 1**: Read-only Übersicht (laufende Container, Images, Netzwerke),
Start/Stop/Restart einzelner Container.
2. **Stufe 2**: Container-Erstellung über einfache Formulare (Image, Ports, Volumes,
Env, Labels), Autostart-Verwaltung direkt in GUI (schreibt in Abschnitt-12-Datei).
3. **Stufe 3**: Log-Viewer, Netzwerk-Verwaltung (Custom Networks/macvlan-Anlage),
Update-Check pro Container.
4. **Stufe 4**: Community-Applications-Anbindung/Template-Import (separates Vorhaben).
### 18.3 Sicherheitsaspekt der WebUI
- API-Service-Socket nur lokal (`/var/run/podman/podman.sock`), keine TCP-Exposition
nach außen ohne explizite, abgesicherte Opt-in-Konfiguration (TLS + Auth), da
rootful Podman-API faktisch Root-Äquivalent ist — gleiche Vorsicht wie beim
Docker-Socket.
---
## 19. Sicherheitsbetrachtungen (Phase 1, rootful)
- Rootful Podman-API-Socket ist funktional gleichwertig zu `docker.sock` bzgl.
Angriffsfläche (Zugriff = Root auf dem Host) — Berechtigungen auf den Socket
(`0660`, Gruppe analog `docker`-Gruppe, z. B. neue Gruppe `podman`) restriktiv setzen.
Achtung Docker Sock: root Äquivalenz, daher keine ungeprüfte Gruppenmitgliedschaft
für WebUI-Prozesse vergeben, die nicht ohnehin schon rootäquivalent laufen (`emhttpd`
läuft unter Unraid ohnehin als root, insofern kein zusätzliches Risiko gegenüber
Docker heute — aber explizit dokumentieren, nicht stillschweigend voraussetzen).
- Keine automatische, ungefragte Netzwerk-Exposition von Container-Ports über die
Standard-Bridge hinaus.
- Rollback-/Backup-Mechanismus darf keine Zugangsdaten (Registry-Logins in
`auth.json`) ungesichert auf dem FAT32-Flash ablegen, ohne dass sich der Nutzer
dessen bewusst ist (Flash ist physisch leicht auslesbar) — ggf. Hinweis, sensible
Registry-Credentials bevorzugt nicht dauerhaft dort zu speichern, oder Ablage
ausschließlich auf dem Cache-Pool statt `/boot`.
---
## 20. Phasenplan
| Phase | Inhalt |
|---|---|
| **MVP (Phase 1)** | Plugin-Grundgerüst, Pakete, `rc.podman`, Storage auf Cache-Pool, Autostart-Datei, Basis-Logging, Preflight/Fehlerbehandlung, Rollback für Pakete/Config, **keine WebUI** (CLI-only, `podman` direkt nutzbar) |
| **Phase 2** | WebUI Stufe 12, Custom-Networks-Verwaltung, Log-Viewer, Storage-Snapshot-Feature |
| **Phase 3** | Rootless-Option, Docker-Deaktivierungs-Schalter, CA-Template-Kompatibilitätsschicht |
| **Phase 4** | Pods/Quadlet-artige Konzepte (ohne systemd nur eingeschränkt sinnvoll — ggf. eigenes Skript-Äquivalent statt echter Quadlet/systemd-Generatoren) |
---
## 21. Offene Fragen / Risiken (zur Klärung vor Implementierungsstart)
1. Ziel-Podman-Version und Update-Kadenz (Tracking von Upstream-CVEs erfordert
Paket-Update-Prozess, nicht nur Erstrelease).
2. Cache-Pool-Pflicht vs. Array-Fallback für `podman.img` — Nutzer ohne Cache-Pool
sind bei Docker heute ebenfalls im Nachteil; gleiche Community-Erwartungshaltung
übernehmen oder verbessern?
2a. Verhalten bei mehreren Cache-Pools (Unraid 6.9+ unterstützt mehrere benannte
Pools) — welchen Pool per Default vorschlagen, wie in GUI wählbar machen.
3. `podman system service` als Dauerprozess vs. Socket-Activation-artiges
On-Demand-Start (ohne systemd nur eingeschränkt nachbaubar) — Dauerprozess ist
der pragmatischere Start, sollte aber gegen Ressourcenverbrauch im Idle
evaluiert werden.
4. Genaues Verhalten bei gleichzeitigem Boot von Docker und Podman bzgl.
iptables-Reihenfolge/Locking (`iptables`-Aufrufe sind nicht atomar über
mehrere Prozesse hinweg) — ggf. Locking-Strategie (`xtables_lock`) explizit prüfen.
5. Lizenz-/Signatur-Anforderungen für Community-Verbreitung des Plugins
(Unraid-Plugin-Repository-Richtlinien, Signierung der `.plg`/Pakete).