Un container non è una macchina virtuale né un’entità separata dal sistema operativo. Dal punto di vista del kernel un container è costituito da uno o più processi Linux avviati sul sistema host isolati tramite namespace e limitati tramite cgroup ed eseguiti direttamente dal kernel.
Il kernel Linux vede i processi dei container allo stesso modo di qualsiasi altro processo. Nei paragrafi seguenti questo comportamento verrà mostrato con esempi pratici utilizzando Podman e Docker senza entrare ancora nei dettagli dei rispettivi strumenti.
I container sono molto leggeri perché non emulano un sistema operativo completo: condividono il kernel della macchina che li ospita e isolano solo i processi necessari. Questo li rende rapidi da avviare, spesso in pochi secondi e perfetti per microservizi o ambienti di sviluppo e test in cui serve creare e distruggere applicazioni velocemente. Queste caratteristiche possono essere riassunte in quattro punti chiave fondamentali:- isolamento: un container è un ambiente isolato dove eseguire un servizio o un'applicazione in autonomia. Non prevedono l'hypervisor come le VM ma l'esecuzione all'interno del kernel della macchina host attraverso il Docker Engine possiamo gestire ed eseguire i container.
- overhead: significa “peso aggiuntivo” o “costo extra” in termini di risorse. Nei container l’overhead è minimo perché non devono avviare un intero sistema operativo.
- avvio: secondi.
- uso tipico: microservizi, workflow dev/test. Indica il modo in cui sviluppatori e tester lavorano ogni giorno. Con i container è facile creare, provare, modificare e distruggere ambienti di sviluppo e test in pochi secondi senza sporcare il sistema e senza configurazioni complicate.
- isolamento: sistema operativo completo. Kernel della VM distinto da quello dell’host. L’hypervisor gestisce l’isolamento tra VM e host.
- overhead: significa “peso aggiuntivo” o “costo extra” in termini di risorse. Nelle macchine virtuali è maggiore perché devono caricare kernel, servizi e tutto il sistema completo.
- avvio: decine di secondi o minuti. Si riferisce al tempo necessario per far partire l’ambiente. Un container parte in pochi secondi perché non deve avviare un sistema operativo. Una macchina virtuale invece impiega molto più tempo, anche decine di secondi o minuti, proprio perché deve caricare un intero sistema operativo da zero.
- uso tipico: isolamento completo.
Docker e Podman non introducono nuovi meccanismi di isolamento: si limitano a combinare e orchestrare funzionalità già presenti nel kernel Linux. Per questo motivo, comprendere processi, chroot, namespaces e cgroups significa comprendere le fondamenta di qualsiasi container runtime.
Gli esempi riportati possono essere eseguiti in modo equivalente sia con Podman sia con Docker poiché il comportamento osservato è determinato dal kernel Linux e non dal runtime utilizzato.
Docker
Se avviamo un container che contiene ad esempio NGINX i suoi processi saranno visibili tramite i normali strumenti di sistema come ps:
frank@debian:~$ ps -fC nginx
UID PID PPID C STIME TTY TIME CMD
Se non ci sono processi NGINX in esecuzione questo comando non restituisce alcuna riga: indica che nessun processo nginx è attivo sul sistema. Avviamo ora il container sull’host utilizzando Docker:
frank@debian:~$ docker run --name serverweb -d nginx
ebea1a17a31c6d1861b903c64d692004bb5b66b904e896903c4da29e9e76f018
Dal momento che un container è un processo Linux possiamo verificarne la presenza
rieseguendo il comando sull’host:
frank@debian:~$ ps -fC nginx
UID PID PPID C STIME TTY TIME CMD
root 3035 3009 0 17:39 ? 00:00:00 nginx: master process nginx -
101 3082 3035 0 17:39 ? 00:00:00 nginx: worker process
101 3083 3035 0 17:39 ? 00:00:00 nginx: worker process
Sull’host è ora in esecuzione un’istanza del server web NGINX. Come per qualsiasi altro processo annotiamo il relativo PID 3035. Per verificare la presenza di container in esecuzione e distinguerli dai processi tradizionali possiamo utilizzare due modalità:
1 modalitàfrank@debian:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ebea1a17a31c nginx "/docker-entrypoint.…" 23 minutes ago Up 23 minutes 80/tcp serverweb
2 modalità
frank@debian:~$ ps -ef --forest
UID PID PPID C STIME TTY TIME CMD
...
root 2479 1 0 17:23 ? 00:00:05 /usr/bin/containerd
root 2592 1 0 17:23 ? 00:00:02 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/
root 3009 1 0 17:39 ? 00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id eb
root 3035 3009 0 17:39 ? 00:00:00 \_ nginx: master process nginx -g daemon off;
101 3082 3035 0 17:39 ? 00:00:00 \_ nginx: worker process
101 3083 3035 0 17:39 ? 00:00:00 \_ nginx: worker process
...
Con Docker esiste un demone centrale (dockerd): i processi del container appaiono comunque come normali processi Linux ma non sono figli diretti dell’utente e vengono avviati e mantenuti tramite dockerd, containerd e containerd-shim.
frank@debian:~$ sudo ls /proc/3035/root
[sudo] password for frank:
bin docker-entrypoint.d home media proc sbin tmp
boot docker-entrypoint.sh lib mnt root srv usr
dev etc lib64 opt run sys var
È inoltre possibile modificare i file di un container accedendo direttamente al suo filesystem tramite l’host navigando in /proc/PID/root che espone la directory radice vista dal processo in esecuzione all’interno del container. Possiamo creare un nuovo file all’interno del container tramite Docker:
frank@debian:~$ docker exec serverweb touch /file01
frank@debian:~$ docker exec serverweb ls -l
total 64
lrwxrwxrwx 1 root root 7 Nov 7 17:40 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Nov 7 17:40 boot
drwxr-xr-x 5 root root 340 Jan 3 16:39 dev
drwxr-xr-x 1 root root 4096 Dec 29 23:22 docker-entrypoint.d
-rwxr-xr-x 1 root root 1620 Dec 29 23:21 docker-entrypoint.sh
drwxr-xr-x 1 root root 4096 Jan 3 16:39 etc
-rw-r--r-- 1 root root 0 Jan 3 17:47 file01
drwxr-xr-x 2 root root 4096 Nov 7 17:40 home
lrwxrwxrwx 1 root root 7 Nov 7 17:40 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Nov 7 17:40 lib64 -> usr/lib64
drwxr-xr-x 2 root root 4096 Dec 29 00:00 media
drwxr-xr-x 2 root root 4096 Dec 29 00:00 mnt
drwxr-xr-x 2 root root 4096 Dec 29 00:00 opt
dr-xr-xr-x 178 root root 0 Jan 3 16:39 proc
drwx------ 2 root root 4096 Dec 29 00:00 root
drwxr-xr-x 1 root root 4096 Jan 3 16:39 run
lrwxrwxrwx 1 root root 8 Nov 7 17:40 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Dec 29 00:00 srv
dr-xr-xr-x 13 root root 0 Jan 3 16:39 sys
drwxrwxrwt 2 root root 4096 Dec 29 00:00 tmp
drwxr-xr-x 1 root root 4096 Dec 29 00:00 usr
drwxr-xr-x 1 root root 4096 Dec 29 00:00 var
oppure
frank@debian:~$ sudo ls -l /proc/3035/root/
total 64
lrwxrwxrwx 1 root root 7 Nov 7 18:40 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Nov 7 18:40 boot
drwxr-xr-x 5 root root 340 Jan 3 17:39 dev
drwxr-xr-x 1 root root 4096 Dec 30 00:22 docker-entrypoint.d
-rwxr-xr-x 1 root root 1620 Dec 30 00:21 docker-entrypoint.sh
drwxr-xr-x 1 root root 4096 Jan 3 17:39 etc
-rw-r--r-- 1 root root 0 Jan 3 18:47 file01
drwxr-xr-x 2 root root 4096 Nov 7 18:40 home
lrwxrwxrwx 1 root root 7 Nov 7 18:40 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Nov 7 18:40 lib64 -> usr/lib64
drwxr-xr-x 2 root root 4096 Dec 29 01:00 media
drwxr-xr-x 2 root root 4096 Dec 29 01:00 mnt
drwxr-xr-x 2 root root 4096 Dec 29 01:00 opt
dr-xr-xr-x 178 root root 0 Jan 3 17:39 proc
drwx------ 2 root root 4096 Dec 29 01:00 root
drwxr-xr-x 1 root root 4096 Jan 3 17:39 run
lrwxrwxrwx 1 root root 8 Nov 7 18:40 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Dec 29 01:00 srv
dr-xr-xr-x 13 root root 0 Jan 3 18:47 sys
drwxrwxrwt 2 root root 4096 Dec 29 01:00 tmp
drwxr-xr-x 1 root root 4096 Dec 29 01:00 usr
drwxr-xr-x 1 root root 4096 Dec 29 01:00 var
Poiché un container è visto dal kernel come un normale processo Linux è possibile terminarlo utilizzando il comando kill sul suo PID causando l’arresto immediato del container.
frank@debian:~$ sudo kill 3035
frank@debian:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ebea1a17a31c nginx "/docker-entrypoint.…" About an hour ago Exited (0) 27 seconds ago serverweb
Il container risulta fermo ma non viene eliminato: Docker continua infatti a mostrarlo tra i container esistenti nonostante STATUS Exited:
Il processo non esiste più e quindi la directory /proc/PID
non è più accessibile:
frank@debian:~$ cd /proc/3035
-bash: cd: /proc/3035: No such file or directory
Tentare di entrare nel container con docker exec fallisce
perché il container non è in stato Running:
frank@debian:~$ docker exec serverweb ls /
Error response from daemon: container ebea1a17a31c6d1861b903c64d692004bb5b66b904e896903c4da29e9e76f018 is not running
Anche se il processo è terminato, il container continua a esistere come oggetto gestito da Docker. Per questo motivo il nome resta occupato e non può essere riutilizzato:
frank@debian:~$ docker run --name serverweb -d nginx
docker: Error response from daemon: Conflict. The container name "/serverweb" is already in use by container "ebea1a17a31c6d1861b903c64d692004bb5b66b904e896903c4da29e9e76f018". You have to remove (or rename) that container to be able to reuse that name.
Run 'docker run --help' for more information
Questo dimostra che:
- un processo scompare quando termina
- un container resta definito finché non viene rimosso esplicitamente
frank@debian:~$ docker rm serverweb
Avviamo ora nuovamente il container sull’host utilizzando Docker:
frank@debian:~$ docker run --name serverweb -d nginx
7756e3022f69008867ab93684f4c87db97b600a02f05c84e52988cfbfc1273d8
Un container è semplicemente un processo Linux: chi ha accesso all’host può
vederne i processi, analizzarne il comportamento e ispezionare informazioni come
le variabili d’ambiente tramite /proc/PID/environ. Anche se questi strumenti non sono container-aware restano fondamentali per il debug, l’analisi delle
performance e la sicurezza.
Poiché un container è visto dal kernel come un normale processo Linux la sua gestione avviene attraverso operazioni ben definite sul suo ciclo di vita. Docker fornisce comandi dedicati per arrestare, terminare o rimuovere un container, ognuno con un significato preciso.
Docker mette a disposizione diversi comandi per gestire il ciclo di vita di un container. Ogni comando ha uno scopo preciso ed è importante usarlo nel contesto corretto.
Stop pulito (consigliato): il comandodocker stop invia un segnale di terminazione al processo principale del container permettendogli di chiudersi correttamente.
frank@debian:~$ docker stop serverweb
Stop forzato: il comando docker kill termina immediatamente il processo del container. È l’equivalente di un kill -9 e va usato solo in caso di necessità.
frank@debian:~$ docker kill serverweb
Rimozione del container: un container fermo continua a esistere come oggetto gestito da Docker. Per eliminarlo definitivamente è necessario utilizzare docker rm, operazione possibile solo dopo lo stop.
frank@debian:~$ docker rm serverweb
PODMAN
Se avviamo un container che contiene ad esempio NGINX i suoi processi saranno visibili tramite i normali strumenti di sistema come ps:
frank@fedora:~$ ps -fC nginx
UID PID PPID C STIME TTY TIME CMD
Se non ci sono processi NGINX in esecuzione questo comando non restituisce alcuna riga: indica che nessun processo nginx è attivo sul sistema. Avviamo ora il container sull’host utilizzando Podman:
frank@fedora:~$ podman run --name serverweb -d nginx
eae419c458a3fd713c9e3e29352db188c92391c88325cbcb2f3be9fd72c0f745
Dal momento che un container è un processo Linux possiamo verificarne la presenza
rieseguendo il comando sull’host:
frank@fedora:~$ ps -fC nginx
UID PID PPID C STIME TTY TIME CMD
frank 3886 3884 0 11:51 ? 00:00:00 nginx: master process nginx -g daemon off;
524388 3914 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3915 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3916 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3917 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3918 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3919 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3920 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3921 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3922 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3923 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3924 3886 0 11:51 ? 00:00:00 nginx: worker process
524388 3925 3886 0 11:51 ? 00:00:00 nginx: worker process
Sull’host è ora in esecuzione un’istanza del server web NGINX. Come per qualsiasi altro processo annotiamo il relativo PID 3886. Per verificare la presenza di container in esecuzione e distinguerli dai processi tradizionali possiamo utilizzare due modalità:
1 modalitàfrank@fedora:~$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eae419c458a3 docker.io/library/nginx:latest nginx -g daemon o... 3 minutes ago Up 3 minutes 80/tcp serverweb
2 modalità
frank@fedora:~$ ps -ef --forest
UID PID PPID C STIME TTY TIME CMD
...
frank 3886 3884 0 11:51 ? 00:00:00 \_ nginx: master process nginx -g daemon off;
524388 3914 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3915 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3916 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3917 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3918 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3919 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3920 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3921 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3922 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3923 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3924 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
524388 3925 3886 0 11:51 ? 00:00:00 \_ nginx: worker process
...
Con Podman non esiste un demone centrale come in Docker: i processi del container appaiono come normali processi Linux, figli diretti dell’utente o del processo Podman che li ha avviati.
Il filesystem /proc contiene le informazioni su tutti i processi in esecuzione sull’host. Ogni directory numerica rappresenta un PID e fornisce una vista dettagliata dello stato del processo. È possibile ispezionare il filesystem visto dal container accedendo alla directory:frank@fedora:~$ sudo ls /proc/3886/root
[sudo] password di frank:
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
È inoltre possibile modificare i file di un container accedendo direttamente al suo filesystem tramite l’host navigando in /proc/PID/root che espone la directory radice vista dal processo in esecuzione all’interno del container. Possiamo creare un nuovo file all’interno del container tramite Podman:
frank@fedora:~$ podman exec serverweb touch /file01
frank@fedora:~$ podman exec serverweb ls -l /
oppure
frank@fedora:~$ sudo ls -l /proc/3886/root/
Poiché un container è visto dal kernel come un normale processo Linux è possibile terminarlo utilizzando il comando kill sul suo PID causando l’arresto immediato del container.
frank@fedora:~$ sudo kill 3886
frank@fedora:~$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eae419c458a3 docker.io/library/nginx:latest nginx -g daemon o... 11 minutes ago Exited (0) 44 seconds ago 80/tcp serverweb
Il container risulta fermo ma non viene eliminato: Podman continua infatti a mostrarlo tra i container esistenti nonostante STATUS Exited:
Il processo non esiste più e quindi la directory /proc/PID
non è più accessibile:
frank@fedora:~$ cd /proc/3886
-bash: cd: /proc/3886: File o directory non esistente
Tentare di entrare nel container con podman exec fallisce
perché il container non è in stato Running:
frank@fedora:~$ podman exec serverweb ls /
Error: can only create exec sessions on running containers: container state improper
Anche se il processo è terminato, il container continua a esistere come oggetto gestito da Podman. Per questo motivo il nome resta occupato e non può essere riutilizzato:
frank@fedora:~$ podman run --name serverweb -d nginx
Error: creating container storage: the container name "serverweb" is already in use by eae419c458a3fd713c9e3e29352db188c92391c88325cbcb2f3be9fd72c0f745. You have to remove that container to be able to reuse that name: that name is already in use, or use --replace to instruct Podman to do so.
Questo dimostra che:
- un processo scompare quando termina
- un container resta definito finché non viene rimosso esplicitamente
frank@fedora:~$ podman rm serverweb
oppure sovrascriverlo con l’opzione --replace:
frank@fedora:~$ podman run --replace --name serverweb -d nginx
Avviamo ora nuovamente il container sull’host utilizzando Podman:
frank@fedora:~$ podman run --name serverweb -d nginx
ff701de5c37316135b42d30e68c293470fa0e89791598a6198673b02ecd6b119
Un container è semplicemente un processo Linux, chi ha accesso all’host può vederne i processi, leggere variabili d’ambiente sensibili tramite /proc/PID/environ, e analizzarne il comportamento con i classici strumenti di sistema ed anche se questi strumenti non sono container-aware restano fondamentali per debug, analisi delle performance e sicurezza.
Poiché un container è visto dal kernel come un normale processo Linux la sua gestione avviene attraverso operazioni ben definite sul suo ciclo di vita. Podman fornisce comandi dedicati per arrestare, terminare o rimuovere un container, ognuno con un significato preciso.
Podman mette a disposizione diversi comandi per gestire il ciclo di vita di un container. Ogni comando ha uno scopo preciso ed è importante usarlo nel contesto corretto.
Stop pulito (consigliato): il comandopodman stop invia un segnale di terminazione al processo principale del container permettendogli di chiudersi correttamente.
frank@fedora:~$ podman stop serverweb
Stop forzato: il comando podman kill termina immediatamente il processo del container. È l’equivalente di un kill -9 e va usato solo in caso di necessità.
frank@fedora:~$ podman kill serverweb
Rimozione del container: un container fermo continua a esistere come oggetto gestito da Podman. Per eliminarlo definitivamente è necessario utilizzare podman rm, operazione possibile solo dopo lo stop.
frank@fedora:~$ podman rm serverweb


