contesto
Serviva un servizio interno di time-tracking self-hosted per il team.
La macchina target — mauden-services (10.1.1.117) su Rocky
Linux — era già adibita ad altri servizi containerizzati, quindi la
scelta naturale era Podman: rootless, daemonless,
allineato alla linea RHEL.
Requisiti non negoziabili: database isolato dalla rete aziendale, solo la porta applicativa esposta, riavvio automatico dei container al boot della VM, nessuna credenziale hardcoded nei file di composizione.
approccio
-
Due container con Podman Compose:
sqldb(MySQL 8.3) senza port mapping verso l'host, ekimai(immaginekimai2:apache) con0.0.0.0:8001:8001. Comunicazione interna tramite la rete privata creata automaticamente da Compose — MySQL è raggiungibile solo come hostsqldbdall'app. -
Credenziali in
.env: un file separato (chmod 600) conDATABASE_*eADMIN_*, referenziato nel compose via${VARIABILE}. Zero password in chiaro nel file YAML committabile. -
Volumi persistenti:
mysqlper/var/lib/mysql,dataper/opt/kimai/var/data,pluginsper/opt/kimai/var/plugins. Tutti i bind con suffisso:z— obbligatorio su RHEL per consentire a SELinux di relabel-are il contenuto con il contesto del container. -
Healthcheck + dependency:
sqldbespone un healthcheck basato sumysqladmin ping, e Kimai dichiaradepends_on.sqldb.condition: service_healthy: all'avvio freddo il DB inizializza per 20–40s e Kimai attende automaticamente invece di crashare sul primo connect. -
Registry espliciti: Podman non assume Docker Hub
come default, quindi le immagini vanno qualificate con
docker.io/library/mysql:8.3edocker.io/kimai/kimai2:apacheper evitare pull da registry sbagliati o fallimenti silenziosi. -
Servizio
systemd --user: unit in~/.config/systemd/user/kimai.servicedi tipooneshotconRemainAfterExit=yes, che invocapodman-compose up -dallo start edownallo stop. Path assoluto (/home/maudadmin/.local/bin/podman-compose) perché systemd non espande la tilde. -
loginctl enable-linger maudadmin: senza linger i servizi--usermuoiono al logout. Con linger la sessione utente resta attiva anche quando nessuno è loggato, e il servizio parte al boot. -
firewalld — zona giusta:
firewall-cmd --get-active-zonesha rivelato una zona customportainerche cattura il traffico dalle subnet 10.1.1.0/24 e 10.1.4.0/24. La porta 8001 va aperta su quella zona, non supublic.
outcome
- Kimai raggiungibile stabilmente su
http://10.1.1.117:8001dalle subnet aziendali. - MySQL isolato: nessun port mapping verso l'host, raggiungibile solo dalla rete Compose interna.
- Riavvio automatico al boot tramite systemd utente + linger — testato con
rebootdella VM. - Zero credenziali nei file versionabili: tutto nel
.envcon permessi 600. systemctl --user status kimai.service→active (exited),status=0/SUCCESS.
constraints
Podman è daemonless. A differenza di Docker non c'è un processo sempre attivo che riavvia i container: la persistenza deve passare da un meccanismo esterno (systemd, cronjob @reboot, ecc.). Questo è potenza, non limite — ma obbliga a progettare il life-cycle esplicitamente.
SELinux in enforcing su RHEL. Volumi senza
:z (o :Z) generano permission denied
silenziosi dentro al container, mentre l'host vede i file
perfettamente. Il :z applica un relabel condiviso,
:Z un relabel privato.
podman generate systemd è deprecato
nelle versioni recenti, e in ogni caso non funziona con
podman-compose: il comando lavora solo su container/pod
nominati, non su stack. La strada pulita oggi è o il nuovo
Quadlet (.container / .kube units) o —
come qui — un unit oneshot che wrappa
podman-compose.
problemi riscontrati
-
Interfaccia web irraggiungibile dal client 10.1.4.x
nonostante
firewall-cmdmostrasse 8001/tcp aperta. Diagnosi:Test-NetConnectionda PowerShell →PingSucceeded=True,TcpTestSucceeded=False. Causa: la porta era aperta sulla zonapublic, ma il traffico da 10.1.4.0/24 finiva nella zona customportainer. Fix:firewall-cmd --zone=portainer --add-port=8001/tcp --permanent && firewall-cmd --reload. -
systemctl --userfallisce al primo avvio con control process exited with error.which podman-compose→~/.local/bin/podman-compose. Il mio unit file usava la tilde. Systemd non la espande — fix: path assoluto/home/maudadmin/.local/bin/podman-compose. -
podman generate systemd --name kimai_defaultrestituiva does not refer to a container or pod: i nomi reali dei container eranokimai_sqldb_1ekimai_kimai_1, nonkimai_default. A prescindere, il comando è deprecato: soluzione definitiva con unitoneshotcustom.
lezioni
- Su RHEL con firewalld le zone contano più delle porte. Prima di debuggare al livello applicativo,
firewall-cmd --get-active-zones. - Il
:zSELinux sui volumi Podman è il tipo di dettaglio che non rompe mai subito, solo appena vai in produzione. Vale la pena metterlo sempre. - Systemd non espande
~: path assoluti ovunque, testati consystemctl --user statusprima di dichiarare vittoria. - Senza
loginctl enable-lingeri servizi utente sono un'illusione: funzionano finché sei loggato, poi muoiono silenziosamente. - Healthcheck +
depends_onevitano il classico problema cold-start in cui l'app prova a connettersi a un DB ancora in initializing database system.