Rocky container: Unterschied zwischen den Versionen
| (6 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 4: | Zeile 4: | ||
! Parameter !! Wert !! Erläuterung | ! Parameter !! Wert !! Erläuterung | ||
|- | |- | ||
| − | | '''VM Name || container || Name der VM | + | | '''VM Name''' || container || Name der VM |
|- | |- | ||
| '''Disk''' || 20GB || Vorgabe übernehmen | | '''Disk''' || 20GB || Vorgabe übernehmen | ||
| Zeile 12: | Zeile 12: | ||
| '''Ram''' || 4 GB || Speicher | | '''Ram''' || 4 GB || Speicher | ||
|- | |- | ||
| − | | '''Netzwerk (NIC)''' || DMZ|| Interface-Zuweisung in VirtualBox | + | | '''Netzwerk (NIC)''' || DMZ || Interface-Zuweisung in VirtualBox |
|- | |- | ||
| '''Admin''' || root || Passwort: radler | | '''Admin''' || root || Passwort: radler | ||
| Zeile 22: | Zeile 22: | ||
| '''CIDR''' || 24 || Classless Inter-Domain Routing Präfixlänge | | '''CIDR''' || 24 || Classless Inter-Domain Routing Präfixlänge | ||
|- | |- | ||
| − | | '''GW''' || 10.88.2XX.1 || GATEWAY | + | | '''GW''' || 10.88.2XX.1 || GATEWAY |
|- | |- | ||
| − | | '''NS''' || 10.88.2XX.21 || Resolver | + | | '''NS''' || 10.88.2XX.21 || Resolver |
|- | |- | ||
| '''FQDN''' || container.it2XX.int || Fully Qualified Domain Name | | '''FQDN''' || container.it2XX.int || Fully Qualified Domain Name | ||
|- | |- | ||
| − | | '''DOM''' || it2XX.int|| Domain Name | + | | '''DOM''' || it2XX.int || Domain Name |
|} | |} | ||
{{Vorlage:Rocky Setup}} | {{Vorlage:Rocky Setup}} | ||
| − | == | + | == Container mit Podman: Uptime Kuma, Caddy und HedgeDoc (container.it2XX.int) == |
| − | Als Ausblick nach der klassischen Systemadministration | + | Als Ausblick nach der klassischen Systemadministration geht es auf der |
| − | Maschine '''container.it2XX.int''' | + | Maschine '''container.it2XX.int''' um Container-Verwaltung mit Podman. |
| − | Uptime Kuma | + | Als praktisches Beispiel dienen drei Dienste: Uptime Kuma (Monitoring), |
| − | + | Caddy (Reverse Proxy) und HedgeDoc (kollaborative Notizen). Jeder | |
| + | Dienst bekommt sein '''eigenes Verzeichnis mit eigener compose.yaml''' – | ||
| + | das entspricht der Praxis, Container-Stacks unabhängig voneinander | ||
| + | verwaltbar zu halten (eigenes Update, eigenes Down/Up, ohne andere | ||
| + | Dienste zu berühren). Verwaltet werden die einzelnen Stacks über | ||
| + | '''podman-compose'''. | ||
=== Was ist Podman === | === Was ist Podman === | ||
| Zeile 70: | Zeile 75: | ||
* <code>/etc/ssl/own.crt</code> | * <code>/etc/ssl/own.crt</code> | ||
* <code>/etc/ssl/own.key</code> | * <code>/etc/ssl/own.key</code> | ||
| + | |||
| + | === Verzeichnisstruktur === | ||
| + | |||
| + | Jeder Container-Dienst bekommt ein eigenes Verzeichnis mit eigener | ||
| + | Compose-Datei. Der Dateiname ist immer '''compose.yaml''' – das ist der | ||
| + | aktuelle Standardname (der alte Name <code>docker-compose.yml</code> | ||
| + | funktioniert zwar auch noch, wird aber nicht mehr verwendet). | ||
| + | podman-compose sucht beim Aufruf automatisch nach dieser Datei im | ||
| + | aktuellen Arbeitsverzeichnis. | ||
| + | |||
| + | <pre> | ||
| + | /opt/monitoring/ | ||
| + | ├── uptime-kuma/ | ||
| + | │ └── compose.yaml | ||
| + | ├── caddy/ | ||
| + | │ ├── compose.yaml | ||
| + | │ └── Caddyfile | ||
| + | └── hedgedoc/ | ||
| + | └── compose.yaml | ||
| + | </pre> | ||
| + | |||
| + | ;Struktur anlegen | ||
| + | *mkdir -p /opt/monitoring/{uptime-kuma,caddy,hedgedoc} | ||
| + | |||
| + | ;Merksatz | ||
| + | Ein Verzeichnis = ein Dienst = ein eigenständiges Compose-Projekt. | ||
| + | Jeder Dienst lässt sich einzeln starten, stoppen und aktualisieren, | ||
| + | ohne die anderen zu beeinflussen. | ||
=== Wie Container sich im Netzwerk finden: DNS über container_name === | === Wie Container sich im Netzwerk finden: DNS über container_name === | ||
| − | Podman (wie Docker) legt für jedes | + | Podman (wie Docker) legt für jedes Netzwerk ein eigenes internes DNS |
| − | + | an. Jeder Container ist darin unter seinem '''container_name''' | |
| − | seinem '''container_name''' erreichbar – nicht unter <code>localhost</code> | + | erreichbar – nicht unter <code>localhost</code> und nicht über die |
| − | und nicht über die Host-IP. | + | Host-IP. |
;Merksatz | ;Merksatz | ||
| − | container_name = Hostname im internen | + | container_name = Hostname im internen Netz. Zwei Container im selben |
| − | + | Netzwerk erreichen sich gegenseitig per <code>ping container_name</code> | |
| − | <code>ping container_name</code> bzw. <code>curl http://container_name:PORT</code>. | + | bzw. <code>curl http://container_name:PORT</code>. |
| + | |||
| + | '''Wichtige Besonderheit bei getrennten Verzeichnissen:''' Jedes | ||
| + | Compose-Projekt legt normalerweise sein eigenes, isoliertes Netzwerk | ||
| + | an. Liegen uptime-kuma und caddy in unterschiedlichen Verzeichnissen | ||
| + | (= unterschiedliche Compose-Projekte), würden sie sich '''nicht''' | ||
| + | automatisch finden. Die Lösung: Das Netzwerk wird einmalig '''manuell | ||
| + | und projektübergreifend''' angelegt, und jede compose.yaml bindet sich | ||
| + | darüber als <code>external</code> ein, statt ein eigenes zu erzeugen. | ||
| − | + | ;Einmalig das gemeinsame Netzwerk anlegen | |
| − | + | *podman network create monitoring | |
| − | |||
| − | === | + | === Dienst 1: Uptime Kuma === |
Uptime Kuma braucht keinen externen Port. Es lauscht intern auf Port | Uptime Kuma braucht keinen externen Port. Es lauscht intern auf Port | ||
| Zeile 93: | Zeile 133: | ||
(SQLite-DB, Konfiguration) liegen in einem benannten Volume, damit sie | (SQLite-DB, Konfiguration) liegen in einem benannten Volume, damit sie | ||
Container-Neustarts und -Updates überleben. | Container-Neustarts und -Updates überleben. | ||
| + | |||
| + | ;Compose-Datei anlegen | ||
| + | *vim /opt/monitoring/uptime-kuma/compose.yaml | ||
<syntaxhighlight lang="yaml"> | <syntaxhighlight lang="yaml"> | ||
| + | version: "3" | ||
| + | |||
| + | services: | ||
uptime-kuma: | uptime-kuma: | ||
image: docker.io/louislam/uptime-kuma:1 | image: docker.io/louislam/uptime-kuma:1 | ||
| Zeile 102: | Zeile 148: | ||
networks: | networks: | ||
- monitoring | - monitoring | ||
| + | |||
| + | volumes: | ||
| + | uptime-kuma-data: | ||
| + | |||
| + | networks: | ||
| + | monitoring: | ||
| + | external: true | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Zeile 110: | Zeile 163: | ||
am Host-Netz, ungeschützt durch TLS). | am Host-Netz, ungeschützt durch TLS). | ||
| − | === | + | Der Eintrag <code>networks.monitoring.external: true</code> ist hier |
| + | entscheidend: er sagt podman-compose "leg kein eigenes Netz an, nutze | ||
| + | das bereits existierende <code>monitoring</code>-Netz". | ||
| + | |||
| + | === Dienst 2: Caddy (Reverse Proxy) === | ||
Caddy ist der einzige Container, der Ports nach außen (Host) öffnen | Caddy ist der einzige Container, der Ports nach außen (Host) öffnen | ||
muss, weil er der einzige Kontaktpunkt von außen ist. Er terminiert | muss, weil er der einzige Kontaktpunkt von außen ist. Er terminiert | ||
TLS mit dem vorhandenen Zertifikat und leitet intern per Klartext-HTTP | TLS mit dem vorhandenen Zertifikat und leitet intern per Klartext-HTTP | ||
| − | an | + | an die jeweiligen Backend-Dienste weiter. |
| + | |||
| + | ;Compose-Datei anlegen | ||
| + | *vim /opt/monitoring/caddy/compose.yaml | ||
<syntaxhighlight lang="yaml"> | <syntaxhighlight lang="yaml"> | ||
| + | version: "3" | ||
| + | |||
| + | services: | ||
caddy: | caddy: | ||
image: docker.io/library/caddy:2 | image: docker.io/library/caddy:2 | ||
| Zeile 131: | Zeile 194: | ||
networks: | networks: | ||
- monitoring | - monitoring | ||
| − | + | ||
| − | + | volumes: | |
| + | caddy-data: | ||
| + | |||
| + | networks: | ||
| + | monitoring: | ||
| + | external: true | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Zeile 140: | Zeile 208: | ||
passenden Kontext erhält. Bei benannten Volumes (uptime-kuma-data, | passenden Kontext erhält. Bei benannten Volumes (uptime-kuma-data, | ||
caddy-data) ist das nicht nötig, das übernimmt Podman selbst. | caddy-data) ist das nicht nötig, das übernimmt Podman selbst. | ||
| + | |||
| + | ;Hinweis depends_on | ||
| + | Da Caddy und Uptime Kuma in getrennten Compose-Projekten liegen, gibt | ||
| + | es zwischen ihnen kein <code>depends_on</code> mehr (das funktioniert | ||
| + | nur innerhalb desselben Projekts). Das ist unproblematisch: Caddy | ||
| + | versucht bei jeder Anfrage erneut, den Upstream zu erreichen, und | ||
| + | verkraftet einen kurzzeitig noch nicht gestarteten Backend-Dienst | ||
| + | klaglos. | ||
=== Caddyfile === | === Caddyfile === | ||
| Zeile 147: | Zeile 223: | ||
wird und wohin intern weitergeleitet wird. | wird und wohin intern weitergeleitet wird. | ||
| − | ; | + | ;Caddyfile anlegen |
| + | *vim /opt/monitoring/caddy/Caddyfile | ||
| + | |||
<pre> | <pre> | ||
kuma.it2XX.int { | kuma.it2XX.int { | ||
| Zeile 158: | Zeile 236: | ||
* <code>kuma.it2XX.int {</code> – für welchen Hostnamen (Host-Header) der Block gilt | * <code>kuma.it2XX.int {</code> – für welchen Hostnamen (Host-Header) der Block gilt | ||
* <code>tls ...</code> – welches Zertifikat/Key für diesen Host verwendet wird | * <code>tls ...</code> – welches Zertifikat/Key für diesen Host verwendet wird | ||
| − | * <code>reverse_proxy uptime-kuma:3001</code> – wohin die entschlüsselte Anfrage intern weitergeleitet wird (container_name als DNS-Name) | + | * <code>reverse_proxy uptime-kuma:3001</code> – wohin die entschlüsselte Anfrage intern weitergeleitet wird (container_name als DNS-Name, funktioniert nur weil beide Container im selben externen Netz <code>monitoring</code> hängen) |
| − | === Einen | + | === Einen weiteren Dienst dazugeben === |
| − | Soll z. B. HedgeDoc als | + | Soll z. B. HedgeDoc als weiterer Dienst hinzu, gilt immer dasselbe |
| − | Schema: | + | Schema – wieder ein eigenes Verzeichnis: |
| − | # | + | # Neues Verzeichnis <code>/opt/monitoring/hedgedoc/</code> mit eigener <code>compose.yaml</code> |
| − | # Container hängt im '''selben''' | + | # Container hängt über <code>external: true</code> im '''selben''' Netzwerk <code>monitoring</code> |
# Kein <code>ports:</code>-Eintrag beim Backend-Dienst – nur Caddy exponiert Ports | # Kein <code>ports:</code>-Eintrag beim Backend-Dienst – nur Caddy exponiert Ports | ||
# Eigener Host-Block in der Caddyfile, der per <code>container_name:internerPort</code> auf den Dienst zeigt | # Eigener Host-Block in der Caddyfile, der per <code>container_name:internerPort</code> auf den Dienst zeigt | ||
| − | + | ;Compose-Datei anlegen | |
| − | + | *vim /opt/monitoring/hedgedoc/compose.yaml | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
<syntaxhighlight lang="yaml"> | <syntaxhighlight lang="yaml"> | ||
| Zeile 202: | Zeile 255: | ||
services: | services: | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
hedgedoc: | hedgedoc: | ||
image: docker.io/linuxserver/hedgedoc:latest | image: docker.io/linuxserver/hedgedoc:latest | ||
container_name: hedgedoc | container_name: hedgedoc | ||
| + | environment: | ||
| + | - CMD_DOMAIN=hedgedoc.it2XX.int | ||
| + | - CMD_PROTOCOL_USESSL=true | ||
| + | - CMD_URL_ADDPORT=false | ||
volumes: | volumes: | ||
- hedgedoc-data:/config | - hedgedoc-data:/config | ||
networks: | networks: | ||
- monitoring | - monitoring | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
volumes: | volumes: | ||
| − | |||
hedgedoc-data: | hedgedoc-data: | ||
| − | |||
networks: | networks: | ||
monitoring: | monitoring: | ||
| + | external: true | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| + | |||
| + | ;Hinweis Reverse-Proxy-Umgebungsvariablen | ||
| + | HedgeDoc muss hinter einem Reverse Proxy wissen, unter welcher URL es | ||
| + | von außen erreichbar ist – sonst generiert es Links auf Assets (CSS, | ||
| + | JavaScript) mit falscher Adresse und die Seite erscheint "nackt" ohne | ||
| + | Styling: | ||
| + | * <code>CMD_DOMAIN</code> – externer Hostname (die Caddy-Domain) | ||
| + | * <code>CMD_PROTOCOL_USESSL=true</code> – Links als https:// generieren, obwohl HedgeDoc intern nur HTTP spricht (TLS terminiert Caddy) | ||
| + | * <code>CMD_URL_ADDPORT=false</code> – keinen Port an URLs hängen (443 ist Standard) | ||
| + | |||
| + | ;Caddyfile ergänzen | ||
| + | *vim /opt/monitoring/caddy/Caddyfile | ||
| + | |||
| + | <pre> | ||
| + | hedgedoc.it2XX.int { | ||
| + | tls /etc/ssl/own.crt /etc/ssl/own.key | ||
| + | reverse_proxy hedgedoc:3000 | ||
| + | } | ||
| + | </pre> | ||
| + | |||
| + | ;Merksatz | ||
| + | Ein neuer Dienst braucht vier Dinge: eigenes Verzeichnis, eigener | ||
| + | container_name, Anbindung an das externe Netz <code>monitoring</code> | ||
| + | via <code>external: true</code>, eigener Host-Block in der Caddyfile | ||
| + | mit <code>container_name:Port</code>. Der Port nach außen bleibt immer | ||
| + | nur 80/443 bei Caddy – alles andere läuft intern. | ||
=== Starten und Stoppen === | === Starten und Stoppen === | ||
| − | ;Stack starten | + | Da jeder Dienst ein eigenes Verzeichnis und Compose-Projekt ist, wird |
| − | *cd /opt/monitoring | + | auch jeder Dienst einzeln aus seinem Verzeichnis heraus gestartet. |
| − | *podman-compose up -d | + | Reihenfolge ist egal, solange das Netzwerk <code>monitoring</code> |
| + | bereits existiert. | ||
| + | |||
| + | ;Stack starten (pro Verzeichnis) | ||
| + | *cd /opt/monitoring/uptime-kuma && podman-compose up -d | ||
| + | *cd /opt/monitoring/caddy && podman-compose up -d | ||
| + | *cd /opt/monitoring/hedgedoc && podman-compose up -d | ||
;Status prüfen | ;Status prüfen | ||
| − | *podman | + | *podman ps |
| − | ; | + | ;Einzelnen Dienst stoppen |
| − | *podman-compose down | + | *cd /opt/monitoring/caddy && podman-compose down |
=== Test === | === Test === | ||
| Zeile 261: | Zeile 324: | ||
<pre> | <pre> | ||
https://kuma.it2XX.int | https://kuma.it2XX.int | ||
| − | https:// | + | https://hedgedoc.it2XX.int |
</pre> | </pre> | ||
| Zeile 267: | Zeile 330: | ||
Danach lassen sich Monitore für ns, www, ldap, client und fw anlegen | Danach lassen sich Monitore für ns, www, ldap, client und fw anlegen | ||
(Ping, TCP-Port, HTTP) und der Status live auf dem Dashboard verfolgen. | (Ping, TCP-Port, HTTP) und der Status live auf dem Dashboard verfolgen. | ||
| + | |||
| + | == Cheat Sheet: Podman und podman-compose == | ||
| + | |||
| + | Die Podman-CLI ist bewusst zur Docker-CLI kompatibel – wer Docker | ||
| + | kennt, kann fast alle Befehle 1:1 übernehmen (<code>alias | ||
| + | docker=podman</code> funktioniert tatsächlich). Die wichtigsten | ||
| + | Befehle für den Alltag: | ||
| + | |||
| + | === Container === | ||
| + | |||
| + | ;Laufende Container anzeigen | ||
| + | *podman ps | ||
| + | |||
| + | ;Alle Container anzeigen (auch gestoppte) | ||
| + | *podman ps -a | ||
| + | |||
| + | ;Logs eines Containers anzeigen | ||
| + | *podman logs caddy | ||
| + | |||
| + | ;Logs live mitverfolgen (wie tail -f) | ||
| + | *podman logs -f uptime-kuma | ||
| + | |||
| + | ;Nur die letzten 50 Zeilen | ||
| + | *podman logs --tail 50 hedgedoc | ||
| + | |||
| + | ;Shell im laufenden Container öffnen | ||
| + | *podman exec -it caddy sh | ||
| + | |||
| + | ;Einzelnen Befehl im Container ausführen | ||
| + | *podman exec caddy cat /etc/caddy/Caddyfile | ||
| + | |||
| + | ;Container neu starten | ||
| + | *podman restart caddy | ||
| + | |||
| + | ;Container stoppen / starten | ||
| + | *podman stop caddy | ||
| + | *podman start caddy | ||
| + | |||
| + | ;Details zu einem Container (IP, Mounts, Env-Variablen) | ||
| + | *podman inspect uptime-kuma | ||
| + | |||
| + | ;Ressourcenverbrauch live anzeigen | ||
| + | *podman stats | ||
| + | |||
| + | === Images === | ||
| + | |||
| + | ;Lokale Images anzeigen | ||
| + | *podman images | ||
| + | |||
| + | ;Image aktualisieren (neue Version ziehen) | ||
| + | *podman pull docker.io/louislam/uptime-kuma:1 | ||
| + | |||
| + | ;Ungenutzte Images aufräumen | ||
| + | *podman image prune | ||
| + | |||
| + | === Netzwerke und Volumes === | ||
| + | |||
| + | ;Netzwerke anzeigen | ||
| + | *podman network ls | ||
| + | |||
| + | ;Details zu einem Netzwerk (welche Container hängen drin, IPs) | ||
| + | *podman network inspect monitoring | ||
| + | |||
| + | ;Volumes anzeigen | ||
| + | *podman volume ls | ||
| + | |||
| + | ;Wo liegt ein Volume auf dem Host | ||
| + | *podman volume inspect uptime-kuma-data | ||
| + | |||
| + | === podman-compose (im jeweiligen Dienst-Verzeichnis) === | ||
| + | |||
| + | ;Stack starten (im Hintergrund) | ||
| + | *podman-compose up -d | ||
| + | |||
| + | ;Stack stoppen und Container entfernen (Volumes bleiben erhalten) | ||
| + | *podman-compose down | ||
| + | |||
| + | ;Status der Container dieses Projekts | ||
| + | *podman-compose ps | ||
| + | |||
| + | ;Logs aller Container dieses Projekts | ||
| + | *podman-compose logs | ||
| + | |||
| + | ;Logs live mitverfolgen | ||
| + | *podman-compose logs -f | ||
| + | |||
| + | ;Dienst aktualisieren (neues Image ziehen, dann neu erstellen) | ||
| + | *podman-compose pull | ||
| + | *podman-compose up -d | ||
| + | |||
| + | === Troubleshooting-Reihenfolge === | ||
| + | |||
| + | Wenn ein Dienst nicht erreichbar ist, immer in dieser Reihenfolge | ||
| + | prüfen: | ||
| + | |||
| + | # Läuft der Container überhaupt? → <code>podman ps</code> | ||
| + | # Was sagen die Logs? → <code>podman logs -f containername</code> | ||
| + | # Hängt er im richtigen Netz? → <code>podman network inspect monitoring</code> | ||
| + | # Ist der Dienst intern erreichbar? → <code>podman exec -it caddy sh</code>, dann <code>wget -O- http://uptime-kuma:3001</code> | ||
| + | # Stimmt die Caddyfile? → <code>podman exec caddy cat /etc/caddy/Caddyfile</code> | ||
| + | |||
| + | ;Merksatz | ||
| + | podman logs -f = erste Anlaufstelle bei jedem Problem | podman exec | ||
| + | -it NAME sh = ich schaue mir das Problem von innen an | podman network | ||
| + | inspect = wer hängt wirklich in welchem Netz | ||
=== Merksatz (Zusammenfassung) === | === Merksatz (Zusammenfassung) === | ||
podman-compose = ein YAML-File statt mehrerer podman-run-Befehle | | podman-compose = ein YAML-File statt mehrerer podman-run-Befehle | | ||
| − | container_name = DNS-Name im internen Netz | nur Caddy exponiert Ports | + | getrenntes Verzeichnis pro Dienst = unabhängige Compose-Projekte | |
| − | nach außen | jeder neue Dienst = | + | gemeinsames Netz muss einmalig manuell angelegt und in jeder |
| − | eigener Host-Block in der Caddyfile | + | compose.yaml als external eingebunden werden | container_name = DNS- |
| + | Name im internen Netz | nur Caddy exponiert Ports nach außen | jeder | ||
| + | neue Dienst = eigenes Verzeichnis + eigener Host-Block in der Caddyfile | ||
Aktuelle Version vom 5. Juli 2026, 11:52 Uhr
Installation
DATEN
| Parameter | Wert | Erläuterung |
|---|---|---|
| VM Name | container | Name der VM |
| Disk | 20GB | Vorgabe übernehmen |
| CPU | 4 Cores | Kerne |
| Ram | 4 GB | Speicher |
| Netzwerk (NIC) | DMZ | Interface-Zuweisung in VirtualBox |
| Admin | root | Passwort: radler |
| User | kit | Passwort: kit |
| IP | 10.88.2XX.39/24 | Statische IP |
| CIDR | 24 | Classless Inter-Domain Routing Präfixlänge |
| GW | 10.88.2XX.1 | GATEWAY |
| NS | 10.88.2XX.21 | Resolver |
| FQDN | container.it2XX.int | Fully Qualified Domain Name |
| DOM | it2XX.int | Domain Name |
Hostname
- hostnamectl hostname FQDN
Netzwerk
- nmcli con mod enp0s3 ipv4.addresses IP/CIDR
- nmcli con mod enp0s3 ipv4.gateway GW
- nmcli con mod enp0s3 ipv4.dns NS
- nmcli con mod enp0s3 ipv4.method manual
- nmcli con mod enp0s3 connection.autoconnect yes
- nmcli con up enp0s3
Nameserver & Suchdomain
Der DNS wird bereits über nmcli gesetzt. Suchdomain ergänzen:
- nmcli con mod enp0s3 ipv4.dns-search DOM
- nmcli con up enp0s3
Tools die wir haben wollen
- dnf install vim sudo git curl tcpdump nmap wget epel-release policycoreutils-python-utils tar
Kit Stamm-CA ziehen und in das System einbauen
- wget https://web.samogo.de/certs/ca.crt
- cp ca.crt /etc/pki/ca-trust/source/anchors/
- update-ca-trust extract
Zertifikat und Key holen
- wget -nv -O /tmp/own.int.tgz https://web.samogo.de/certs/it2XX.int.tgz
- tar -C /tmp -xvzf /tmp/own.int.tgz
- mv /tmp/fullchain.pem /etc/ssl/own.crt
- mv /tmp/privkey.pem /etc/ssl/own.key
Container mit Podman: Uptime Kuma, Caddy und HedgeDoc (container.it2XX.int)
Als Ausblick nach der klassischen Systemadministration geht es auf der Maschine container.it2XX.int um Container-Verwaltung mit Podman. Als praktisches Beispiel dienen drei Dienste: Uptime Kuma (Monitoring), Caddy (Reverse Proxy) und HedgeDoc (kollaborative Notizen). Jeder Dienst bekommt sein eigenes Verzeichnis mit eigener compose.yaml – das entspricht der Praxis, Container-Stacks unabhängig voneinander verwaltbar zu halten (eigenes Update, eigenes Down/Up, ohne andere Dienste zu berühren). Verwaltet werden die einzelnen Stacks über podman-compose.
Was ist Podman
Podman ist eine Container-Engine, kompatibel zur Docker-CLI, aber ohne zentralen Root-Daemon.
- Wichtigste Unterschiede zu Docker
- Daemonless: Docker braucht einen dauerhaft laufenden Root-Daemon
(dockerd). Fällt er aus oder wird kompromittiert, sind alle Container betroffen. Podman startet Container als direkte Kindprozesse ohne zentralen Daemon.
- Rootless by Default: Podman-Container laufen standardmäßig ohne
Root-Rechte. Ein Escape aus dem Container landet nicht automatisch bei Root auf dem Host.
- Systemd-Integration: Podman-Container lassen sich nativ als
systemd-Units verwalten (Quadlets), passt gut in RHEL/Rocky-Abläufe.
- Merksatz
Docker = ein Daemon regiert alle Container (meist als Root) | Podman = jeder Container ein eigener Prozess, standardmäßig ohne Root-Rechte
podman-compose installieren
- Über EPEL installieren
- dnf install -y epel-release
- dnf install -y podman-compose
Vorbereitung: Zertifikate
Die Zertifikate liegen bereits vor unter:
/etc/ssl/own.crt/etc/ssl/own.key
Verzeichnisstruktur
Jeder Container-Dienst bekommt ein eigenes Verzeichnis mit eigener
Compose-Datei. Der Dateiname ist immer compose.yaml – das ist der
aktuelle Standardname (der alte Name docker-compose.yml
funktioniert zwar auch noch, wird aber nicht mehr verwendet).
podman-compose sucht beim Aufruf automatisch nach dieser Datei im
aktuellen Arbeitsverzeichnis.
/opt/monitoring/
├── uptime-kuma/
│ └── compose.yaml
├── caddy/
│ ├── compose.yaml
│ └── Caddyfile
└── hedgedoc/
└── compose.yaml
- Struktur anlegen
- mkdir -p /opt/monitoring/{uptime-kuma,caddy,hedgedoc}
- Merksatz
Ein Verzeichnis = ein Dienst = ein eigenständiges Compose-Projekt. Jeder Dienst lässt sich einzeln starten, stoppen und aktualisieren, ohne die anderen zu beeinflussen.
Wie Container sich im Netzwerk finden: DNS über container_name
Podman (wie Docker) legt für jedes Netzwerk ein eigenes internes DNS
an. Jeder Container ist darin unter seinem container_name
erreichbar – nicht unter localhost und nicht über die
Host-IP.
- Merksatz
container_name = Hostname im internen Netz. Zwei Container im selben
Netzwerk erreichen sich gegenseitig per ping container_name
bzw. curl http://container_name:PORT.
Wichtige Besonderheit bei getrennten Verzeichnissen: Jedes
Compose-Projekt legt normalerweise sein eigenes, isoliertes Netzwerk
an. Liegen uptime-kuma und caddy in unterschiedlichen Verzeichnissen
(= unterschiedliche Compose-Projekte), würden sie sich nicht
automatisch finden. Die Lösung: Das Netzwerk wird einmalig manuell
und projektübergreifend angelegt, und jede compose.yaml bindet sich
darüber als external ein, statt ein eigenes zu erzeugen.
- Einmalig das gemeinsame Netzwerk anlegen
- podman network create monitoring
Dienst 1: Uptime Kuma
Uptime Kuma braucht keinen externen Port. Es lauscht intern auf Port 3001 und wird ausschließlich über Caddy nach außen gereicht. Daten (SQLite-DB, Konfiguration) liegen in einem benannten Volume, damit sie Container-Neustarts und -Updates überleben.
- Compose-Datei anlegen
- vim /opt/monitoring/uptime-kuma/compose.yaml
version: "3"
services:
uptime-kuma:
image: docker.io/louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- uptime-kuma-data:/app/data
networks:
- monitoring
volumes:
uptime-kuma-data:
networks:
monitoring:
external: true
- Wichtig
Kein ports:-Eintrag nötig. Ein Backend-Dienst, der nur von
Caddy angesprochen wird, muss nicht auf dem Host exponiert werden –
das wäre sogar ein unnötiges Sicherheitsrisiko (Port läge dann offen
am Host-Netz, ungeschützt durch TLS).
Der Eintrag networks.monitoring.external: true ist hier
entscheidend: er sagt podman-compose "leg kein eigenes Netz an, nutze
das bereits existierende monitoring-Netz".
Dienst 2: Caddy (Reverse Proxy)
Caddy ist der einzige Container, der Ports nach außen (Host) öffnen muss, weil er der einzige Kontaktpunkt von außen ist. Er terminiert TLS mit dem vorhandenen Zertifikat und leitet intern per Klartext-HTTP an die jeweiligen Backend-Dienste weiter.
- Compose-Datei anlegen
- vim /opt/monitoring/caddy/compose.yaml
version: "3"
services:
caddy:
image: docker.io/library/caddy:2
container_name: caddy
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:Z
- /etc/ssl/own.crt:/etc/ssl/own.crt:Z
- /etc/ssl/own.key:/etc/ssl/own.key:Z
- caddy-data:/data
networks:
- monitoring
volumes:
caddy-data:
networks:
monitoring:
external: true
- Hinweis SELinux
Das :Z an den Bind-Mounts (Caddyfile, Zertifikate) ist auf
Rocky notwendig, damit Caddy trotz aktivem SELinux Lesezugriff über den
passenden Kontext erhält. Bei benannten Volumes (uptime-kuma-data,
caddy-data) ist das nicht nötig, das übernimmt Podman selbst.
- Hinweis depends_on
Da Caddy und Uptime Kuma in getrennten Compose-Projekten liegen, gibt
es zwischen ihnen kein depends_on mehr (das funktioniert
nur innerhalb desselben Projekts). Das ist unproblematisch: Caddy
versucht bei jeder Anfrage erneut, den Upstream zu erreichen, und
verkraftet einen kurzzeitig noch nicht gestarteten Backend-Dienst
klaglos.
Caddyfile
Die Caddyfile ist Caddys eigene Konfigurationsdatei (reiner Text, kein YAML/JSON). Sie legt pro Domain fest, welches Zertifikat benutzt wird und wohin intern weitergeleitet wird.
- Caddyfile anlegen
- vim /opt/monitoring/caddy/Caddyfile
kuma.it2XX.int {
tls /etc/ssl/own.crt /etc/ssl/own.key
reverse_proxy uptime-kuma:3001
}
Aufbau:
kuma.it2XX.int {– für welchen Hostnamen (Host-Header) der Block gilttls ...– welches Zertifikat/Key für diesen Host verwendet wirdreverse_proxy uptime-kuma:3001– wohin die entschlüsselte Anfrage intern weitergeleitet wird (container_name als DNS-Name, funktioniert nur weil beide Container im selben externen Netzmonitoringhängen)
Einen weiteren Dienst dazugeben
Soll z. B. HedgeDoc als weiterer Dienst hinzu, gilt immer dasselbe Schema – wieder ein eigenes Verzeichnis:
- Neues Verzeichnis
/opt/monitoring/hedgedoc/mit eigenercompose.yaml - Container hängt über
external: trueim selben Netzwerkmonitoring - Kein
ports:-Eintrag beim Backend-Dienst – nur Caddy exponiert Ports - Eigener Host-Block in der Caddyfile, der per
container_name:internerPortauf den Dienst zeigt
- Compose-Datei anlegen
- vim /opt/monitoring/hedgedoc/compose.yaml
version: "3"
services:
hedgedoc:
image: docker.io/linuxserver/hedgedoc:latest
container_name: hedgedoc
environment:
- CMD_DOMAIN=hedgedoc.it2XX.int
- CMD_PROTOCOL_USESSL=true
- CMD_URL_ADDPORT=false
volumes:
- hedgedoc-data:/config
networks:
- monitoring
volumes:
hedgedoc-data:
networks:
monitoring:
external: true
- Hinweis Reverse-Proxy-Umgebungsvariablen
HedgeDoc muss hinter einem Reverse Proxy wissen, unter welcher URL es von außen erreichbar ist – sonst generiert es Links auf Assets (CSS, JavaScript) mit falscher Adresse und die Seite erscheint "nackt" ohne Styling:
CMD_DOMAIN– externer Hostname (die Caddy-Domain)CMD_PROTOCOL_USESSL=true– Links als https:// generieren, obwohl HedgeDoc intern nur HTTP spricht (TLS terminiert Caddy)CMD_URL_ADDPORT=false– keinen Port an URLs hängen (443 ist Standard)
- Caddyfile ergänzen
- vim /opt/monitoring/caddy/Caddyfile
hedgedoc.it2XX.int {
tls /etc/ssl/own.crt /etc/ssl/own.key
reverse_proxy hedgedoc:3000
}
- Merksatz
Ein neuer Dienst braucht vier Dinge: eigenes Verzeichnis, eigener
container_name, Anbindung an das externe Netz monitoring
via external: true, eigener Host-Block in der Caddyfile
mit container_name:Port. Der Port nach außen bleibt immer
nur 80/443 bei Caddy – alles andere läuft intern.
Starten und Stoppen
Da jeder Dienst ein eigenes Verzeichnis und Compose-Projekt ist, wird
auch jeder Dienst einzeln aus seinem Verzeichnis heraus gestartet.
Reihenfolge ist egal, solange das Netzwerk monitoring
bereits existiert.
- Stack starten (pro Verzeichnis)
- cd /opt/monitoring/uptime-kuma && podman-compose up -d
- cd /opt/monitoring/caddy && podman-compose up -d
- cd /opt/monitoring/hedgedoc && podman-compose up -d
- Status prüfen
- podman ps
- Einzelnen Dienst stoppen
- cd /opt/monitoring/caddy && podman-compose down
Test
- Aufruf im Browser
https://kuma.it2XX.int https://hedgedoc.it2XX.int
Beim ersten Aufruf fragt Uptime Kuma nach Anlegen eines Admin-Accounts. Danach lassen sich Monitore für ns, www, ldap, client und fw anlegen (Ping, TCP-Port, HTTP) und der Status live auf dem Dashboard verfolgen.
Cheat Sheet: Podman und podman-compose
Die Podman-CLI ist bewusst zur Docker-CLI kompatibel – wer Docker
kennt, kann fast alle Befehle 1:1 übernehmen (alias
docker=podman funktioniert tatsächlich). Die wichtigsten
Befehle für den Alltag:
Container
- Laufende Container anzeigen
- podman ps
- Alle Container anzeigen (auch gestoppte)
- podman ps -a
- Logs eines Containers anzeigen
- podman logs caddy
- Logs live mitverfolgen (wie tail -f)
- podman logs -f uptime-kuma
- Nur die letzten 50 Zeilen
- podman logs --tail 50 hedgedoc
- Shell im laufenden Container öffnen
- podman exec -it caddy sh
- Einzelnen Befehl im Container ausführen
- podman exec caddy cat /etc/caddy/Caddyfile
- Container neu starten
- podman restart caddy
- Container stoppen / starten
- podman stop caddy
- podman start caddy
- Details zu einem Container (IP, Mounts, Env-Variablen)
- podman inspect uptime-kuma
- Ressourcenverbrauch live anzeigen
- podman stats
Images
- Lokale Images anzeigen
- podman images
- Image aktualisieren (neue Version ziehen)
- podman pull docker.io/louislam/uptime-kuma:1
- Ungenutzte Images aufräumen
- podman image prune
Netzwerke und Volumes
- Netzwerke anzeigen
- podman network ls
- Details zu einem Netzwerk (welche Container hängen drin, IPs)
- podman network inspect monitoring
- Volumes anzeigen
- podman volume ls
- Wo liegt ein Volume auf dem Host
- podman volume inspect uptime-kuma-data
podman-compose (im jeweiligen Dienst-Verzeichnis)
- Stack starten (im Hintergrund)
- podman-compose up -d
- Stack stoppen und Container entfernen (Volumes bleiben erhalten)
- podman-compose down
- Status der Container dieses Projekts
- podman-compose ps
- Logs aller Container dieses Projekts
- podman-compose logs
- Logs live mitverfolgen
- podman-compose logs -f
- Dienst aktualisieren (neues Image ziehen, dann neu erstellen)
- podman-compose pull
- podman-compose up -d
Troubleshooting-Reihenfolge
Wenn ein Dienst nicht erreichbar ist, immer in dieser Reihenfolge prüfen:
- Läuft der Container überhaupt? →
podman ps - Was sagen die Logs? →
podman logs -f containername - Hängt er im richtigen Netz? →
podman network inspect monitoring - Ist der Dienst intern erreichbar? →
podman exec -it caddy sh, dannwget -O- http://uptime-kuma:3001 - Stimmt die Caddyfile? →
podman exec caddy cat /etc/caddy/Caddyfile
- Merksatz
podman logs -f = erste Anlaufstelle bei jedem Problem | podman exec -it NAME sh = ich schaue mir das Problem von innen an | podman network inspect = wer hängt wirklich in welchem Netz
Merksatz (Zusammenfassung)
podman-compose = ein YAML-File statt mehrerer podman-run-Befehle | getrenntes Verzeichnis pro Dienst = unabhängige Compose-Projekte | gemeinsames Netz muss einmalig manuell angelegt und in jeder compose.yaml als external eingebunden werden | container_name = DNS- Name im internen Netz | nur Caddy exponiert Ports nach außen | jeder neue Dienst = eigenes Verzeichnis + eigener Host-Block in der Caddyfile