Domina l'infrastructure-as-code creando deployment di container riproducibili con Terraform su Docker. Impara la pipeline, dalla configurazione del provider all'automazione CI/CD.
Ho passato innumerevoli ore a gestire manualmente container Docker tra ambienti di sviluppo e produzione. Configurazioni non allineate, stati che divergevano e l'incertezza sul fatto che il mio setup locale corrispondesse davvero alla produzione funestavano ogni deployment. Quando ho scoperto il provider Docker di Terraform, è cambiato tutto.
In questo tutorial ti guiderò nell'integrare Terraform con Docker per creare un'infrastruttura riproducibile e versionata. Imparerai a effettuare il provisioning di container, gestire network e volumi, applicare vincoli di risorse e mantenere lo state condiviso nel team.
Che tu stia gestendo server di build o orchestrando microservizi, apri il terminale e cambiamo il modo in cui gestisci l'infrastruttura Docker.
Se sei alle prime armi con Docker, ti consiglio di iniziare dal nostro corso Introduzione a Docker.
Cos'è l'integrazione Terraform Docker?
Terraform è uno strumento di infrastructure-as-code che gestisce il ciclo di vita delle risorse tramite un flusso dichiarativo. Invece di eseguire singoli comandi Docker, definisci lo stato desiderato dell'infrastruttura in file di configurazione. Terraform poi esegue il ciclo plan-apply-destroy: calcola le modifiche necessarie, mostra un'anteprima e le applica solo dopo approvazione.
Il provider Terraform Docker funge da livello di traduzione tra il codice in HashiCorp Configuration Language (HCL) e le API Docker. Quando scrivi resource “docker_container" in una configurazione, il provider converte quella dichiarazione nelle chiamate API Docker appropriate. Questa astrazione significa che le risorse Docker possono essere gestite con lo stesso workflow usato per l'infrastruttura cloud.
Compose gestisce i container con un approccio basato su progetto e file YAML, ma non mantiene un file di state che tracci ciò che esiste realmente.
Terraform, al contrario, registra ogni risorsa che crea in terraform.tfstate, permettendogli di rilevare la deriva tra configurazione e realtà. Se qualcuno modifica manualmente un container, Terraform se ne accorge e può ripristinarlo allo stato definito.
Questo approccio brilla in diversi scenari:
Per server di build riproducibili, puoi distruggere e ricreare l'intero ambiente con un solo comando, garantendo coerenza.
Negli ambienti di test, puoi avviare un'infrastruttura isolata per ogni esecuzione.
Per deployment immutabili, sostituisci interamente i container invece di modificarli in-place, riducendo i rischi di deployment.
Prerequisiti e installazione
Prima di immergerci nelle configurazioni Terraform, verifichiamo che gli strumenti necessari siano installati e configurati correttamente.
Soddisfare i requisiti di sistema
Per prima cosa, verifica che Docker Engine sia in esecuzione sulla tua macchina.
Linux: Controlla con docker ps
macOS o Windows: Assicurati che Docker Desktop sia installato e in esecuzione
Il provider Terraform necessita di accesso al demone Docker, quindi devi confermare che il tuo utente abbia i permessi per accedere alla socket Unix /var/run/docker.sock su Linux o alla named pipe su Windows.
Successivamente, installa la CLI di Terraform dal sito ufficiale di HashiCorp. L'installazione di Terraform varia in base al sistema operativo:
Linux: Scarica il pacchetto appropriato o usa il gestore pacchetti della tua distribuzione.
macOS: Usa Homebrew. Per prima cosa, aggiungi il tap di HashiCorp con brew tap hashicorp/tap, poi installa Terraform con brew install hashicorp/tap/terraform.
Windows: Scarica il binario da hashicorp.com e aggiungilo al PATH, oppure usa Chocolatey: choco install terraform.
Una volta installato, verifica l'installazione eseguendo terraform version.
Verificati Docker e Terraform, il passo successivo è organizzare i file di progetto per la manutenibilità.
Configurare la struttura del progetto
I progetti Terraform beneficiano di un approccio chiaro e strutturato. Di solito creo una directory dedicata per il progetto Terraform e inizializzo il versionamento:
mkdir terraform-docker-project
cd terraform-docker-project
git initOfferto da
Il controllo di versione è fondamentale per l'infrastructure-as-code. Ogni modifica all'infrastruttura Docker dovrebbe essere tracciata, revisionata e reversibile. Normalmente aggiungo la working directory e i file di state di Terraform al file .gitignore per escluderli dal versionamento:
.terraform/
*.tfstate
*.tfstate.backup
.terraform.lock.hclOfferto da
Infine, creo un file vuoto main.tf in cui definirò la mia infrastruttura. Questo file conterrà la configurazione del provider e le definizioni delle risorse.
Provider Docker di Terraform e risorse core
Con la struttura del progetto pronta, è il momento di configurare il provider Docker e iniziare a definire le risorse.
Inizializzare il provider Docker
Per prima cosa, aggiungendo la configurazione del provider a main.tf:
Il blocco required_providers specifica da dove Terraform deve scaricare il plugin del provider. Usare vincoli di versione (~> 3.0) consente aggiornamenti minori evitando breaking change. Per host Docker remoti, puoi cambiare il parametro host in qualcosa come tcp://192.168.1.100:2376.
Ora esegui terraform init nella root del progetto. Questo scarica il plugin del provider Docker e lo salva in .terraform/providers/. Il comando mostra l'esatta versione installata e Terraform crea un file di lock per garantire versioni coerenti del provider all'interno del team.
Gestire le immagini dei container
Con il provider inizializzato, il passo successivo è definire le immagini dei container. Prima di creare i container, devi gestire le immagini che useranno, quindi definisci una risorsa image:
resource "docker_image" "nginx" {
name = "nginx:1.29"
keep_locally = true
}Offerto da
Aggiungi questa risorsa subito dopo il blocco del provider. Il parametro keep_locally è importante perché impedisce a Terraform di rimuovere l'immagine quando eseguo terraform destroy. È utile quando più container condividono la stessa immagine o quando voglio preservare le immagini in cache tra i deployment.
Per i deployment in produzione, fissa le immagini col digest SHA256 per garantire l'immutabilità:
resource "docker_image" "nginx" {
name = "nginx@sha256:b60f25eb5acdd79172de1a289891d9cfe4fc3669daddeeb68619240cbe839d9f"
}Offerto da
Il digest può essere ottenuto dal registry dei container. Per immagini su Docker Hub come nginx, puoi trovarli sul sito ufficiale Docker nella sezione Tag. Usare i digest garantisce che venga distribuita sempre la stessa identica immagine binaria, anche se qualcuno pubblica una nuova immagine con lo stesso tag.
Effettuare il provisioning dei container Docker
Definite le immagini, il passo successivo è creare i container che le referenziano:
L'immagine è referenziata tramite docker_image.nginx.image_id. Questo crea una dipendenza implicita: Terraform non proverà a creare il container finché l'immagine non esiste. Il blocco ports mappa la porta interna 80 del container alla porta 8080 dell'host, equivalente a docker run -p 8080:80.
Le variabili d'ambiente vanno nella lista env come stringhe. Terraform ricreerà il container se cambia qualsiasi parametro, garantendo che l'infrastruttura in esecuzione corrisponda sempre alla configurazione.
A questo punto, esegui terraform plan per vedere cosa verrà creato, quindi terraform apply per effettuare il provisioning. Terraform creerà l'immagine e il container. Puoi verificarlo in Docker Desktop o eseguendo docker ps nel terminale.
Networking, storage e limiti di risorse in Terraform
Ora che hai un container di base in esecuzione e hai compreso il workflow principale, espandiamo l'infrastruttura con funzionalità pronte per la produzione. Le prossime sezioni costruiscono su queste basi aggiungendo networking, storage persistente e vincoli di risorse, tutti elementi essenziali per i deployment reali
Gestire il networking
Per abilitare la comunicazione tra container mantenendo l'isolamento, è fondamentale creare network dedicati. Ecco come creare network isolati per controllare la connettività dei container. Aggiungi la seguente risorsa al file main.tf:
resource "docker_network" "app_network" {
name = "application_net"
driver = "bridge"
}Offerto da
Ora aggiungi un secondo container che usa questa rete. Inseriscilo dopo il container web:
resource "docker_container" "api" {
name = "api-server"
image = docker_image.nginx.image_id
networks_advanced {
name = docker_network.app_network.name
}
}Offerto da
Questo crea una rete bridge dedicata e collega il container api ad essa. I container sulla stessa rete possono comunicare usando i loro nomi come hostname, perfetto per architetture a microservizi.
Verifica l'isolamento della rete eseguendo docker network inspect application_net e confermando che siano connessi solo i container previsti. Assicurati di eseguire questo comando dopo aver creato le risorse con terraform plan e terraform apply.
Configurato il networking per la comunicazione tra container, l'aspetto critico successivo è la persistenza dei dati.
Configurare volumi persistenti
Per i dati che devono sopravvivere ai riavvii dei container, aggiungi risorse volume. Inseriscile prima delle definizioni dei container:
resource "docker_volume" "db_data" {
name = "postgres_data"
}Offerto da
Ora aggiungi un container database che usa questo volume:
I volumi gestiti come questo sono creati e controllati da Docker, archiviati nella directory dati di Docker. Per condividere file di configurazione dall'host, usa i bind mount specificando host_path invece di volume_name.
Dopo aver garantito che i dati sopravvivano ai riavvii dei container, è altrettanto importante evitare che un singolo container monopolizzi le risorse di sistema.
Impostare i vincoli di risorse
Oltre allo storage, controllare quanta CPU e memoria può consumare ciascun container è fondamentale per la stabilità del sistema. Per impedire consumi eccessivi, applica limiti di risorse.
Sebbene Docker consenta di aggiornare alcuni limiti in-place, il modello dichiarativo di Terraform spesso richiede la sostituzione del container per garantire che lo state corrisponda alla configurazione. Per evitare downtime durante questa sostituzione, usiamo la regola di lifecycle create_before_destroy.
Importante: Dobbiamo rimuovere l'argomento name fisso per evitare conflitti di denominazione durante la sostituzione. Un'opzione è usare l'argomento name_prefix.
Il parametro memory limita la RAM in megabyte. Il parametro cpu_shares funziona in modo diverso: è un peso relativo usato quando c'è contesa CPU. Un container con 1024 share otterrà il doppio del tempo CPU rispetto a uno con 512, ma solo quando l'host è sotto carico.
Per limiti CPU rigidi, puoi anche usare cpu_set per vincolare i container a core CPU specifici:
Convalida questi vincoli eseguendo docker inspect background-worker --format='{{.HostConfig.Memory}} {{.HostConfig.CpuShares}} e verificando che i valori di Memory e NanoCpus corrispondano alla configurazione.
Anche qui, come indicato sopra, devi prima eseguire terraform plan e terraform apply per creare le risorse.
A questo punto, il file main.tf contiene la configurazione del provider, un'immagine, una rete, un volume e tre container con varie configurazioni.
Variabili, moduli e secret in Terraform
Con l'aumentare dell'infrastruttura, i valori hardcoded diventano poco flessibili. Questa sezione mostra come rendere la configurazione riutilizzabile con variabili, moduli e una corretta gestione dei secret.
Usare variabili di input
Il primo passo per rendere flessibili le configurazioni è estrarre i valori hardcoded in variabili, modificabili senza toccare il codice principale. Puoi farlo creando un nuovo file chiamato variables.tf nella stessa directory:
variable "web_port" {
description = "External port for web container"
type = number
default = 8080
validation {
condition = var.web_port > 1024 && var.web_port < 65535
error_message = "Port must be between 1024 and 65535."
}
}Offerto da
Ora aggiorna il container web in main.tf per usare questa variabile:
Quando esegui terraform apply, Terraform leggerà automaticamente i valori da questo file. In alternativa, puoi passare i valori da riga di comando: terraform apply -var="web_port=9000".
Creare moduli riutilizzabili
Le variabili risolvono il problema dei valori hardcoded, ma se devi distribuire lo stesso schema intero più volte, completo di tutte le sue risorse?
Qui i moduli eccellono. Pensa a un modulo come a un blueprint che raggruppa tutti quei pezzi in un unico pacchetto, permettendoti di "stampare" quell'intero setup complesso come un'unità unica.
Inizia creando la struttura delle directory del modulo:
Infine, crea un file di output modules/web-app/outputs.tf, usato principalmente per mostrare in terminale il valore desiderato dopo l'esecuzione di terraform apply:
output "container_ip" {
value = docker_container.app.network_data[0].ip_address
description = "IP address of the application container"
}Offerto da
Dopo aver aggiunto le definizioni del modulo, esegui terraform init per installare i moduli, poi terraform apply per creare entrambi i container dallo stesso template. Dovresti vedere tutte le risorse in Docker Desktop.
Definiti pattern di infrastruttura riutilizzabili tramite moduli, rimane un aspetto critico della gestione della configurazione: trattare in sicurezza informazioni sensibili come password e API key.
Gestire i dati sensibili
Valori sensibili, come le password del database, non vanno hardcodati. Puoi invece usare il file variables.tf e impostare sensitive = true:
variable "db_password" {
description = "Database root password"
type = string
sensitive = true
}Offerto da
Ora aggiorna il container del database in main.tf per referenziare la password del database:
Invece di inserire la password in terraform.tfvars e hardcodarla, impostala come variabile d'ambiente:
exportTF_VAR_db_password="your-secure-password"
terraform applyOfferto da
Il flag sensitive = true impedisce a Terraform di mostrare il valore in output a console.
Tuttavia, questi valori sono memorizzati in chiaro in terraform.tfstate, quindi per mettere al sicuro questo file possiamo proteggerlo con backend remoti con cifratura. Vediamo come farlo.
Gestione dello state di Terraform e integrazione CI/CD
Questa sezione copre le meccaniche dello state e i flussi di automazione essenziali per i deployment in produzione. Ma prima di implementare l'automazione, è essenziale capire come Terraform tiene traccia della tua infrastruttura tramite il suo file di state.
Comprendere la meccanica dello state
Lo state è la base della capacità di Terraform di gestire l'infrastruttura. Il file terraform.tfstate mappa la configurazione agli ID reali dei container Docker e agli attributi delle risorse. Quando esegui terraform apply, Terraform confronta questo state con la configurazione e calcola il minimo cambiamento necessario.
Ispeziona lo state corrente con terraform show, che mostra ogni risorsa gestita. Per i team, gli state file locali creano problemi, conflitti e potenziali perdite di dati. La soluzione sono i backend remoti.
Per workflow focalizzati su Docker senza infrastruttura cloud, il livello gratuito di Terraform Cloud è l'ideale. Aggiungi un blocco backend alla sezione terraform in main.tf:
Crea un account gratuito sul sito di Terraform. Quindi esegui terraform login per autenticarti con il token fornito, e poi terraform init per migrare lo state. Questo fornisce il lock dello state in un workspace remoto e collaborazione di team senza infrastruttura cloud.
Tuttavia, è anche possibile creare un backend remoto su provider cloud come AWS o GCP.
Automatizzare i workflow
Con la gestione dello state configurata e archiviata in remoto, l'infrastruttura è pronta per la collaborazione in team. L'ultimo tassello è automatizzare il processo di deployment tramite pipeline CI/CD, assicurando modifiche coerenti e revisionabili.
Per prima cosa, crea .github/workflows/terraform.yml:
Questo workflow esegue terraform plan su ogni pull request, mostrando ai revisori quali cambiamenti infrastrutturali avverranno. Quando le modifiche vengono unite su main, terraform apply si esegue automaticamente, garantendo che ogni modifica all'infrastruttura segua lo stesso processo di revisione del codice.
Risoluzione dei problemi con Terraform Docker
Anche con una configurazione accurata possono emergere problemi. Capire gli errori comuni e quando considerare alternative aiuta a prendere decisioni architetturali migliori.
“Permission denied”
L'errore più comune su Linux è "permission denied" quando ci si connette alla socket Docker. Succede quando l'utente non ha i permessi per accedere a /var/run/docker.sock. Risolvilo aggiungendo l'utente al gruppo docker:
Su Windows con WSL2, sorgono problemi di connettività perché il percorso della socket differisce. Assicurati di abilitare l'impostazione di Docker Desktop "Expose daemon on tcp://localhost:2375 without TLS", quindi aggiorna il provider in main.tf:
provider "docker" {
host = "tcp://localhost:2375"
}Offerto da
Errori di autenticazione con registry privati
Per registry Docker privati, aggiungi l'autenticazione al blocco del provider, definisci le credenziali come variabili e impostale tramite variabili d'ambiente per evitare problemi dovuti all'hardcoding.
Oltre alla risoluzione dei problemi, è altrettanto utile capire quando altri strumenti potrebbero essere più adatti di Terraform al tuo caso d'uso specifico.
Terraform vs Kubernetes
Kubernetes offre funzionalità di orchestrazione molto più sofisticate come:
Self-healing
Scheduling avanzato
Service discovery
Load balancing integrato
Per deployment complessi multi-nodo con requisiti di alta disponibilità, Kubernetes è la scelta migliore. Terraform può effettuare il provisioning dell'infrastruttura Kubernetes, mentre Helm o i manifest di Kubernetes gestiscono il deployment delle applicazioni.
Terraform vs Ansible
Ansible eccelle nella configurazione all'interno dei container. Mentre Terraform fa il provisioning dell'infrastruttura, Ansible gestisce la configurazione software e l'installazione dei pacchetti. Spesso i due strumenti lavorano insieme: Terraform crea i container Docker, poi Ansible configura le applicazioni al loro interno.
Terraform vs Docker Compose
Per semplici ambienti di sviluppo locale, Docker Compose rimane la scelta più pragmatica. Terraform brilla per deployment riproducibili tra ambienti diversi e quando serve integrazione con altri componenti infrastrutturali.
Per aiutarti a decidere quale scegliere, ho riassunto le differenze più importanti in questa tabella:
Deployment complessi multi-nodo che richiedono High Availability.
Complementare: Terraform fornisce l'infrastruttura (il cluster), mentre Kubernetes gestisce il deployment delle applicazioni.
Ansible
Configuration management, installazione pacchetti e setup software.
Gestire software e configurazione all'interno dei container.
Complementare: Terraform crea i container Docker e Ansible configura le applicazioni al loro interno.
Docker Compose
Gestione semplice e pragmatica basata su progetto.
Semplici ambienti di sviluppo locale.
Alternativa: Compose è più semplice per lo sviluppo locale, ma Terraform è migliore per deployment riproducibili tra ambienti diversi.
Se stai cercando uno strumento per sostituire Docker, dai un'occhiata alla nostra guida sulle migliori alternative a Docker.
Conclusione
Portare Docker sotto la gestione del ciclo di vita di Terraform trasforma i deployment dei container da procedure manuali a infrastrutture riproducibili e versionate. In questo tutorial ti ho mostrato come costruire un'infrastruttura completa passo dopo passo, partendo da un provider e un singolo container, aggiungendo poi networking, storage, variabili, moduli e automazione.
La distinzione chiave è che Terraform gestisce provisioning, creazione, aggiornamento e distruzione delle risorse sulla base di una configurazione dichiarativa. La piena orchestrazione con scalabilità automatica e self-healing richiede strumenti come Kubernetes o Docker Swarm. Terraform può effettuare il provisioning di questi orchestrator, ma non li sostituisce.
Come impedisco a Terraform di rimuovere le immagini Docker quando eseguo terraform destroy?⌄
Usa il parametro keep_locally = true nella risorsa docker_image. Questo indica a Terraform di mantenere l'immagine in cache localmente anche dopo la distruzione dell'infrastruttura, utile quando più container condividono la stessa immagine o quando vuoi preservare le immagini in cache tra i deployment.
Perché Terraform Cloud restituisce errori "Cannot connect to Docker daemon"?⌄
Per impostazione predefinita, Terraform Cloud esegue in modalità di esecuzione remota, il che significa che i comandi vengono eseguiti sui server di Terraform Cloud dove Docker non è accessibile. Devi configurare il workspace per usare la modalità di esecuzione locale: vai su Settings > General > ExecutionMode > Local. Questo conserva lo state in remoto ma esegue i comandi Terraform sulla tua macchina locale, dove Docker è accessibile.
Come posso gestire in Terraform valori sensibili come le password del database?⌄
Contrassegna le variabili come sensibili usando sensitive = true nella definizione della variabile. Per Terraform Cloud, imposta le variabili sensibili nella UI del workspace e marcate come "Sensitive". Per l'esecuzione locale, usa variabili d'ambiente con il prefisso TF_VAR_ (ad es., export TF_VAR_db_password="password"). Nota che i valori sono comunque salvati in chiaro in terraform.tfstate, quindi metti sempre in sicurezza il file di state.
Qual è la differenza tra Terraform e Docker Compose per gestire i container?⌄
Docker Compose gestisce i container con un approccio YAML basato su progetto senza mantenere file di state. Terraform registra ogni risorsa in terraform.tfstate, permettendo di rilevare la deriva tra configurazione e realtà. Terraform eccelle nei deployment riproducibili tra ambienti e nell'integrazione con altra infrastruttura, mentre Docker Compose è più semplice per lo sviluppo locale. Terraform offre gestione esplicita dello state e controllo del ciclo di vita (plan/apply/destroy).
Come aggiorno i limiti di risorse dei container Docker senza errori?⌄
Mentre la CLI di Docker supporta aggiornamenti in-place, il provider Docker di Terraform spesso forza la sostituzione della risorsa per garantire la coerenza dello state. Per evitare downtime durante questa sostituzione, aggiungi un blocco lifecycle { create_before_destroy = true }. Fondamentale, devi anche rimuovere l'argomento name fisso (o usare name_prefix) dalla definizione della risorsa. Se mantieni un nome fisso come name = "web-server", il nuovo container non riuscirà ad avviarsi con un errore "name already in use" prima che quello vecchio venga distrutto.
Come fondatore di Martin Data Solutions e Data Scientist/ML & AI Engineer freelance, porto un portfolio vario che include Regression, Classification, NLP, LLM, RAG, reti neurali, metodi ensemble e computer vision.
Ho sviluppato con successo diversi progetti ML end-to-end, includendo data cleaning, analytics, modellazione e deployment su AWS e GCP, offrendo soluzioni efficaci e scalabili.
Ho realizzato applicazioni web interattive e scalabili con Streamlit e Gradio per casi d’uso in settori diversi.
Ho insegnato e fatto mentoring a studenti di data science e analytics, supportandone la crescita professionale con approcci di apprendimento personalizzati.
Ho progettato contenuti didattici per applicazioni di retrieval-augmented generation (RAG) su misura per le esigenze enterprise.
Ho scritto articoli tecnici di grande impatto su AI & ML, trattando temi come MLOps, database vettoriali e LLM, ottenendo un notevole coinvolgimento.
In ogni progetto che affronto, applico pratiche aggiornate di ingegneria del software e DevOps, come CI/CD, linting del codice, formattazione, monitoraggio dei modelli, tracciamento degli esperimenti e gestione robusta degli errori. Mi impegno a fornire soluzioni complete, trasformando gli insight dai dati in strategie pratiche che aiutano le aziende a crescere e a sfruttare al meglio data science, machine learning e AI.
Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Ho passato innumerevoli ore a gestire manualmente container Docker tra ambienti di sviluppo e produzione. Configurazioni non allineate, stati che divergevano e l'incertezza sul fatto che il mio setup locale corrispondesse davvero alla produzione funestavano ogni deployment. Quando ho scoperto il provider Docker di Terraform, è cambiato tutto.
In questo tutorial ti guiderò nell'integrare Terraform con Docker per creare un'infrastruttura riproducibile e versionata. Imparerai a effettuare il provisioning di container, gestire network e volumi, applicare vincoli di risorse e mantenere lo state condiviso nel team.
Che tu stia gestendo server di build o orchestrando microservizi, apri il terminale e cambiamo il modo in cui gestisci l'infrastruttura Docker.
Se sei alle prime armi con Docker, ti consiglio di iniziare dal nostro corso Introduzione a Docker.
Cos'è l'integrazione Terraform Docker?
Terraform è uno strumento di infrastructure-as-code che gestisce il ciclo di vita delle risorse tramite un flusso dichiarativo. Invece di eseguire singoli comandi Docker, definisci lo stato desiderato dell'infrastruttura in file di configurazione. Terraform poi esegue il ciclo plan-apply-destroy: calcola le modifiche necessarie, mostra un'anteprima e le applica solo dopo approvazione.
Il provider Terraform Docker funge da livello di traduzione tra il codice in HashiCorp Configuration Language (HCL) e le API Docker. Quando scrivi
resource “docker_container"in una configurazione, il provider converte quella dichiarazione nelle chiamate API Docker appropriate. Questa astrazione significa che le risorse Docker possono essere gestite con lo stesso workflow usato per l'infrastruttura cloud.Ecco dove Terraform si differenzia da Docker Compose.
Compose gestisce i container con un approccio basato su progetto e file YAML, ma non mantiene un file di state che tracci ciò che esiste realmente.
Terraform, al contrario, registra ogni risorsa che crea in
terraform.tfstate, permettendogli di rilevare la deriva tra configurazione e realtà. Se qualcuno modifica manualmente un container, Terraform se ne accorge e può ripristinarlo allo stato definito.Questo approccio brilla in diversi scenari:
Prerequisiti e installazione
Prima di immergerci nelle configurazioni Terraform, verifichiamo che gli strumenti necessari siano installati e configurati correttamente.
Soddisfare i requisiti di sistema
Per prima cosa, verifica che Docker Engine sia in esecuzione sulla tua macchina.
Linux: Controlla con
docker psmacOS o Windows: Assicurati che Docker Desktop sia installato e in esecuzione
Il provider Terraform necessita di accesso al demone Docker, quindi devi confermare che il tuo utente abbia i permessi per accedere alla socket Unix
/var/run/docker.socksu Linux o alla named pipe su Windows.Successivamente, installa la CLI di Terraform dal sito ufficiale di HashiCorp. L'installazione di Terraform varia in base al sistema operativo:
Linux: Scarica il pacchetto appropriato o usa il gestore pacchetti della tua distribuzione.
macOS: Usa Homebrew. Per prima cosa, aggiungi il tap di HashiCorp con
brew tap hashicorp/tap, poi installa Terraform conbrew install hashicorp/tap/terraform.Windows: Scarica il binario da hashicorp.com e aggiungilo al PATH, oppure usa Chocolatey:
choco install terraform.Una volta installato, verifica l'installazione eseguendo
terraform version.Verificati Docker e Terraform, il passo successivo è organizzare i file di progetto per la manutenibilità.
Configurare la struttura del progetto
I progetti Terraform beneficiano di un approccio chiaro e strutturato. Di solito creo una directory dedicata per il progetto Terraform e inizializzo il versionamento:
Il controllo di versione è fondamentale per l'infrastructure-as-code. Ogni modifica all'infrastruttura Docker dovrebbe essere tracciata, revisionata e reversibile. Normalmente aggiungo la working directory e i file di state di Terraform al file
.gitignoreper escluderli dal versionamento:Infine, creo un file vuoto
main.tfin cui definirò la mia infrastruttura. Questo file conterrà la configurazione del provider e le definizioni delle risorse.Provider Docker di Terraform e risorse core
Con la struttura del progetto pronta, è il momento di configurare il provider Docker e iniziare a definire le risorse.
Inizializzare il provider Docker
Per prima cosa, aggiungendo la configurazione del provider a
main.tf:Il blocco
required_providersspecifica da dove Terraform deve scaricare il plugin del provider. Usare vincoli di versione (~> 3.0) consente aggiornamenti minori evitando breaking change. Per host Docker remoti, puoi cambiare il parametrohostin qualcosa cometcp://192.168.1.100:2376.Ora esegui
terraform initnella root del progetto. Questo scarica il plugin del provider Docker e lo salva in.terraform/providers/. Il comando mostra l'esatta versione installata e Terraform crea un file di lock per garantire versioni coerenti del provider all'interno del team.Gestire le immagini dei container
Con il provider inizializzato, il passo successivo è definire le immagini dei container. Prima di creare i container, devi gestire le immagini che useranno, quindi definisci una risorsa image:
Aggiungi questa risorsa subito dopo il blocco del provider. Il parametro
keep_locallyè importante perché impedisce a Terraform di rimuovere l'immagine quando eseguoterraform destroy. È utile quando più container condividono la stessa immagine o quando voglio preservare le immagini in cache tra i deployment.Per i deployment in produzione, fissa le immagini col digest SHA256 per garantire l'immutabilità:
Il digest può essere ottenuto dal registry dei container. Per immagini su Docker Hub come nginx, puoi trovarli sul sito ufficiale Docker nella sezione Tag. Usare i digest garantisce che venga distribuita sempre la stessa identica immagine binaria, anche se qualcuno pubblica una nuova immagine con lo stesso tag.
Effettuare il provisioning dei container Docker
Definite le immagini, il passo successivo è creare i container che le referenziano:
L'immagine è referenziata tramite
docker_image.nginx.image_id. Questo crea una dipendenza implicita: Terraform non proverà a creare il container finché l'immagine non esiste. Il bloccoportsmappa la porta interna 80 del container alla porta 8080 dell'host, equivalente adocker run -p 8080:80.Le variabili d'ambiente vanno nella lista
envcome stringhe. Terraform ricreerà il container se cambia qualsiasi parametro, garantendo che l'infrastruttura in esecuzione corrisponda sempre alla configurazione.A questo punto, esegui
terraform planper vedere cosa verrà creato, quinditerraform applyper effettuare il provisioning. Terraform creerà l'immagine e il container. Puoi verificarlo in Docker Desktop o eseguendodocker psnel terminale.Networking, storage e limiti di risorse in Terraform
Ora che hai un container di base in esecuzione e hai compreso il workflow principale, espandiamo l'infrastruttura con funzionalità pronte per la produzione. Le prossime sezioni costruiscono su queste basi aggiungendo networking, storage persistente e vincoli di risorse, tutti elementi essenziali per i deployment reali
Gestire il networking
Per abilitare la comunicazione tra container mantenendo l'isolamento, è fondamentale creare network dedicati. Ecco come creare network isolati per controllare la connettività dei container. Aggiungi la seguente risorsa al file
main.tf:Ora aggiungi un secondo container che usa questa rete. Inseriscilo dopo il container
web:Questo crea una rete bridge dedicata e collega il container
apiad essa. I container sulla stessa rete possono comunicare usando i loro nomi come hostname, perfetto per architetture a microservizi.Verifica l'isolamento della rete eseguendo
docker network inspect application_nete confermando che siano connessi solo i container previsti. Assicurati di eseguire questo comando dopo aver creato le risorse conterraform planeterraform apply.Configurato il networking per la comunicazione tra container, l'aspetto critico successivo è la persistenza dei dati.
Configurare volumi persistenti
Per i dati che devono sopravvivere ai riavvii dei container, aggiungi risorse volume. Inseriscile prima delle definizioni dei container:
Ora aggiungi un container database che usa questo volume:
I volumi gestiti come questo sono creati e controllati da Docker, archiviati nella directory dati di Docker. Per condividere file di configurazione dall'host, usa i bind mount specificando
host_pathinvece divolume_name.Dopo aver garantito che i dati sopravvivano ai riavvii dei container, è altrettanto importante evitare che un singolo container monopolizzi le risorse di sistema.
Impostare i vincoli di risorse
Oltre allo storage, controllare quanta CPU e memoria può consumare ciascun container è fondamentale per la stabilità del sistema. Per impedire consumi eccessivi, applica limiti di risorse.
Sebbene Docker consenta di aggiornare alcuni limiti in-place, il modello dichiarativo di Terraform spesso richiede la sostituzione del container per garantire che lo state corrisponda alla configurazione. Per evitare downtime durante questa sostituzione, usiamo la regola di lifecycle
create_before_destroy.Importante: Dobbiamo rimuovere l'argomento
namefisso per evitare conflitti di denominazione durante la sostituzione. Un'opzione è usare l'argomentoname_prefix.Il parametro
memorylimita la RAM in megabyte. Il parametrocpu_sharesfunziona in modo diverso: è un peso relativo usato quando c'è contesa CPU. Un container con 1024 share otterrà il doppio del tempo CPU rispetto a uno con 512, ma solo quando l'host è sotto carico.Per limiti CPU rigidi, puoi anche usare
cpu_setper vincolare i container a core CPU specifici:Convalida questi vincoli eseguendo
docker inspect background-worker --format='{{.HostConfig.Memory}} {{.HostConfig.CpuShares}}e verificando che i valori di Memory e NanoCpus corrispondano alla configurazione.Anche qui, come indicato sopra, devi prima eseguire
terraform planeterraform applyper creare le risorse.A questo punto, il file
main.tfcontiene la configurazione del provider, un'immagine, una rete, un volume e tre container con varie configurazioni.Variabili, moduli e secret in Terraform
Con l'aumentare dell'infrastruttura, i valori hardcoded diventano poco flessibili. Questa sezione mostra come rendere la configurazione riutilizzabile con variabili, moduli e una corretta gestione dei secret.
Usare variabili di input
Il primo passo per rendere flessibili le configurazioni è estrarre i valori hardcoded in variabili, modificabili senza toccare il codice principale. Puoi farlo creando un nuovo file chiamato
variables.tfnella stessa directory:Ora aggiorna il container web in
main.tfper usare questa variabile:Il blocco di validazione impedisce l'uso di porte privilegiate. Per sovrascrivere il valore predefinito, crea un file
terraform.tfvars:Quando esegui
terraform apply, Terraform leggerà automaticamente i valori da questo file. In alternativa, puoi passare i valori da riga di comando:terraform apply -var="web_port=9000".Creare moduli riutilizzabili
Le variabili risolvono il problema dei valori hardcoded, ma se devi distribuire lo stesso schema intero più volte, completo di tutte le sue risorse?
Qui i moduli eccellono. Pensa a un modulo come a un blueprint che raggruppa tutti quei pezzi in un unico pacchetto, permettendoti di "stampare" quell'intero setup complesso come un'unità unica.
Inizia creando la struttura delle directory del modulo:
Ora crea il file
modules/web-app/variables.tfe aggiungi quanto segue:Quindi crea un file principale
modules/web-app/main.tfcon questa risorsa:Infine, crea un file di output
modules/web-app/outputs.tf, usato principalmente per mostrare in terminale il valore desiderato dopo l'esecuzione diterraform apply:Dopo aver aggiunto le definizioni del modulo, esegui
terraform initper installare i moduli, poiterraform applyper creare entrambi i container dallo stesso template. Dovresti vedere tutte le risorse in Docker Desktop.Gestire i dati sensibili
Valori sensibili, come le password del database, non vanno hardcodati. Puoi invece usare il file
variables.tfe impostaresensitive = true:Ora aggiorna il container del database in
main.tfper referenziare la password del database:Invece di inserire la password in
terraform.tfvarse hardcodarla, impostala come variabile d'ambiente:Il flag
sensitive = trueimpedisce a Terraform di mostrare il valore in output a console.Tuttavia, questi valori sono memorizzati in chiaro in
terraform.tfstate, quindi per mettere al sicuro questo file possiamo proteggerlo con backend remoti con cifratura. Vediamo come farlo.Gestione dello state di Terraform e integrazione CI/CD
Questa sezione copre le meccaniche dello state e i flussi di automazione essenziali per i deployment in produzione. Ma prima di implementare l'automazione, è essenziale capire come Terraform tiene traccia della tua infrastruttura tramite il suo file di state.
Comprendere la meccanica dello state
Lo state è la base della capacità di Terraform di gestire l'infrastruttura. Il file
terraform.tfstatemappa la configurazione agli ID reali dei container Docker e agli attributi delle risorse. Quando eseguiterraform apply, Terraform confronta questo state con la configurazione e calcola il minimo cambiamento necessario.Ispeziona lo state corrente con
terraform show, che mostra ogni risorsa gestita. Per i team, gli state file locali creano problemi, conflitti e potenziali perdite di dati. La soluzione sono i backend remoti.Per workflow focalizzati su Docker senza infrastruttura cloud, il livello gratuito di Terraform Cloud è l'ideale. Aggiungi un blocco backend alla sezione
terraforminmain.tf:Crea un account gratuito sul sito di Terraform. Quindi esegui
terraform loginper autenticarti con il token fornito, e poiterraform initper migrare lo state. Questo fornisce il lock dello state in un workspace remoto e collaborazione di team senza infrastruttura cloud.Tuttavia, è anche possibile creare un backend remoto su provider cloud come AWS o GCP.
Automatizzare i workflow
Con la gestione dello state configurata e archiviata in remoto, l'infrastruttura è pronta per la collaborazione in team. L'ultimo tassello è automatizzare il processo di deployment tramite pipeline CI/CD, assicurando modifiche coerenti e revisionabili.
Per prima cosa, crea
.github/workflows/terraform.yml:Questo workflow esegue
terraform plansu ogni pull request, mostrando ai revisori quali cambiamenti infrastrutturali avverranno. Quando le modifiche vengono unite su main,terraform applysi esegue automaticamente, garantendo che ogni modifica all'infrastruttura segua lo stesso processo di revisione del codice.Risoluzione dei problemi con Terraform Docker
Anche con una configurazione accurata possono emergere problemi. Capire gli errori comuni e quando considerare alternative aiuta a prendere decisioni architetturali migliori.
“Permission denied”
L'errore più comune su Linux è "permission denied" quando ci si connette alla socket Docker. Succede quando l'utente non ha i permessi per accedere a
/var/run/docker.sock. Risolvilo aggiungendo l'utente al gruppodocker:Poi esci e rientra nella sessione.
Problemi di connettività con WSL2
Su Windows con WSL2, sorgono problemi di connettività perché il percorso della socket differisce. Assicurati di abilitare l'impostazione di Docker Desktop "Expose daemon on tcp://localhost:2375 without TLS", quindi aggiorna il provider in
main.tf:Errori di autenticazione con registry privati
Per registry Docker privati, aggiungi l'autenticazione al blocco del provider, definisci le credenziali come variabili e impostale tramite variabili d'ambiente per evitare problemi dovuti all'hardcoding.
Alternative a Terraform
Oltre alla risoluzione dei problemi, è altrettanto utile capire quando altri strumenti potrebbero essere più adatti di Terraform al tuo caso d'uso specifico.
Terraform vs Kubernetes
Kubernetes offre funzionalità di orchestrazione molto più sofisticate come:
Per deployment complessi multi-nodo con requisiti di alta disponibilità, Kubernetes è la scelta migliore. Terraform può effettuare il provisioning dell'infrastruttura Kubernetes, mentre Helm o i manifest di Kubernetes gestiscono il deployment delle applicazioni.
Terraform vs Ansible
Ansible eccelle nella configurazione all'interno dei container. Mentre Terraform fa il provisioning dell'infrastruttura, Ansible gestisce la configurazione software e l'installazione dei pacchetti. Spesso i due strumenti lavorano insieme: Terraform crea i container Docker, poi Ansible configura le applicazioni al loro interno.
Terraform vs Docker Compose
Per semplici ambienti di sviluppo locale, Docker Compose rimane la scelta più pragmatica. Terraform brilla per deployment riproducibili tra ambienti diversi e quando serve integrazione con altri componenti infrastrutturali.
Per aiutarti a decidere quale scegliere, ho riassunto le differenze più importanti in questa tabella:
Strumento
Punti di forza principali
Use case ideale
Confronto con Terraform
Kubernetes
Orchestrazione sofisticata (self-healing, scheduling avanzato, load balancing).
Deployment complessi multi-nodo che richiedono High Availability.
Complementare: Terraform fornisce l'infrastruttura (il cluster), mentre Kubernetes gestisce il deployment delle applicazioni.
Ansible
Configuration management, installazione pacchetti e setup software.
Gestire software e configurazione all'interno dei container.
Complementare: Terraform crea i container Docker e Ansible configura le applicazioni al loro interno.
Docker Compose
Gestione semplice e pragmatica basata su progetto.
Semplici ambienti di sviluppo locale.
Alternativa: Compose è più semplice per lo sviluppo locale, ma Terraform è migliore per deployment riproducibili tra ambienti diversi.
Se stai cercando uno strumento per sostituire Docker, dai un'occhiata alla nostra guida sulle migliori alternative a Docker.
Conclusione
Portare Docker sotto la gestione del ciclo di vita di Terraform trasforma i deployment dei container da procedure manuali a infrastrutture riproducibili e versionate. In questo tutorial ti ho mostrato come costruire un'infrastruttura completa passo dopo passo, partendo da un provider e un singolo container, aggiungendo poi networking, storage, variabili, moduli e automazione.
La distinzione chiave è che Terraform gestisce provisioning, creazione, aggiornamento e distruzione delle risorse sulla base di una configurazione dichiarativa. La piena orchestrazione con scalabilità automatica e self-healing richiede strumenti come Kubernetes o Docker Swarm. Terraform può effettuare il provisioning di questi orchestrator, ma non li sostituisce.
Per i prossimi passi, esplora la documentazione sul Terraform Registry per il provider Docker per trovare opzioni avanzate come i servizi Docker per Swarm, la gestione dei secret e la configurazione dei plugin.
Se vuoi fare pratica con i container, ti consiglio di iscriverti al nostro percorso interattivo Containerization and Virtualization with Docker and Kubernetes.