Podman avvia ogni container come processo figlio del comando che lo lancia, senza alcun daemon di sistema in ascolto. È la differenza architetturale che lo separa da docker, dove il client dialoga con dockerd, un servizio privilegiato sempre attivo. L’ultima versione pubblicata è la 0.12.1.1, del 12 dicembre 2018, e il progetto si avvicina al rilascio 1.0.

Contesto

Il modello a daemon di Docker concentra tutto in un solo processo. Il binario docker è un client sottile che invia richieste a dockerd attraverso un socket; è dockerd a creare, sorvegliare e fermare i container. Da qui due conseguenze concrete.

La prima: dockerd gira come root, e il socket /var/run/docker.sock ne eredita di fatto il privilegio. Chi può scrivere su quel socket può avviare un container che monta la / dell’host, e ottiene così un accesso equivalente a root. La seconda: il daemon è il padre di tutti i container, e un suo riavvio o un suo crash si ripercuote sui processi che ha generato.

Podman nasce nel container-tools team di Red Hat come front-end a libpod, la libreria che gestisce il ciclo di vita di container e pod. Il codice è in Go, sotto licenza Apache 2.0. Lo strumento rispetta le specifiche dell’Open Container Initiative (OCI), le cui versioni 1.0 di runtime-spec e image-spec sono ferme da luglio 2017 e definiscono, rispettivamente, il formato di una bundle eseguibile e quello di un’immagine.

Architettura

Con podman run il binario analizza gli argomenti, prepara la configurazione OCI del container e chiama conmon. conmon è un piccolo processo in C che fa da monitor: apre i terminali pseudo-tty, registra l’exit status, gestisce il logging e resta in vita come padre del container anche dopo che il comando podman è terminato. È poi conmon a invocare il runtime OCI — runc per impostazione predefinita — che predispone namespace, cgroup e mount, ed esegue infine il processo applicativo.

La catena è quindi podmanconmonrunc → processo, senza alcun servizio centrale. Ogni container ha il proprio conmon come supervisore. Con --detach il comando podman ritorna e il processo del container resta agganciato a conmon, non a un daemon condiviso. L’integrazione con systemd diventa così diretta: si scrive una unit che lancia podman run e si affida il restart al supervisore di sistema, senza duplicarlo dentro un daemon applicativo.

La CLI ricalca quella di Docker. podman run, podman build, podman pull, podman ps, podman images accettano in larga parte gli stessi flag, e con alias docker=podman molti script girano di nuovo senza modifiche. La compatibilità è a livello di interfaccia da riga di comando, non di API: manca un socket REST equivalente a quello di Docker.

Punto critico

Il rootless è la conseguenza più interessante del modello senza daemon. Ogni comando è un normale processo dell’utente, non una richiesta inoltrata a un servizio root: un utente senza privilegi può quindi creare container nel proprio contesto. Il meccanismo si appoggia agli user namespace del kernel Linux.

All’avvio in modalità rootless, Podman si reinietta (re-exec) dentro un nuovo user namespace, dove l’utente diventa root all’interno del namespace pur restando non privilegiato sull’host. La mappatura di UID e GID usa i file /etc/subuid e /etc/subgid: a ogni utente è assegnato un intervallo di identificatori subordinati — di norma 65536 — che vengono mappati nel namespace tramite gli helper setuid newuidmap e newgidmap del pacchetto shadow-utils. Così l’UID 0 dentro al container corrisponde all’UID reale dell’utente sull’host, e gli altri UID del container agli identificatori dell’intervallo subordinato.

Il vincolo è che senza una voce in /etc/subuid e /etc/subgid la modalità rootless non parte: l’amministratore deve aver predisposto le mappature, oppure le imposta useradd alla creazione dell’utente. La versione 0.12 ha corretto una regressione del rootless introdotta nel ramo 0.11, segno che la funzionalità è ancora in fase di stabilizzazione.

Implicazioni

Il modello sposta la responsabilità della supervisione. In Docker il daemon centralizza riavvii, restart policy e raccolta dei log; con Podman quel compito passa a systemd o a un altro supervisore di processo. La logica si sposta, non sparisce: una restart policy come --restart=always ha senso solo se qualcosa, fuori da Podman, riavvia il comando.

Il concetto di pod arriva dal vocabolario di Kubernetes. podman pod create crea un gruppo di container che condividono il network namespace, e con esso lo stesso localhost e lo stesso indirizzo. Serve a riprodurre in locale la topologia di un pod prima di portarla su un cluster. Allo stesso scopo serve podman generate kube, aggiunto nella 0.12: produce il manifesto YAML di pod e service a partire da container o pod esistenti, e fa da ponte verso i file che un cluster Kubernetes legge direttamente.

Per chi costruisce immagini, lo stesso team mantiene buildah per la build di immagini OCI senza daemon e skopeo per ispezionare e copiare immagini tra registry senza un pull locale. Sono binari separati, componibili negli script, e condividono con Podman le stesse librerie di storage e di gestione immagini.

Limiti

La compatibilità con Docker si ferma alla CLI. Manca un socket compatibile con l’API Docker, quindi gli strumenti che dialogano direttamente con dockerddocker-compose compreso — non funzionano contro Podman senza un adattatore. Esiste podman-compose, un progetto di terze parti, che però non fa parte del progetto né ne condivide le garanzie.

Il rootless ha confini precisi a questa data. Il networking rootless poggia su uno stack in user space e non espone ancora tutte le funzioni del bridge privilegiato; il binding di porte sotto la 1024 resta riservato a root salvo configurazione esplicita; alcuni driver di storage richiedono permessi che un utente non privilegiato non ha. Restano poi le dipendenze esterne: senza newuidmap e newgidmap installati correttamente e senza intervalli subordinati assegnati, la modalità non si avvia.

L’integrazione con systemd è manuale: alla 0.12 non c’è un comando che generi le unit, e i file vanno scritti a mano. È un’asimmetria rispetto a Docker, dove il daemon si fa carico di quel compito al prezzo della centralizzazione che Podman evita per scelta.


https://podman.io/release/2018/12/12/podman-alpha-v0.12.1.1 https://github.com/containers/libpod https://www.opencontainers.org/ https://github.com/opencontainers/runtime-spec https://github.com/opencontainers/image-spec https://github.com/containers/conmon https://github.com/containers/buildah https://github.com/containers/skopeo https://www.noze.it/insights/podman-1-0/

Immagine di copertina: Diagramma schematico delle interfacce del kernel Linux usate dai container: blocchi etichettati libcontainer, namespaces, cgroups,… — diagramma di User:Maklaan, pubblico dominio — https://commons.wikimedia.org/wiki/File:Docker-linux-interfaces.svg