Tutorial completo di Docker Compose per l'orchestrazione dei container

Ultimo aggiornamento: 17/12/2025
Autore: Isaac
  • Docker Compose consente di definire e gestire più contenitori da un singolo file YAML, semplificando l'utilizzo di applicazioni basate su microservizi.
  • La sezione servizi è obbligatoria e si completa con reti, volumi, configurazioni e segreti per controllare la comunicazione, la persistenza e la configurazione.
  • Esempi come Flask+Redis o un'app full stack con frontend, backend e database mostrano come Compose semplifica lo sviluppo e la distribuzione.
  • I comandi docker compose up, down, ps e i registri Costituiscono il flusso di lavoro di base per il sollevamento, il debug e l'arresto di pile di container.

Tutorial passo dopo passo su Docker Compose

Se hai già sperimentato con i contenitori e hai visto che per un'app "reale" hai bisogno più di un servizio in esecuzione contemporaneamente (database, API, frontend, cache...), prima o poi ti imbatterai in Docker Compose. Questo strumento ti consente di configurare tutto questo con un singolo file e un paio di comandi, senza dover destreggiarti tra terminali e script infiniti.

In questo tutorial imparerai cos'è Docker Compose, come funziona il suo file compose.yaml e come orchestrare le applicazioni Configurazioni multi-container pulite: da semplici esempi con Flask e Redis ad architetture più complesse con frontend, backend e database. Imparerai anche nozioni su networking, volumi, configurazioni, segreti e i comandi chiave per lavorare agevolmente in ambienti di sviluppo e più esigenti.

Che cos'è Docker Compose e perché vale la pena utilizzarlo?

Docker Compose è un'estensione Docker che consente definire e gestire più contenitori Come se fossero un'unica applicazione. Invece di avviare manualmente ogni servizio con il suo "docker run" e i suoi parametri, si descrive tutto in un file YAML e lo si avvia con un singolo comando.

Il bello di Compose è che molte applicazioni moderne sono basate su di esso. microservizi che risiedono in singoli contenitoriUn database, un'API, un frontend, un sistema di code, una cache simile a Redis, ecc. Docker consiglia che ogni contenitore esegua un singolo servizio, quindi se si cerca di far stare tutto in una singola immagine, si finisce per ottenere un mostro difficile da scalare e gestire.

È possibile eseguire due o più servizi all'interno dello stesso contenitore, ma Ciò annulla molti dei vantaggi di Docker.Se uno si guasta, trascina giù anche gli altri; non è possibile ridimensionare solo la parte che riceve il carico maggiore e la gestione di registri, risorse e guasti diventa un vero disastro.

Con Docker Compose definisci ogni servizio separatamente, specificando come si connettono tra loro, quali dati persistono, quali porte espongono, quali variabili di ambiente utilizzano... In questo modo Se un contenitore si guasta, gli altri possono continuare a funzionare. a seconda di come lo si configura, ridimensionare un pezzo specifico è semplice come modificarne le impostazioni o il numero di repliche.

Inoltre, Compose si adatta perfettamente ai flussi di lavoro di CI/CD e distribuzione in produzionePuoi utilizzarlo direttamente con strumenti come Portainer o Docker Swarm e, se lavori con Kubernetes, progetti come Kompose ti consentono di tradurre un file compose.yaml in manifesti Kubernetes senza dover riscrivere tutto manualmente.

Configurazione dei servizi con Docker Compose

Prerequisiti per seguire il tutorial Docker Compose

Per seguire comodamente gli esempi in questo tutorial è necessario avere Docker e Docker Compose installatiOggi ci sono due percorsi principali:

  • Docker Engine + Docker Compose installati come file binari autonomi.
  • Desktop mobile, che include Docker Engine, Docker Compose e un'interfaccia grafica.

È importante che tu abbia una base minima di comandi Docker di base (immagini, contenitori, porte, volumi) e non abbiate paura di usare la riga di comando. Gli esempi sono solitamente dati in Linux (ad esempio Ubuntu 22.04), ma la logica si applica ugualmente in Windows e macOS con Docker Desktop.

Controlla che tutto sia in ordine eseguendolo sul tuo terminale qualcosa di semplice come “docker –version” e “docker compose version”Se entrambi i comandi rispondono senza errori, puoi continuare con gli esempi.

Struttura di base di un file compose.yaml

Il cuore di Docker Compose è il file compose.yaml (o docker-compose.yml)Qui descriviamo quali servizi vogliamo impostare e come dovrebbero essere correlati. Sebbene in precedenza il campo fosse utilizzato version Per contrassegnare la versione del formato, la documentazione attuale consiglia di non definirla, in modo che venga sempre utilizzata la versione più recente dello schema.

All'interno del file Compose troverai diverse sezioni possibili, anche se solo una è obbligatoria: serviziDa lì puoi aggiungere altre sezioni a seconda della complessità del tuo progetto:

  • servizi: definizione di ciascun microservizio (obbligatorio).
  • reti: reti personalizzate per controllare la comunicazione tra container.
  • volumi: volumi per conservare i dati o condividerli tra i servizi.
  • configs: configurazione del servizio (ad esempio, file di configurazione del server web).
  • segreti: gestione di informazioni sensibili (password, chiavi API…).

In questo tutorial vedrai come combinare tutte queste sezioni per un tipico progetto che include un applicazione, un database e un'APIe anche un esempio di un'app web in Python con Flask e Redis.

Servizi in Docker Compose: il nucleo della definizione

sezione i servizi sono il pezzo essenziale da qualsiasi file Compose. In esso definisci ciascuno dei contenitori che comporranno la tua applicazione, assegnando loro il nome che desideri (ad esempio, web, database, api, redis, Ecc.).

Per ogni servizio è possibile stabilire un buon numero di parametriTra questi ce ne sono alcuni ampiamente utilizzati in progetti concreti:

Parametro build indica dove si trova il Dockerfile da cui verrà creata l'immagine del servizio. In genere, viene specificato un contesto (directory) in cui risiede il Dockerfile dell'applicazione che si desidera pacchettizzare.

Se hai già creato un'immagine o vuoi usarne una dal registro, usa image per farvi riferimentoIl nome segue il formato [<registry>/][<project>/]<image>[:<tag>|@<digest>]E se hai bisogno di controllare quando quell'immagine viene scaricata o aggiornata, puoi usare pull_policy.

Campo ports Viene utilizzato per mappare le porte tra l'host e il contenitoreLa sintassi è del tipo [HOST:]CONTAINER[/PROTOCOL]Ad esempio, se un database PostgreSQL è in ascolto sulla porta 5432 all'interno del contenitore e si desidera esporlo sulla porta 5555 dell'host, si dovrebbe procedere in questo modo: "5555:5432" nell'elenco dei porti.

  Guida completa sulla chiave SysRq in Linux

La politica di riavvio è controllata con restartche indica cosa fare quando un contenitore termina con un errore o si ferma. I valori tipici sono no, always, on-failure y unless-stoppedconsentendo ai servizi critici di rimanere operativi anche in caso di occasionali interruzioni.

Se un servizio necessita che un altro sia disponibile prima di iniziare, puoi utilizzare depends_on per definire le dipendenze tra i contenitoriUn esempio classico è un'app che richiede che il database sia attivo e funzionante per evitare che la connessione iniziale fallisca.

Per la configurazione e le credenziali, sono disponibili due approcci comuni: env_file y environment. Con env_file Stai indicando uno o più file .env con le variabili d'ambiente, mentre in environment È possibile elencarli direttamente in YAML. La soluzione migliore è usare i file. .env per impedire che password e dati sensibili vengano incorporati nel file compose.yaml stesso.

Parametro volumes consente il montaggio di percorsi o volumi host All'interno del contenitore, utilizzerai sia la persistenza dei dati che la condivisione delle cartelle tra i servizi. Qui, farai riferimento solo ai volumi che potrai definire in seguito nella sezione precedente. volumes se hai bisogno che vengano condivisi o gestiti in modo più esplicito.

Con questi campi è già possibile creare servizi abbastanza completi. La specifica Compose include molte altre opzioni avanzate (salute, limiti delle risorse, comandi di Bootecc.), ma con questi si coprono già gli usi più comuni.

Esempio 1: Applicazione Web in Python con Flask e Redis

Un tipico esempio per comprendere Docker Compose è la creazione di un semplice applicazione web in PythonUtilizzo di Flask per servire le pagine e di Redis come archivio in memoria per un contatore di accessi. L'idea è che non sia necessario installare né Python né Redis sulla macchina: tutto viene eseguito all'interno di container.

Il flusso di lavoro sarebbe più o meno questo: prima crei una directory per il progetto e al suo interno aggiungi un file app.py con il codice Flask. In quel codice usi "redis" come nome host e porta 6379, che è la porta predefinita per il servizio Redis nel tuo contenitore.

La funzione che gestisce il contatore dei visitatori Tenta di connettersi a Redis più volte. Prima di rinunciare, tieni presente che potrebbero volerci alcuni secondi prima che il contenitore Redis diventi disponibile quando sollevi l'intero stack.

Più app.py, crei un file requirements.txt con dipendenze Python (ad esempio Flask e redis-py) e un Dockerfile che specifica come creare l'immagine della tua applicazione web: immagine Python di base (3.7, 3.10 o qualsiasi altra), directory di lavoro, variabili di ambiente per Flask, installazione di gcc e dipendenze di sistema, copia di requirements.txt, installazione del pacchetto e copia del codice.

Nel Dockerfile si contrassegna anche il porta che visualizzerà il contenitore (ad esempio 5000) e si definisce il comando predefinito, normalmente flask run --debug o simile, in modo che si avvii automaticamente quando viene creato il contenitore.

Con tutto questo pronto, il file compose.yaml definisce due servizi: uno chiamato, ad esempio, web, che è costruito dal Dockerfile del progetto ed espone la porta 8000 esternamente (mappando la porta 8000 dell'host alla porta 5000 del contenitore), e un altro chiamato redis che Estrai l'immagine ufficiale di Redis da Docker Hub.

Per avviare l'applicazione, è sufficiente andare alla directory del progetto ed eseguire "docker compose up"Compose si occupa di scaricare l'immagine Redis, di creare l'immagine dell'applicazione web e di avviare entrambi i servizi nell'ordine corretto.

Una volta che è attivo e funzionante, accedi con il tuo browser a http://localhost:8000 (o http://127.0.0.1:8000) e dovresti vedere un messaggio di tipo "Hello World" e un Contatore visitatori che aumenta ogni volta che ricarichi la pagina. Se si ispezionano le immagini locali con docker image lsVedrai qualcosa come redis y web creato o scaricato.

Quando vuoi fermare tutto, puoi farlo CTRL+C nel terminale dove hai lasciato "docker compose up" o eseguire docker compose down dalla directory del progetto. Questo arresterà e rimuoverà i contenitori creati da quella composizione.

Miglioramento del flusso di lavoro: Associa i mount e Componi Watch

Lavorare nello sviluppo con Docker è più conveniente se Non è necessario ricostruire l'immagine ogni volta che tocchi il codice. È qui che entrano in gioco i Bind Mount e, nelle versioni più recenti, Docker Compose Watch.

Un Bind Mount comporta il montaggio di una cartella dal computer all'interno del contenitore. Nel file compose.yaml, si aggiunge una sezione al servizio web. volumes che mappa la directory del progetto alla directory di lavoro dal contenitore, ad esempio .:/codeIn questo modo, tutte le modifiche apportate nell'editor vengono immediatamente riflesse nel contenitore.

Se attivi anche la modalità debug di Flask con la variabile FLASK_DEBUG=1, il comando flask run L'applicazione verrà ricaricata automaticamente quando rileverà modifiche nei file, senza dover interrompere e riavviare.

Docker Compose Watch fa un ulteriore passo avanti: puoi usare “docker compose watch” o “docker compose up –watch” Ciò consente a Compose di monitorare i file di progetto e sincronizzare le modifiche con i contenitori in modo più intelligente. Quando si salva un file, questo viene copiato nel contenitore e il server di sviluppo aggiorna l'applicazione senza riavviare l'intero contenitore.

Prova, ad esempio, a cambiare il messaggio di benvenuto in app.py da "Hello World!" a una frase come "Ciao da Docker"Salva il file, aggiorna il browser e vedrai subito il nuovo messaggio, mentre il contatore delle visite continua a funzionare senza perdere il suo stato.

E quando hai finito di lavorare, come sempre, puoi tirare docker compose down per spegnere e pulire i contenitori che erano in corso con quella pila.

  Scorciatoie da tastiera in Linux: la guida definitiva per andare più veloci

Esempio 2: applicazione full stack con frontend, backend e database

Per vedere Docker Compose in un'architettura un po' più realistica, immagina un applicazione per la lista delle cose da fare (Todo List) con un frontend in Vue.js, un'API in Node.js e un database MongoDB. Ogni parte risiede nella propria directory e ha il proprio Dockerfile.

Nel repository potresti trovare una cartella frontend con l'app Vue e un'altra backend con il server Node. Il backend espone gli endpoint per creare, elencare, aggiornare ed eliminare attivitàe si connette a MongoDB per memorizzarli. Il frontend utilizza questi endpoint per visualizzare e gestire l'elenco delle attività nel browser.

il file docker-compose.yml Si trova alla radice del progetto e definisce tre servizi: frontend, backend y databaseIl servizio frontend viene creato a partire dal Dockerfile nella cartella corrispondente, solitamente esponendo la porta interna 80 e mappandola sulla porta 5173 dell'host (ad esempio, per utilizzare lo stesso URL dello sviluppo locale).

Il backend è costruito dal Dockerfile nella directory backend, espone la porta 3000 (sia all'interno che all'esterno del contenitore, se si vuole semplificare) e dichiara una dipendenza dal database per garantire che MongoDB sia disponibile all'avvio.

Il servizio database utilizzare direttamente Immagine ufficiale di MongoDB e costruire un volume, diciamo mongodb_datain /data/db, dove Mongo memorizza i suoi dati. Quel volume è dichiarato nella sezione superiore. volumes da compose, in modo che i dati persistano anche se si eliminano e si ricreano i contenitori.

Infine, tutti questi servizi si connettono tramite una rete personalizzata, ad esempio my-network, definito nella sezione networksCiò consente di risolverli in base al nome del servizio (il backend può connettersi a Mongo utilizzando il nome host). database) e che il traffico è incapsulato in quella rete isolata.

Con la configurazione pronta, esegui docker compose up Al centro del progetto, è responsabile di crea o scarica le immagini e avvia i tre contenitoriPuoi controllare che tutto sia a posto con docker compose ps, quindi accedendo http://localhost:5173 per visualizzare l'app Vue nel tuo browser e creare le tue prime attività.

Networking in Docker Compose: collegare i servizi tra loro

Le reti sono il livello che consente ai tuoi contenitori Si "vedono" e parlano in modo controllatoPer impostazione predefinita, Docker crea già reti per Compose, ma definendole esplicitamente si ottiene maggiore chiarezza e controllo su cosa può comunicare con cosa.

Funziona in modo semplice: ogni servizio include un campo networks dove indichi a quali reti appartiene, e poi nella sezione superiore networks Tali reti vengono definite tramite la loro configurazione. L'approccio più comune (e consigliato in molti casi) è quello di utilizzare il driver. bridge.

Una rete bridge crea un spazio privato rete per i tuoi contenitoricon risoluzione DNS automatica basata sul nome del servizio. Ciò significa che, ad esempio, se il servizio di database si chiama databaseQualsiasi altro servizio sulla stessa rete può connettersi utilizzando solo database come nome host.

In un progetto con frontend, backend e database, potresti decidere, ad esempio, di creare una rete frontend e una rete backend. Il frontend si connetterebbe al backend e il backend al database, ma il frontend e il database... Non dovrebbero necessariamente condividere una reteriducendo la superficie interna esposta.

Nel codice, questo si traduce in qualcosa di semplice come assegnare la rete corrispondente a ciascun servizio e quindi definire tali reti con bridge driver. A livello applicativo, l'approccio più semplice è utilizzare il nome del servizio come host durante la configurazione delle connessioni: di app a databaseAd esempio, semplicemente indicando che l'host del database è "database".

Volumi in Docker Compose: persistenza dei dati

I volumi sono il metodo consigliato per mantenere le informazioni generate dai contenitoriCome databaseFile utente, backup, ecc. Vengono utilizzati anche per condividere dati tra servizi all'interno dello stesso stack.

Nella sezione services È possibile montare i volumi direttamente con volumesMa quando si desidera che quel volume sia accessibile a più contenitori o si desidera gestirlo in modo più esplicito, è possibile definirlo anche nella sezione superiore. volumes dal file compose.yaml.

Immagina di voler configurare un sistema di backup per il tuo database. Il servizio di database monterebbe un volume in cui archiviare i dati e un altro servizio dedicato ai backup che Montare lo stesso volume in modalità di lettura per eseguire esportazioni o sincronizzazioni senza toccare il contenitore principale.

Docker consente di ottimizzare la configurazione dei volumi con più parametri (tipo di driver, opzioni specifiche per autisti fattori esterni, ecc.), ma nella maggior parte dei casi la cosa più pratica è lasciare che accada. Docker gestisce automaticamente i volumi senza impazzire con configurazioni strane.

La cosa importante è avere ben chiaro quali cartelle nei servizi devono essere persistenti e dichiararle come volumi in Compose, in modo da non perdere dati quando si ricreano i contenitori o si aggiornano le immagini.

Configs: gestione dei file di configurazione

sezione configs È progettato per gestire i file di configurazione di servizi all'interno del tuo stack, simili ai volumi ma specificamente focalizzati sulla configurazione.

Pensa a un server Apache o Nginx in esecuzione su Docker. Probabilmente avrai bisogno regola il tuo file di configurazione Ricostruire l'immagine ogni volta che si modificano questi file è inefficiente e fastidioso, soprattutto in ambienti in cui i parametri vengono modificati frequentemente.

  Come risolvere il codice di errore Disney Plus 24

Con configs Puoi specificare nel servizio che desideri applicare una configurazione specifica e poi descrivilo nella sezione configsEsistono diversi modi per definirli, il più comune dei quali è:

  • fileLa configurazione viene generata da un file locale.
  • external: se è contrassegnato come trueCompose presuppone che la configurazione esista già e che venga solo referenziata.
  • name: Nome interno della configurazione in Docker, utile quando combinato con external: true.

In questo modo puoi aggiornare il file di configurazione sulla tua macchina e tornare a aumentare lo stack senza dover ricostruire l'immagine di base, mantenendo il codice dell'immagine separato dalla configurazione specifica dell'ambiente.

Segreti: credenziali e dati sensibili

sezione secrets risolve un problema classicoDove posso archiviare password, chiavi API e altre informazioni sensibili senza lasciarle sparse nel codice o nel file YAML?

Proprio come con le configurazioni, i segreti possono essere definiti in modi diversiLa cosa normale è:

  • file: il segreto viene generato dal contenuto di un file (ad esempio, un file di testo con una chiave).
  • environmentIl segreto viene creato utilizzando il valore di una variabile di ambiente sul tuo sistema.
  • external: indica che il segreto è già stato creato e deve essere solo referenziato, utile per evitare di sovrascrivere segreti gestiti dall'esterno.
  • name: nome interno del segreto, particolarmente rilevante quando si combina external: true con segreti creati da un altro strumento.

Con i segreti puoi creare contenitori che necessitano di accesso a queste credenziali leggerli in modo controllato senza doverli lasciare visibili nel repository del codice o nel compose.yaml stesso, rafforzando notevolmente la sicurezza delle distribuzioni.

Lavorare con più file Componi e includi

Nei progetti di grandi dimensioni, non è raro che l'applicazione sia suddivisa in più servizi, a volte gestiti da team diversi. In questi casi, è pratico... separare la configurazione in più file Compose per modularizzare meglio l'architettura.

Un approccio tipico è quello di avere un compose.yaml file principale per l'applicazione e altri file per parti dell'infrastruttura. Ad esempio, è possibile sposta la definizione di Redis o altri servizi di supporto file infra.yaml e mantieni nella composizione principale solo ciò che riguarda direttamente la tua app.

Per fare questo si crea il file infra.yaml con la sua sezione services dove lasci, ad esempio, il servizio Redis completo. Quindi, nel tuo compose.yaml principale, aggiungi una sezione include che punta al file infra.yaml.

Quando corri docker compose up Dalla directory del progetto, Componi Combina entrambi i file e richiama tutti i servizi come se fossero in un unico YAML, ma la logica rimane comunque separata e più organizzata.

Questa tecnica semplifica la gestione dei file Compose da parte di diversi team e l'assemblaggio dell'applicazione globale tramite include, il che è molto utile in architetture con decine di contenitori o ambienti con molte infrastrutture condivise.

Comandi essenziali di Docker Compose

Sebbene Compose abbia un buon catalogo di comandi, nel lavoro quotidiano la maggior parte delle persone usa una manciata di loro su base ricorrenteÈ importante padroneggiarli perché sono ciò che definisce il tuo flusso di lavoro.

La cosa più importante è docker compose upQuesto comando crea le immagini necessarie (se non esistono già), crea i contenitori, configura reti e volumi e avvia tutti i servizi definiti nel file Compose. È il comando da utilizzare quando si desidera avviare lo stack.

Di solito è abbinato all'opzione -d per eseguirlo in modalità "distaccata"Cioè, in background. In questo modo non si riempie il terminale di log e si può continuare a utilizzare quella sessione per altri comandi. Ad esempio: docker compose up -d.

Per fermarti e ripulire ciò che hai sollevato, usi docker compose downche arresta e rimuove contenitori, reti e, facoltativamente, immagini e volumi associati. Due flag molto comuni in questo caso sono --rmi (per eliminare le immagini) e -v (per rimuovere i volumi definiti nella sezione volumes).

Se vuoi vedere quali contenitori fanno parte del progetto e qual è il loro stato, puoi eseguire docker compose psQuesto elenca ogni servizio, il suo stato (attivo, uscito, ecc.) e le porte esposte, il che è molto utile per verificare che tutto funzioni correttamente dopo un up.

Quando avvii lo stack in modalità distaccata, i log non vengono visualizzati nel terminale. Per visualizzarli, devi usare... docker compose logssia globalmente che filtrando per servizio. Il flag -f Permette di tracciare i log in tempo reale, molto utile per eseguire il debug di un servizio specifico senza dover accedere all'interno del contenitore.

Flusso di lavoro tipico: definire compose.yaml, eseguire un docker compose up -d, controlla con docker compose ps, rivedere i registri con docker compose logs -f <servicio> Se qualcosa va storto, e quando hai finito, usa docker compose down per lasciare tutto pulito.

Se mai ti perdessi, docker compose --help Ti mostra l'elenco dei sottocomandi e delle opzioni disponibili per aiutarti a ricordare a cosa serve ogni cosa senza dover consultare la documentazione.

Alla luce di tutto quanto sopra, strumento chiave Per chiunque lavori con i container al di là dei singoli progetti, Compose è uno strumento eccezionale. Permette di sviluppare direttamente in un ambiente molto simile (o identico) a quello di produzione, controllare servizi, reti e dati da un semplice file YAML, evitando una serie di problemi di compatibilità e distribuzione che inevitabilmente sorgono quando si lavora solo "localmente" senza container. Una volta che ci si abitua a scrivere un buon file YAML in Compose per i propri progetti, è difficile tornare indietro.