Nell’ecosistema dei container, spesso nasce confusione quando gli sviluppatori confrontano Docker (una piattaforma completa) con containerd, che in realtà è un componente runtime mirato. È come paragonare un’automobile completa al suo motore: entrambi sono essenziali, ma servono scopi diversi a livelli di astrazione differenti.
In questa guida chiarirò il rapporto tra queste due tecnologie, esplorandone le architetture, l’integrazione con Kubernetes e gli scenari specifici in cui ciascuno strumento eccelle. Che tu stia creando container in locale o gestendo cluster Kubernetes in produzione, capire quando usare Docker rispetto a containerd può incidere in modo significativo sulle decisioni relative all’infrastruttura.
Alla fine di questo articolo avrai un’idea chiara di come Docker e containerd si completino a vicenda e di come scegliere lo strumento giusto per le tue esigenze specifiche, che si tratti di sviluppo locale rapido o di distribuzioni in produzione su larga scala.
Se sei alle prime armi con la containerizzazione, ti consiglio vivamente di seguire il nostro corso Containerization and Virtualization Concepts.
Che cos’è Docker?
Docker ha rivoluzionato lo sviluppo software rendendo la containerizzazione accessibile e pratica per gli sviluppatori di tutti i giorni. In quanto piattaforma completa, Docker offre tutto il necessario per creare, distribuire ed eseguire applicazioni containerizzate.
Per chi inizia, questa Guida pratica ai container fornisce un’ottima introduzione a Docker e ai container.
Docker funziona come una piattaforma all-in-one per i container, offrendo una suite di strumenti integrata che copre gran parte del ciclo di vita dei container. Al suo core, Docker impacchetta le applicazioni con tutte le loro dipendenze in immagini portabili e autosufficienti che possono essere eseguite in modo coerente in qualsiasi ambiente, dal laptop di uno sviluppatore ai server di produzione.

Questo approccio completo ha reso Docker lo standard di settore per lo sviluppo locale e per l’esperienza degli sviluppatori. Tra i principali vantaggi:
- Ecosistema esteso: Docker Hub ospita milioni di immagini preconfezionate, dai database ai web server, eliminando procedure di installazione complesse
- Portabilità universale: Un container creato su macOS gira in modo identico su Linux o Windows, a patto che Docker sia installato
- Focus sugli sviluppatori: Astrazione delle differenze infrastrutturali, così puoi concentrarti sull’applicazione invece che sulle complessità di deployment
- Onboarding rapido: I nuovi membri del team possono avviare ambienti di sviluppo in minuti invece che in ore
Componenti chiave e flusso di lavoro
L’architettura di Docker è composta da vari componenti interconnessi che lavorano in modo fluido.
La Docker CLI fornisce l’interfaccia lato utente con cui gli sviluppatori impartiscono i comandi. Quando esegui un comando come docker run, la CLI comunica con il demone Docker (dockerd), che è il servizio principale che gestisce container, immagini, reti e volumi.
Per gli utenti Windows e macOS, Docker Desktop offre un ulteriore livello di praticità con un’interfaccia grafica, rendendo la gestione dei container accessibile anche a chi è meno a suo agio con la riga di comando. Questo ambiente integrato include tutto ciò che serve per lo sviluppo locale, dal supporto a Kubernetes alla gestione dei volumi.
Il flusso di lavoro tipico segue uno schema chiaro:
- Gli sviluppatori creano immagini usando i Dockerfile
- Le caricano su registry come Docker Hub
- Infine, le immagini vengono eseguite come container su qualsiasi host con Docker abilitato
Ciò che molti sviluppatori non sanno è che Docker non esegue i container direttamente. Invece, dockerd delega l’esecuzione effettiva a containerd, che gira in background come processo separato.

Questo design modulare, in cui Docker usa containerd per le operazioni di basso livello, consente a ciascun componente di concentrarsi su ciò che sa fare meglio: Docker offre l’interfaccia e l’ecosistema a misura di sviluppatore, mentre containerd gestisce i dettagli più tecnici dell’esecuzione dei container.
Che tu stia iniziando ora con Docker o voglia fare un passo avanti, dai un’occhiata alle nostre 10 idee per progetti Docker per tutti i livelli.
Che cos’è containerd?
Dopo aver visto l’approccio di piattaforma completa di Docker, esaminiamo ora containerd, il runtime specializzato che alimenta l’esecuzione dei container sia all’interno di Docker sia nel più ampio ecosistema.
Un runtime per container standard di settore
Containerd è un runtime core per container leggero e standard di settore che ha conseguito la graduation presso la Cloud Native Computing Foundation (CNCF), a testimonianza di maturità, affidabilità e adozione diffusa. Originariamente parte di Docker, containerd è stato estratto nel 2017 e donato alla CNCF per favorire un’adozione più ampia nell’ecosistema.

A differenza dell’ampia gamma di funzionalità di Docker, l’ambito di containerd è volutamente limitato: gestisce le operazioni fondamentali del ciclo di vita dei container come avvio, arresto, pausa ed eliminazione, e si occupa del trasferimento e dell’archiviazione delle immagini. Questo focus mirato rende containerd estremamente stabile ed efficiente, qualità essenziali per gli ambienti di produzione.
Pensa a containerd come alla “tubatura”: un’infrastruttura progettata per essere integrata in sistemi più grandi piuttosto che usata direttamente dalle persone. Piattaforme importanti come Kubernetes, AWS Fargate e Google Kubernetes Engine fanno affidamento su containerd per eseguire i container, anche se gli utenti interagiscono con queste piattaforme tramite le loro interfacce.
Architettura e design
Per capire perché containerd è così ampiamente adottato nei sistemi di produzione, dobbiamo esaminarne i principi architetturali.
L’architettura di containerd esemplifica i principi del design modulare. Costruito attorno agli standard della Open Container Initiative (OCI), containerd garantisce compatibilità in tutto l’ecosistema dei container. Ciò significa che qualsiasi immagine conforme a OCI funzionerà con containerd, indipendentemente dallo strumento con cui è stata creata.
L’architettura è composta da alcuni livelli chiave:
- Strato API gRPC: offre un modello client-server in cui più client (Docker, Kubernetes, strumenti personalizzati) comunicano con un’unica istanza di containerd
- Demone containerd: mantiene lo stato dei container in esecuzione, gestisce l’archiviazione delle immagini tramite gli snapshotter e coordina il networking tramite plugin
- Runtime runc: un runtime leggero, conforme a OCI, che interagisce direttamente con le funzionalità del kernel Linux, creando namespace, configurando cgroup e avviando i processi dei container
- Plugin modulari: snapshotter personalizzati per storage specializzati, runtime alternativi (gVisor, Kata Containers) e plugin di rete possono essere integrati senza modificare containerd
Quando containerd deve avviare un container, genera runc, che esegue il lavoro effettivo di creare namespace isolati, impostare i cgroup per i limiti di risorse e avviare il processo del container.
Questa separazione delle responsabilità rende containerd altamente estensibile. Le organizzazioni possono personalizzare quasi ogni aspetto senza toccare il codice core di containerd.
Differenze chiave tra containerd e Docker
Con una solida comprensione del ruolo di ciascuno strumento, vediamo come differiscono nella pratica, dall’architettura ai modelli di integrazione.
La differenza fondamentale sta nell’ambito e nello scopo.
Docker usa un’architettura con demone centralizzato in cui dockerd coordina molti aspetti diversi, come:
- Build delle immagini
- Esecuzione dei container
- Networking
- Gestione dei volumi
Questo design all-in-one semplifica l’esperienza degli sviluppatori ma introduce overhead attraverso più livelli di astrazione.
Containerd, al contrario, si concentra in modo ristretto sull’esecuzione dei container. Non include funzionalità di build delle immagini, di orchestrazione o un’interfaccia grafica. Per il networking, Docker include Libnetwork nel suo demone, mentre containerd si affida a plugin esterni CNI (Container Network Interface) intercambiabili in base alle esigenze.
Questa differenza architetturale ha implicazioni sulle prestazioni. In ambienti ad alta dinamicità, in cui i container vengono avviati e arrestati di frequente, come nei cluster Kubernetes con autoscaling, il design snello di containerd può portare a tempi di avvio più rapidi e a un consumo di risorse inferiore. L’overhead ridotto significa meno memoria e CPU utilizzate, un vantaggio significativo su larga scala.
Integrazione con Kubernetes
Parlando di Kubernetes, il rapporto tra i runtime dei container e le piattaforme di orchestrazione si è evoluto molto, in particolare per come Kubernetes si collega a containerd.
Il rapporto tra Kubernetes e i runtime dei container ha subìto un cambiamento importante con Kubernetes 1.24, rilasciato nel 2022. Questa versione ha rimosso il "Dockershim", uno strato di compatibilità che permetteva a Kubernetes di usare Docker come runtime dei container.
Dockershim è sempre stato pensato come ponte temporaneo. Quando Kubernetes ha introdotto la Container Runtime Interface (CRI) per standardizzare la comunicazione con i runtime, Docker non poteva implementarla direttamente perché precedente alla progettazione di CRI. Dockershim traduceva tra Kubernetes e Docker, aggiungendo uno strato di traduzione alla fine non necessario.
Le versioni moderne di Kubernetes comunicano direttamente con containerd tramite CRI, eliminando del tutto lo strato di traduzione Docker. Questa semplificazione porta benefici concreti:
- Latenza ridotta: la comunicazione diretta elimina l’overhead di traduzione nelle operazioni sui container
- Maggior stabilità: meno componenti in gioco significa meno potenziali punti di errore
- Migliori prestazioni: lo stack runtime snellito avvantaggia in particolare le distribuzioni su larga scala
- Debug semplificato: l’integrazione CRI diretta rende la risoluzione dei problemi più lineare
Per gli utenti Kubernetes, questo cambiamento è in gran parte trasparente. Le immagini Docker restano pienamente compatibili perché aderenti agli standard OCI. L’impatto pratico è che i cluster Kubernetes in produzione ora funzionano in modo più efficiente usando direttamente containerd, mentre gli sviluppatori possono continuare a usare Docker in locale per build e test.
Se non ti è chiaro pro e contro dell’uso di Kubernetes in primo luogo, dai un’occhiata a questo confronto su Docker Compose vs Kubernetes.
Creazione e gestione delle immagini
Sebbene l’integrazione del runtime sia cruciale per l’orchestrazione, il flusso di lavoro degli sviluppatori dipende molto da come ciascuno strumento gestisce la creazione e l’archiviazione delle immagini. La build delle immagini rappresenta un divario di capacità significativo tra Docker e containerd.
Docker offre un sistema di build integrato tramite Dockerfile e BuildKit, che consente di costruire build multi-stage complesse con caching, parallelizzazione e funzionalità avanzate come secret di build e forwarding dell’agente SSH.
Containerd, per progettazione, non include alcun flusso di lavoro nativo per la build delle immagini. Per creare immagini con containerd, gli sviluppatori devono usare strumenti esterni. Le opzioni includono l’esecuzione di BuildKit come demone separato e l’uso di buildctl per build da riga di comando, oppure l’adozione di nerdctl, una CLI compatibile con Docker che integra BuildKit.
Anche i meccanismi di storage differiscono per filosofia. La gestione dei volumi di Docker fornisce un’astrazione naturale per gli sviluppatori, con volumi nominati che persistono i dati indipendentemente dal ciclo di vita dei container. Containerd usa un sistema di snapshotter più a basso livello, in cui diversi driver di snapshot possono essere collegati per gestire i filesystem a livelli in modo diverso in base ai requisiti di storage sottostante.
Questa differenza riflette i pubblici di riferimento degli strumenti:
- Docker ottimizza la produttività degli sviluppatori con pratiche funzioni integrate
- Containerd fornisce primitive flessibili che i builder di piattaforme possono assemblare in base alle loro esigenze specifiche
CLI, esperienza sviluppatore e nerdctl
Oltre ad architettura e funzionalità, l’esperienza quotidiana degli sviluppatori è plasmata soprattutto dall’interfaccia a riga di comando che ciascuno strumento offre. L’esperienza da terminale rivela con maggiore chiarezza le diverse filosofie di design.
La CLI di Docker è nota per la sua facilità d’uso. Comandi come docker run, docker build e docker logs sono intuitivi, ben documentati e progettati per le persone. La CLI include default utili, messaggi di errore chiari e opzioni estese che coprono la maggior parte dei casi d’uso.
Containerd include ctr, una CLI minimale pensata esclusivamente per il debugging e il testing delle funzionalità di basso livello di containerd. Lo strumento ctr è volutamente poco adatto agli sviluppatori applicativi. Manca di funzionalità comuni come scorciatoie per il port mapping, policy di riavvio automatico e integrazione con i gestori di credenziali. È progettato per gli sviluppatori di containerd, non per chi sviluppa applicazioni.
Colmare il divario con nerdctl
Questo gap di usabilità ha portato alla creazione di nerdctl, una CLI compatibile con Docker per containerd. Usare nerdctl è identico a usare Docker: stessa sintassi dei comandi, stesse flag, stesso flusso di lavoro, ma con containerd come runtime sottostante. Questo rende nerdctl un ottimo ponte per i team che passano da Docker a containerd negli ambienti di sviluppo.
Nella pratica, gli sviluppatori interagiscono raramente direttamente con containerd. Quando serve, nerdctl fornisce l’interfaccia familiare che ci si aspetta, mentre gli operatori di piattaforma e gli amministratori usano le API di containerd in modo programmatico tramite sistemi di orchestrazione come Kubernetes.
Per illustrare le differenze nella pratica, ecco come si confrontano le comuni operazioni sui container tra i tre strumenti da riga di comando:
|
Task
|
Docker
|
nerdctl
|
ctr
|
|
Eseguire un container
|
docker run -d -p 8080:80 nginx
|
nerdctl run -d -p 8080:80 nginx
|
ctr run --net-host -d docker.io/library/nginx:latest nginx_id
|
|
Elencare i container
|
docker ps
|
nerdctl ps
|
ctr tasks list
|
|
Creare un’immagine
|
docker build -t myapp .
|
nerdctl build -t myapp .
|
Non supportato
|
|
Visualizzare i log
|
docker logs <container>
|
nerdctl logs <container>
|
Non supportato
|
|
Ispezionare un container
|
docker inspect <container>
|
nerdctl inspect <container>
|
ctr containers info <container>
|
|
Scaricare un’immagine
|
docker pull nginx
|
nerdctl pull nginx
|
ctr images pull docker.io/library/nginx:latest
|
|
Supporto Compose
|
docker compose up
|
nerdctl compose up
|
Non supportato
|
Nota: ctr non supporta il port mapping (-p) e richiede l’host networking (--net-host) per esporre i servizi. Inoltre non scarica automaticamente le immagini.
Panoramica delle differenze chiave
Prima di passare ai consigli per casi d’uso specifici, ricapitoliamo le differenze trattate finora. La tabella seguente riassume le principali differenze funzionali tra Docker e containerd, evidenziandone le capacità e i destinatari:
|
Feature
|
Docker
|
Containerd
|
|
Image Building
|
Integrato (Dockerfile, BuildKit)
|
Richiede strumenti esterni (buildctl, nerdctl)
|
|
Orchestrazione
|
Docker Swarm / Kubernetes
|
Nessuna (usato da Kubernetes)
|
|
Gestione storage
|
Gestione dei volumi
|
Sistema di snapshotter
|
|
Interfaccia grafica
|
Docker Desktop
|
Nessuna
|
|
Ciclo di vita dei container
|
Gestione completa (tramite containerd)
|
Focus core (compatibile CRI)
|
|
Utenti principali
|
Sviluppatori di applicazioni
|
Operatori di cluster, Platform builder
|
Perché scegliere Docker?
Ora che abbiamo coperto le differenze tecniche, vediamo gli scenari pratici in cui ciascuno strumento eccelle. Nonostante la diffusione di containerd negli ambienti di produzione, Docker resta la scelta migliore in contesti specifici in cui contano soprattutto l’esperienza sviluppatore e gli strumenti completi.
Per sviluppo locale e prototipazione
Docker eccelle quando ti serve una soluzione “tutto in uno” per scrivere e testare codice. La toolchain integrata consente di passare da zero a container in esecuzione in pochi minuti, senza assemblare più componenti o configurare networking complessi.
L’ecosistema Docker offre enormi benefici di produttività:
- Docker Hub: milioni di immagini pronte all’uso per database, code di messaggi e web server
- Docker Compose: definisci applicazioni multi-container in un unico file YAML e avvia interi ambienti di sviluppo con un solo comando
- GUI di Docker Desktop: gestione visuale dei container, esplorazione dei volumi, regolazione dei limiti di risorse e supporto Kubernetes integrato
- Soglia d’ingresso più bassa: strumenti grafici e comandi intuitivi rendono i container accessibili anche a chi è nuovo alla tecnologia

Per i team che usano Docker Desktop, la GUI offre praticità aggiuntive che accelerano sensibilmente l’onboarding e il lavoro quotidiano.
Per pipeline di build complesse
Oltre allo sviluppo, le capacità di build di Docker lo rendono la scelta naturale per workflow di integrazione e distribuzione continua sofisticati.
BuildKit integrato in Docker offre funzionalità avanzate essenziali per pipeline CI/CD moderne. Le build multi-stage riducono al minimo le dimensioni delle immagini mantenendo leggibili i Dockerfile. I meccanismi di caching di BuildKit riutilizzano in modo intelligente i layer tra build, riducendo drasticamente i tempi nelle pipeline di integrazione continua.
La maggior parte delle piattaforme di automazione, come GitHub Actions, GitLab CI, Jenkins e altre, hanno integrazioni Docker mature e collaudate. Queste integrazioni gestiscono senza attriti autenticazione, caching e pubblicazione delle immagini.
Sebbene altri strumenti possano ottenere risultati simili, l’ubiquità di Docker significa che soluzioni e aiuto per il troubleshooting sono facilmente disponibili, un altro grande vantaggio.
Perché scegliere containerd?
Containerd brilla negli scenari di produzione in cui minimalismo, prestazioni e stabilità prevalgono sulla convenienza degli strumenti integrati.
Per cluster Kubernetes in produzione
Usare containerd come runtime per i nodi Kubernetes porta benefici misurabili:
- Overhead ridotto: eliminare il demone di Docker significa minore consumo di risorse per nodo e risparmi significativi su larga scala
- Maggior stabilità: meno componenti in gioco significa meno potenziali punti di errore nell’infrastruttura
- Superficie di attacco più piccola: meno codice da verificare e meno potenziali vulnerabilità
- Debug semplificato: l’integrazione CRI diretta rimuove lo strato di traduzione dockershim, rendendo il troubleshooting più lineare
- Migliori prestazioni: lo stack runtime snellito migliora i tempi di avvio dei container e la reattività

Lo status di graduation CNCF di containerd ne segnala maturità e affidabilità. I principali cloud provider, tra cui AWS, Google Cloud e Azure, hanno standardizzato su containerd per le loro offerte Kubernetes gestite, a dimostrazione della fiducia nella sua prontezza per infrastrutture critiche di produzione.
Per ambienti specializzati e minimali
Sebbene Kubernetes sia il caso d’uso di produzione più comune, la natura leggera di containerd apre la strada a scenari di distribuzione in cui Docker sarebbe impraticabile.
Edge computing e dispositivi IoT operano spesso con vincoli di risorse severi. Il design leggero di containerd lo rende adatto in questi ambienti, dove lo stack completo di Docker sarebbe proibitivo. Ogni megabyte di memoria e ogni ciclo di CPU contano quando si lavora su hardware embedded.
Gli scenari avanzati di sicurezza beneficiano dell’architettura modulare dei runtime di containerd. Le organizzazioni possono integrare runtime sandboxati per carichi di lavoro che richiedono confini di sicurezza aggiuntivi. Esempi includono:
- gVisor: fornisce una forte isolamento a livello di kernel
- Kata Containers: esegue i container in VM leggere
Queste integrazioni si collegano a containerd senza richiedere modifiche estese.
Migrazione da Docker a containerd
Capire quando usare ciascuno strumento è importante, ma sapere come passare dall’uno all’altro è altrettanto cruciale. Migrare da Docker a containerd in ambienti esistenti richiede una pianificazione attenta, ma il processo è ben documentato e lineare.
Migrare i nodi Kubernetes
I passaggi operativi per migrare i nodi Kubernetes da Docker a containerd seguono uno schema standard:
-
Metti in cordon il nodo: impedisci che vengano schedulati nuovi pod (kubectl cordon <node-name>)
-
Effettua il drain dei pod esistenti: sposta i carichi di lavoro su altri nodi (kubectl drain <node-name>)
-
Aggiorna la configurazione di Kubelet: punta al socket CRI di containerd in /run/containerd/containerd.sock
-
Verifica i plugin CNI: assicurati che containerd abbia i plugin di rete necessari installati
-
Riavvia Kubelet: registrati con il nuovo runtime e riunisci il nodo al cluster
Testare la migrazione prima sui nodi non di produzione aiuta a individuare problemi specifici dell’ambiente prima di estendere i cambiamenti a tutto il cluster. Per evitare gli errori più comuni, assicurati di seguire queste best practice:
- Modifiche ai percorsi di log: Docker e containerd usano posizioni predefinite diverse per i log, quindi aggiorna di conseguenza la tua infrastruttura di logging
- Installa i plugin CNI mancanti: containerd richiede i binari dei plugin CNI per il networking; non sempre sono installati di default
- Attenzione alle differenze nel pull delle immagini: potrebbero servire aggiustamenti per autenticazione e configurazioni dei registry
- Occhio alle discrepanze dei driver di storage: assicura la compatibilità dei volumi persistenti con lo snapshotter di containerd
Orientarsi tra le differenze della CLI
Una volta migrata l’infrastruttura, gli sviluppatori devono adattare i flussi di lavoro quotidiani al nuovo runtime.
Per chi è abituato ai comandi Docker, nerdctl offre un’esperienza quasi identica. Comandi come nerdctl run, nerdctl build e nerdctl compose up funzionano esattamente come gli equivalenti Docker, rendendo la transizione indolore.
Per il debugging, è essenziale capire come mappare i workflow di debug di Docker su containerd. Dove potresti usare docker inspect per esaminare un container, ctr containers info fornisce informazioni simili, sebbene in un formato diverso. Allo stesso modo, ctr tasks list mostra i container in esecuzione.
La maggior parte degli sviluppatori scopre che nerdctl elimina la necessità di imparare la sintassi di ctr per le attività quotidiane. La CLI di basso livello ctr resta utile per risolvere problemi specifici del runtime o quando si lavora direttamente con le API di containerd.
Conclusione
Il rapporto tra Docker e containerd è un perfetto esempio di design modulare di successo nell’infrastruttura software. Docker resta lo strumento ottimale per le persone che scrivono codice, perché offre un’esperienza integrata, un ecosistema completo e interfacce a misura di sviluppatore che rendono produttiva (e piacevole) la creazione di applicazioni containerizzate.
Containerd, invece, eccelle come runtime per le macchine che eseguono il codice, offrendo stabilità, prestazioni e minimalismo necessari per le piattaforme di orchestrazione in produzione. Il fatto che Docker Engine utilizzi containerd sotto il cofano dimostra come i due strumenti si completino, anziché competere tra loro.
Per la maggior parte delle organizzazioni consiglio un approccio pragmatico: continua a usare Docker sui laptop degli sviluppatori, dove i suoi strumenti accelerano i flussi di lavoro di sviluppo, ma valuta la migrazione dei cluster Kubernetes di produzione a containerd direttamente, per i vantaggi operativi di overhead ridotto e stack runtime semplificati.
Che tu scelga la piattaforma completa di Docker o il runtime mirato di containerd, entrambi restano componenti essenziali dell’ecosistema moderno dei container, ciascuno ottimizzato per fasi diverse del ciclo di vita dell’applicazione.
Per continuare a imparare, assicurati di iscriverti al nostro percorso di competenze Containerization and Virtualization with Docker and Kubernetes.
Nell’ecosistema dei container, spesso nasce confusione quando gli sviluppatori confrontano Docker (una piattaforma completa) con containerd, che in realtà è un componente runtime mirato. È come paragonare un’automobile completa al suo motore: entrambi sono essenziali, ma servono scopi diversi a livelli di astrazione differenti.
In questa guida chiarirò il rapporto tra queste due tecnologie, esplorandone le architetture, l’integrazione con Kubernetes e gli scenari specifici in cui ciascuno strumento eccelle. Che tu stia creando container in locale o gestendo cluster Kubernetes in produzione, capire quando usare Docker rispetto a containerd può incidere in modo significativo sulle decisioni relative all’infrastruttura.
Alla fine di questo articolo avrai un’idea chiara di come Docker e containerd si completino a vicenda e di come scegliere lo strumento giusto per le tue esigenze specifiche, che si tratti di sviluppo locale rapido o di distribuzioni in produzione su larga scala.
Se sei alle prime armi con la containerizzazione, ti consiglio vivamente di seguire il nostro corso Containerization and Virtualization Concepts.
Che cos’è Docker?
Docker ha rivoluzionato lo sviluppo software rendendo la containerizzazione accessibile e pratica per gli sviluppatori di tutti i giorni. In quanto piattaforma completa, Docker offre tutto il necessario per creare, distribuire ed eseguire applicazioni containerizzate.
Per chi inizia, questa Guida pratica ai container fornisce un’ottima introduzione a Docker e ai container.
Una piattaforma di container completa
Docker funziona come una piattaforma all-in-one per i container, offrendo una suite di strumenti integrata che copre gran parte del ciclo di vita dei container. Al suo core, Docker impacchetta le applicazioni con tutte le loro dipendenze in immagini portabili e autosufficienti che possono essere eseguite in modo coerente in qualsiasi ambiente, dal laptop di uno sviluppatore ai server di produzione.
Questo approccio completo ha reso Docker lo standard di settore per lo sviluppo locale e per l’esperienza degli sviluppatori. Tra i principali vantaggi:
Componenti chiave e flusso di lavoro
L’architettura di Docker è composta da vari componenti interconnessi che lavorano in modo fluido.
La Docker CLI fornisce l’interfaccia lato utente con cui gli sviluppatori impartiscono i comandi. Quando esegui un comando come
docker run, la CLI comunica con il demone Docker (dockerd), che è il servizio principale che gestisce container, immagini, reti e volumi.Per gli utenti Windows e macOS, Docker Desktop offre un ulteriore livello di praticità con un’interfaccia grafica, rendendo la gestione dei container accessibile anche a chi è meno a suo agio con la riga di comando. Questo ambiente integrato include tutto ciò che serve per lo sviluppo locale, dal supporto a Kubernetes alla gestione dei volumi.
Il flusso di lavoro tipico segue uno schema chiaro:
Ciò che molti sviluppatori non sanno è che Docker non esegue i container direttamente. Invece,
dockerddelega l’esecuzione effettiva a containerd, che gira in background come processo separato.Questo design modulare, in cui Docker usa containerd per le operazioni di basso livello, consente a ciascun componente di concentrarsi su ciò che sa fare meglio: Docker offre l’interfaccia e l’ecosistema a misura di sviluppatore, mentre containerd gestisce i dettagli più tecnici dell’esecuzione dei container.
Che tu stia iniziando ora con Docker o voglia fare un passo avanti, dai un’occhiata alle nostre 10 idee per progetti Docker per tutti i livelli.
Che cos’è containerd?
Dopo aver visto l’approccio di piattaforma completa di Docker, esaminiamo ora containerd, il runtime specializzato che alimenta l’esecuzione dei container sia all’interno di Docker sia nel più ampio ecosistema.
Un runtime per container standard di settore
Containerd è un runtime core per container leggero e standard di settore che ha conseguito la graduation presso la Cloud Native Computing Foundation (CNCF), a testimonianza di maturità, affidabilità e adozione diffusa. Originariamente parte di Docker, containerd è stato estratto nel 2017 e donato alla CNCF per favorire un’adozione più ampia nell’ecosistema.
A differenza dell’ampia gamma di funzionalità di Docker, l’ambito di containerd è volutamente limitato: gestisce le operazioni fondamentali del ciclo di vita dei container come avvio, arresto, pausa ed eliminazione, e si occupa del trasferimento e dell’archiviazione delle immagini. Questo focus mirato rende containerd estremamente stabile ed efficiente, qualità essenziali per gli ambienti di produzione.
Pensa a containerd come alla “tubatura”: un’infrastruttura progettata per essere integrata in sistemi più grandi piuttosto che usata direttamente dalle persone. Piattaforme importanti come Kubernetes, AWS Fargate e Google Kubernetes Engine fanno affidamento su containerd per eseguire i container, anche se gli utenti interagiscono con queste piattaforme tramite le loro interfacce.
Architettura e design
Per capire perché containerd è così ampiamente adottato nei sistemi di produzione, dobbiamo esaminarne i principi architetturali.
L’architettura di containerd esemplifica i principi del design modulare. Costruito attorno agli standard della Open Container Initiative (OCI), containerd garantisce compatibilità in tutto l’ecosistema dei container. Ciò significa che qualsiasi immagine conforme a OCI funzionerà con containerd, indipendentemente dallo strumento con cui è stata creata.
L’architettura è composta da alcuni livelli chiave:
Quando containerd deve avviare un container, genera runc, che esegue il lavoro effettivo di creare namespace isolati, impostare i cgroup per i limiti di risorse e avviare il processo del container.
Questa separazione delle responsabilità rende containerd altamente estensibile. Le organizzazioni possono personalizzare quasi ogni aspetto senza toccare il codice core di containerd.
Differenze chiave tra containerd e Docker
Con una solida comprensione del ruolo di ciascuno strumento, vediamo come differiscono nella pratica, dall’architettura ai modelli di integrazione.
Runtime di container vs piattaforma
La differenza fondamentale sta nell’ambito e nello scopo.
Docker usa un’architettura con demone centralizzato in cui
dockerdcoordina molti aspetti diversi, come:Questo design all-in-one semplifica l’esperienza degli sviluppatori ma introduce overhead attraverso più livelli di astrazione.
Containerd, al contrario, si concentra in modo ristretto sull’esecuzione dei container. Non include funzionalità di build delle immagini, di orchestrazione o un’interfaccia grafica. Per il networking, Docker include
Libnetworknel suo demone, mentre containerd si affida a plugin esterni CNI (Container Network Interface) intercambiabili in base alle esigenze.Questa differenza architetturale ha implicazioni sulle prestazioni. In ambienti ad alta dinamicità, in cui i container vengono avviati e arrestati di frequente, come nei cluster Kubernetes con autoscaling, il design snello di containerd può portare a tempi di avvio più rapidi e a un consumo di risorse inferiore. L’overhead ridotto significa meno memoria e CPU utilizzate, un vantaggio significativo su larga scala.
Integrazione con Kubernetes
Parlando di Kubernetes, il rapporto tra i runtime dei container e le piattaforme di orchestrazione si è evoluto molto, in particolare per come Kubernetes si collega a containerd.
Il rapporto tra Kubernetes e i runtime dei container ha subìto un cambiamento importante con Kubernetes 1.24, rilasciato nel 2022. Questa versione ha rimosso il "Dockershim", uno strato di compatibilità che permetteva a Kubernetes di usare Docker come runtime dei container.
Dockershim è sempre stato pensato come ponte temporaneo. Quando Kubernetes ha introdotto la Container Runtime Interface (CRI) per standardizzare la comunicazione con i runtime, Docker non poteva implementarla direttamente perché precedente alla progettazione di CRI. Dockershim traduceva tra Kubernetes e Docker, aggiungendo uno strato di traduzione alla fine non necessario.
Le versioni moderne di Kubernetes comunicano direttamente con containerd tramite CRI, eliminando del tutto lo strato di traduzione Docker. Questa semplificazione porta benefici concreti:
Per gli utenti Kubernetes, questo cambiamento è in gran parte trasparente. Le immagini Docker restano pienamente compatibili perché aderenti agli standard OCI. L’impatto pratico è che i cluster Kubernetes in produzione ora funzionano in modo più efficiente usando direttamente containerd, mentre gli sviluppatori possono continuare a usare Docker in locale per build e test.
Se non ti è chiaro pro e contro dell’uso di Kubernetes in primo luogo, dai un’occhiata a questo confronto su Docker Compose vs Kubernetes.
Creazione e gestione delle immagini
Sebbene l’integrazione del runtime sia cruciale per l’orchestrazione, il flusso di lavoro degli sviluppatori dipende molto da come ciascuno strumento gestisce la creazione e l’archiviazione delle immagini. La build delle immagini rappresenta un divario di capacità significativo tra Docker e containerd.
Docker offre un sistema di build integrato tramite Dockerfile e BuildKit, che consente di costruire build multi-stage complesse con caching, parallelizzazione e funzionalità avanzate come secret di build e forwarding dell’agente SSH.
Containerd, per progettazione, non include alcun flusso di lavoro nativo per la build delle immagini. Per creare immagini con containerd, gli sviluppatori devono usare strumenti esterni. Le opzioni includono l’esecuzione di BuildKit come demone separato e l’uso di
buildctlper build da riga di comando, oppure l’adozione dinerdctl, una CLI compatibile con Docker che integra BuildKit.Anche i meccanismi di storage differiscono per filosofia. La gestione dei volumi di Docker fornisce un’astrazione naturale per gli sviluppatori, con volumi nominati che persistono i dati indipendentemente dal ciclo di vita dei container. Containerd usa un sistema di snapshotter più a basso livello, in cui diversi driver di snapshot possono essere collegati per gestire i filesystem a livelli in modo diverso in base ai requisiti di storage sottostante.
Questa differenza riflette i pubblici di riferimento degli strumenti:
CLI, esperienza sviluppatore e nerdctl
Oltre ad architettura e funzionalità, l’esperienza quotidiana degli sviluppatori è plasmata soprattutto dall’interfaccia a riga di comando che ciascuno strumento offre. L’esperienza da terminale rivela con maggiore chiarezza le diverse filosofie di design.
La CLI di Docker è nota per la sua facilità d’uso. Comandi come
docker run,docker buildedocker logssono intuitivi, ben documentati e progettati per le persone. La CLI include default utili, messaggi di errore chiari e opzioni estese che coprono la maggior parte dei casi d’uso.Containerd include
ctr, una CLI minimale pensata esclusivamente per il debugging e il testing delle funzionalità di basso livello di containerd. Lo strumentoctrè volutamente poco adatto agli sviluppatori applicativi. Manca di funzionalità comuni come scorciatoie per il port mapping, policy di riavvio automatico e integrazione con i gestori di credenziali. È progettato per gli sviluppatori di containerd, non per chi sviluppa applicazioni.Colmare il divario con nerdctl
Questo gap di usabilità ha portato alla creazione di
nerdctl, una CLI compatibile con Docker per containerd. Usarenerdctlè identico a usare Docker: stessa sintassi dei comandi, stesse flag, stesso flusso di lavoro, ma con containerd come runtime sottostante. Questo rendenerdctlun ottimo ponte per i team che passano da Docker a containerd negli ambienti di sviluppo.Nella pratica, gli sviluppatori interagiscono raramente direttamente con containerd. Quando serve,
nerdctlfornisce l’interfaccia familiare che ci si aspetta, mentre gli operatori di piattaforma e gli amministratori usano le API di containerd in modo programmatico tramite sistemi di orchestrazione come Kubernetes.Per illustrare le differenze nella pratica, ecco come si confrontano le comuni operazioni sui container tra i tre strumenti da riga di comando:
Task
Docker
nerdctl
ctr
Eseguire un container
docker run -d -p 8080:80 nginxnerdctl run -d -p 8080:80 nginxctr run --net-host -d docker.io/library/nginx:latest nginx_idElencare i container
docker psnerdctl psctr tasks listCreare un’immagine
docker build -t myapp .nerdctl build -t myapp .Non supportato
Visualizzare i log
docker logs <container>nerdctl logs <container>Non supportato
Ispezionare un container
docker inspect <container>nerdctl inspect <container>ctr containers info <container>Scaricare un’immagine
docker pull nginxnerdctl pull nginxctr images pull docker.io/library/nginx:latestSupporto Compose
docker compose upnerdctl compose upNon supportato
Nota: ctr non supporta il port mapping (
-p) e richiede l’host networking (--net-host) per esporre i servizi. Inoltre non scarica automaticamente le immagini.Panoramica delle differenze chiave
Prima di passare ai consigli per casi d’uso specifici, ricapitoliamo le differenze trattate finora. La tabella seguente riassume le principali differenze funzionali tra Docker e containerd, evidenziandone le capacità e i destinatari:
Feature
Docker
Containerd
Image Building
Integrato (Dockerfile, BuildKit)
Richiede strumenti esterni (buildctl, nerdctl)
Orchestrazione
Docker Swarm / Kubernetes
Nessuna (usato da Kubernetes)
Gestione storage
Gestione dei volumi
Sistema di snapshotter
Interfaccia grafica
Docker Desktop
Nessuna
Ciclo di vita dei container
Gestione completa (tramite containerd)
Focus core (compatibile CRI)
Utenti principali
Sviluppatori di applicazioni
Operatori di cluster, Platform builder
Perché scegliere Docker?
Ora che abbiamo coperto le differenze tecniche, vediamo gli scenari pratici in cui ciascuno strumento eccelle. Nonostante la diffusione di containerd negli ambienti di produzione, Docker resta la scelta migliore in contesti specifici in cui contano soprattutto l’esperienza sviluppatore e gli strumenti completi.
Per sviluppo locale e prototipazione
Docker eccelle quando ti serve una soluzione “tutto in uno” per scrivere e testare codice. La toolchain integrata consente di passare da zero a container in esecuzione in pochi minuti, senza assemblare più componenti o configurare networking complessi.
L’ecosistema Docker offre enormi benefici di produttività:
Per i team che usano Docker Desktop, la GUI offre praticità aggiuntive che accelerano sensibilmente l’onboarding e il lavoro quotidiano.
Per pipeline di build complesse
Oltre allo sviluppo, le capacità di build di Docker lo rendono la scelta naturale per workflow di integrazione e distribuzione continua sofisticati.
BuildKit integrato in Docker offre funzionalità avanzate essenziali per pipeline CI/CD moderne. Le build multi-stage riducono al minimo le dimensioni delle immagini mantenendo leggibili i Dockerfile. I meccanismi di caching di BuildKit riutilizzano in modo intelligente i layer tra build, riducendo drasticamente i tempi nelle pipeline di integrazione continua.
La maggior parte delle piattaforme di automazione, come GitHub Actions, GitLab CI, Jenkins e altre, hanno integrazioni Docker mature e collaudate. Queste integrazioni gestiscono senza attriti autenticazione, caching e pubblicazione delle immagini.
Sebbene altri strumenti possano ottenere risultati simili, l’ubiquità di Docker significa che soluzioni e aiuto per il troubleshooting sono facilmente disponibili, un altro grande vantaggio.
Perché scegliere containerd?
Containerd brilla negli scenari di produzione in cui minimalismo, prestazioni e stabilità prevalgono sulla convenienza degli strumenti integrati.
Per cluster Kubernetes in produzione
Usare containerd come runtime per i nodi Kubernetes porta benefici misurabili:
Lo status di graduation CNCF di containerd ne segnala maturità e affidabilità. I principali cloud provider, tra cui AWS, Google Cloud e Azure, hanno standardizzato su containerd per le loro offerte Kubernetes gestite, a dimostrazione della fiducia nella sua prontezza per infrastrutture critiche di produzione.
Per ambienti specializzati e minimali
Sebbene Kubernetes sia il caso d’uso di produzione più comune, la natura leggera di containerd apre la strada a scenari di distribuzione in cui Docker sarebbe impraticabile.
Edge computing e dispositivi IoT operano spesso con vincoli di risorse severi. Il design leggero di containerd lo rende adatto in questi ambienti, dove lo stack completo di Docker sarebbe proibitivo. Ogni megabyte di memoria e ogni ciclo di CPU contano quando si lavora su hardware embedded.
Gli scenari avanzati di sicurezza beneficiano dell’architettura modulare dei runtime di containerd. Le organizzazioni possono integrare runtime sandboxati per carichi di lavoro che richiedono confini di sicurezza aggiuntivi. Esempi includono:
Queste integrazioni si collegano a containerd senza richiedere modifiche estese.
Migrazione da Docker a containerd
Capire quando usare ciascuno strumento è importante, ma sapere come passare dall’uno all’altro è altrettanto cruciale. Migrare da Docker a containerd in ambienti esistenti richiede una pianificazione attenta, ma il processo è ben documentato e lineare.
Migrare i nodi Kubernetes
I passaggi operativi per migrare i nodi Kubernetes da Docker a containerd seguono uno schema standard:
Metti in cordon il nodo: impedisci che vengano schedulati nuovi pod (
kubectl cordon <node-name>)Effettua il drain dei pod esistenti: sposta i carichi di lavoro su altri nodi (
kubectl drain <node-name>)Aggiorna la configurazione di Kubelet: punta al socket CRI di containerd in
/run/containerd/containerd.sockVerifica i plugin CNI: assicurati che containerd abbia i plugin di rete necessari installati
Riavvia Kubelet: registrati con il nuovo runtime e riunisci il nodo al cluster
Testare la migrazione prima sui nodi non di produzione aiuta a individuare problemi specifici dell’ambiente prima di estendere i cambiamenti a tutto il cluster. Per evitare gli errori più comuni, assicurati di seguire queste best practice:
Orientarsi tra le differenze della CLI
Una volta migrata l’infrastruttura, gli sviluppatori devono adattare i flussi di lavoro quotidiani al nuovo runtime.
Per chi è abituato ai comandi Docker, nerdctl offre un’esperienza quasi identica. Comandi come
nerdctl run,nerdctl buildenerdctl compose upfunzionano esattamente come gli equivalenti Docker, rendendo la transizione indolore.Per il debugging, è essenziale capire come mappare i workflow di debug di Docker su containerd. Dove potresti usare
docker inspectper esaminare un container,ctr containers infofornisce informazioni simili, sebbene in un formato diverso. Allo stesso modo,ctr tasks listmostra i container in esecuzione.La maggior parte degli sviluppatori scopre che
nerdctlelimina la necessità di imparare la sintassi dictrper le attività quotidiane. La CLI di basso livelloctrresta utile per risolvere problemi specifici del runtime o quando si lavora direttamente con le API di containerd.Conclusione
Il rapporto tra Docker e containerd è un perfetto esempio di design modulare di successo nell’infrastruttura software. Docker resta lo strumento ottimale per le persone che scrivono codice, perché offre un’esperienza integrata, un ecosistema completo e interfacce a misura di sviluppatore che rendono produttiva (e piacevole) la creazione di applicazioni containerizzate.
Containerd, invece, eccelle come runtime per le macchine che eseguono il codice, offrendo stabilità, prestazioni e minimalismo necessari per le piattaforme di orchestrazione in produzione. Il fatto che Docker Engine utilizzi containerd sotto il cofano dimostra come i due strumenti si completino, anziché competere tra loro.
Per la maggior parte delle organizzazioni consiglio un approccio pragmatico: continua a usare Docker sui laptop degli sviluppatori, dove i suoi strumenti accelerano i flussi di lavoro di sviluppo, ma valuta la migrazione dei cluster Kubernetes di produzione a containerd direttamente, per i vantaggi operativi di overhead ridotto e stack runtime semplificati.
Che tu scelga la piattaforma completa di Docker o il runtime mirato di containerd, entrambi restano componenti essenziali dell’ecosistema moderno dei container, ciascuno ottimizzato per fasi diverse del ciclo di vita dell’applicazione.
Per continuare a imparare, assicurati di iscriverti al nostro percorso di competenze Containerization and Virtualization with Docker and Kubernetes.