
Cos'è Docker e come funziona?
docker invia comandi tramite un'API REST al daemon dockerd, che
crea immagini ed esegue contenitori da esse.Perché esiste. Docker ti consente di creare un pacchetto di un'applicazione con tutte le sue dipendenze (librerie, configurazione, runtime) in un singolo artefatto, un'immagine, ed eseguirla in modo identico su un laptop per sviluppatori, in CI e in produzione. Questo risolve il classico problema "funziona sulla mia macchina".
Contenitori e macchine virtuali. Una VM virtualizza l'hardware e fornisce un intero sistema operativo guest con il proprio kernel. Un contenitore è un processo isolato che utilizza il kernel dell'host. Di conseguenza, i contenitori sono più leggeri, si avviano in frazioni di secondo e utilizzano meno risorse. Ma un contenitore non è una VM leggera: è legato al kernel host e il suo confine di isolamento è più debole di quello di una VM. Una VM ha un isolamento forzato dall'hardware, mentre una vulnerabilità nel kernel condiviso può potenzialmente essere elusa da un contenitore: questo è il compromesso per la velocità e la densità. L'host deve corrispondere al tipo di contenitore: i contenitori Linux necessitano di un kernel Linux, i contenitori Windows necessitano di un kernel Windows. Su macOS e Windows, Docker Desktop esegue in modo trasparente una piccola VM Linux (ad esempio tramite WSL2), quindi i contenitori Linux condividono il kernel della VM, non il kernel del tuo sistema operativo.
Architettura (client-server). Secondo la documentazione di Docker:
- Il client
dockerè il modo principale con cui gli utenti interagiscono con Docker; comandi comedocker runvengono inviati al demone. - Il demone
dockerd"fa il lavoro pesante" di creazione, esecuzione e distribuzione di contenitori e gestisce oggetti come immagini, contenitori, reti e volumi. - Il client comunica con il demone tramite un'API REST tramite un socket UNIX o un'interfaccia di rete, in modo che possano essere eseguiti su macchine diverse.
- Un registro memorizza le immagini; quello pubblico predefinito è Docker Hub.
Immagini e contenitori. Un'analogia pratica: un'immagine è come una classe (o un programma su disco) e un contenitore ne è un'istanza (un processo in esecuzione); puoi avviare molti contenitori da un'immagine.
- Un'immagine è un modello di sola lettura con istruzioni per la creazione di un contenitore, creato da livelli. È definito da un Dockerfile; quando lo modifichi, vengono ricostruiti solo i livelli modificati.
- Un contenitore è un'istanza eseguibile di un'immagine. Sopra i livelli di sola lettura dell'immagine viene aggiunto un sottile livello scrivibile (copia su scrittura); tutto ciò che è scritto lì va perso quando il contenitore viene rimosso, che è esattamente il motivo per cui i dati persistenti necessitano di volumi.
Come funziona l'isolamento (dietro le quinte). dockerd non chiama direttamente il kernel: delega la gestione del ciclo di vita del contenitore a containerd, che invoca un runtime OCI; in Docker l'impostazione predefinita è runc, ma il runtime è collegabile (crun, gVisor, Kata, ecc.). L'effettivo isolamento deriva dalle funzionalità del kernel Linux:
- I spazi dei nomi isolano le risorse (secondo la pagina man7 ci sono 8 tipi: PID, rete, mount, UTS, IPC, utente, cgroup, time). Un processo "vede" solo i propri PID, il proprio stack di rete e così via.
- I cgroups (gruppi di controllo) limitano e tengono conto dell'utilizzo delle risorse (CPU, memoria, I/O).
Follow-up comuni. I livelli di immagine vengono memorizzati nella cache; docker compose esegue app multi-contenitore; i dati persistono tramite volumi (un contenitore è temporaneo per impostazione predefinita); e OCI standardizza il formato dell'immagine (image-spec), il runtime (runtime-spec) e il protocollo di distribuzione del registro (distribution-spec): queste tre specifiche mantengono l'ecosistema interoperabile.
