Attraverso una combinazione di Remote Backends strutturati con cura, una progettazione attenta degli output e l'uso mirato della Data Source terraform_remote_state, è possibile stabilire un flusso di informazioni controllato tra diversi livelli tenant - il tutto senza compromettere l'isolamento dei singoli mandanti.
L'uso efficace del Remote State per lo scambio di informazioni tra unità organizzative richiede una configurazione ponderata dell'ambiente Terraform. Elemento centrale è la scelta e la configurazione di uno Storage Backend adeguato per la memorizzazione dei dati di stato nei cosiddetti State Files.
Configurazione di un Remote Backend
Terraform offre diversi backends ufficiali per la memorizzazione dei dati di stato. Le opzioni ufficialmente supportate da HashiCorp sono:
Esempio di configurazione | |
---|---|
HashiCorp Consul |
terraform { backend "consul" { address = "consul.example.com:8500" scheme = "https" path = "terraform/network/global" lock = true } } |
HashiCorp Terraform Enterprise |
terraform { backend "remote" { hostname = "terraform.example.com" organization = "ict-technology" workspaces { name = "network-global" } } } |
HashiCorp Terraform Cloud |
terraform { backend "remote" { hostname = "app.terraform.io" organization = "ict-technology" workspaces { name = "network-global" } } } |
Questi Storage Backends offrono diversi vantaggi rispetto ai file di stato locali:
- Memorizzazione centralizzata: Tutti i membri del team lavorano con lo stesso stato.
- Meccanismi di locking: Impedisce modifiche parallele e i conflitti correlati.
- Versionamento: Memorizza lo storico delle modifiche.
- Controllo degli accessi: Consente concetti di autorizzazione differenziati.
Nella scelta di un backend, dovreste considerare i requisiti della vostra organizzazione.
Consul è un'ottima scelta per le aziende che utilizzano la versione gratuita di Terraform.
Terraform Cloud e Terraform Enterprise invece adottano un'architettura client/server e dispongono già di uno Storage Backend integrato lato server. Pertanto, non è necessario preoccuparsi ulteriormente di questo aspetto e si può semplicemente utilizzare lo storage integrato. Terraform Cloud/Enterprise offrono inoltre funzionalità aggiuntive come Policy-as-Code per l'implementazione e l'applicazione di politiche, controlli di governance e funzionalità avanzate di collaborazione.
Esistono anche altri backends supportati da Terraform. Tra questi figurano backends come Amazon S3, HTTPS, diverse banche dati e altri ancora.
Tuttavia, c'è un aspetto critico da considerare: HashiCorp fornisce supporto solo per i file di stato locali, Consul e Terraform Cloud/Enterprise.
Potete utilizzare altri backends, ma in caso di problemi non potete aspettarvi alcun supporto o soluzione da HashiCorp, nemmeno con un contratto di supporto Enterprise esistente.
In ambienti di produzione critici, dovreste quindi utilizzare esclusivamente Consul o Terraform Cloud/Enterprise come Remote State Backends.
Se utilizzate un backend non supportato da HashiCorp, dovreste assolutamente assicurarvi che il backend offra un meccanismo di locking!
Altrimenti rischiate che, in caso di esecuzione parallela di un comando terraform apply, le istanze Terraform eseguite in parallelo sovrascrivano reciprocamente i file di stato, rendendo così l'intera infrastruttura incoerente. Ad esempio, backends come una condivisione NFSv3 o un backend HTTPS sono probabilmente scelte discutibili e rischiate perdite di dati.
Meccanismi di sicurezza
La memorizzazione dei file di stato è critica, poiché questi possono contenere informazioni sensibili come credenziali di accesso e dettagli sull'infrastruttura.
Cifratura
In Terraform Cloud/Enterprise i dati di stato vengono automaticamente cifrati sia a riposo che durante la trasmissione. In Consul dovreste abilitare TLS per la connessione e le funzionalità di cifratura integrate di Consul:
terraform { backend "consul" { address = "consul.example.com:8500" access_token = var.consul_acl_token scheme = "https" path = "terraform/network/global" ca_file = "/etc/ssl/certs/ca.pem" cert_file = "/etc/ssl/certs/cert.pem" key_file = "/etc/ssl/private/key.pem" } }
Controllo degli accessi
In Terraform Cloud/Enterprise la gestione degli utenti offre ruoli e permessi dettagliati. Ad esempio, ruoli differenti possono avere diversi privilegi:
- Team Network-Admins: Accesso completo al workspace network-global.
- Team App-Developers: Accesso in sola lettura al workspace network-global.
In Consul si utilizzano le ACL, che possono essere assegnate a un ruolo. Ecco, ad esempio, l'ACL che concede al team App-Developers il permesso di sola lettura:
# Consul ACL policy key_prefix "terraform/network/global" { policy = "read" }
In ambienti produttivi dovreste inoltre utilizzare un token ACL per impedire l'accesso non autorizzato da parte di altri client:
data "terraform_remote_state" "global_network" { backend = "consul" config = { address = "consul.example.com:8500" path = "terraform/network/global" scheme = "https" token = var.consul_acl_token } }
Versionamento e ripristino
Terraform Cloud/Enterprise crea automaticamente snapshot dei vostri state files dopo ogni esecuzione riuscita. Questi snapshot possono essere utilizzati per ripristinare lo stato di un ambiente in caso di errore. Utilizzate l'interfaccia utente o la CLI (terraform state pull) per visualizzare la cronologia delle versioni e ripristinare la versione desiderata.
In Consul è possibile configurare cluster ad alta disponibilità con repliche in altri data center per prevenire perdite di dati. È inoltre possibile creare snapshot dello storage Raft.
Struttura dei file di stato per una gestione ottimale del Multi-Tenancy
L'organizzazione degli state files dovrebbe riflettere la vostra struttura organizzativa e al tempo stesso supportare i flussi informativi.
In Terraform Cloud/Enterprise le diverse configurazioni sono organizzate in "Workspaces". In Consul la separazione avviene tramite "Paths", che corrispondono a uno spazio dei nomi logico.
Una struttura consolidata potrebbe apparire così:
Infrastruttura | Workspace/Path |
---|---|
Sistemi e servizi globali necessari | global-infrastructure |
Sedi regionali | region-{region_name} |
Infrastruttura specifica per cliente | customer-{customer_name} |
Infrastruttura specifica per team | team-{team_name} |
Infrastruttura specifica per applicazione | app-{app_name} |
Una struttura di questo tipo consente una chiara separazione delle responsabilità e facilita il ritrovamento degli state files rilevanti.
In Terraform Cloud/Enterprise potete inoltre utilizzare il Workspace-Tagging per raggruppare workspaces correlati. Questo vi consente, ad esempio, di stabilire una connessione logica tra l'infrastruttura di un'applicazione e quella del team associato.
Esempi di codice per l'ereditarietà dei dati tra tenant
L'effettiva ereditarietà dei dati avviene tramite la Data Source terraform_remote_state, che consente l'accesso agli Outputs da altre configurazioni Terraform.
Esempio: Configurazione di rete in un ambiente Multi-Tenant
Team di rete globale (nel Workspace global-infrastructure):
module "global_network" {
source = "./modules/network"
name = "global-vcn"
description = "Global customer network"
cidr_block = "10.0.0.0/16"
parent_id = var.root_compartment_id
}
output "global_vcn_id" {
value = module.global_network.id
}
Team regionale (nel Workspace region-dc1):
data "terraform_remote_state" "global_network" { backend = "remote" config = { organization = "ict-technology" workspaces = { name = "network-global" } } } module "regional_network" { source = "./modules/network" name = "regional-subnet-dc1" description = "Subnet of europe region" parent_id = data.terraform_remote_state.global_network.outputs.global_vcn_id cidr_block = "10.0.1.0/24" } output "regional_subnet_id" { value = module.regional_network.id }
Team cliente (nel Workspace customer-acme):
data "terraform_remote_state" "regional_network" { backend = "remote" config = { organization = "ict-technology" workspaces = { name = "region-europe" } } } module "customer_environment" { source = "./modules/environment" name = "customer-instance" description = "customer instance" parent_id = var.customer_compartment_id subnet_id = data.terraform_remote_state.regional_network.outputs.regional_subnet_id instance_shape = "VM.Standard2.1" }
Un modello particolarmente raccomandato è l'uso di Outputs strutturati. Invece di fornire molti valori singoli, un Output strutturato può esportare diverse informazioni raggruppate:
output "network_config" { value = { vcn_id = module.global_network.id cidr_block = module.global_network.cidr_block dns_label = module.global_network.dns_label subnets = { public = module.subnet_public.id private = module.subnet_private.id } } }
In questo modo una configurazione dipendente può accedere in modo mirato alle informazioni rilevanti:
module "app_server" { source = "./modules/compute" name = "app-server" description = "application server in private subnet" parent_id = var.customer_compartment_id subnet_id = data.terraform_remote_state.network.outputs.network_config.subnets.private instance_shape = "VM.Standard2.1" }
Nel prossimo capitolo esamineremo le insidie, le best practices e alcuni trucchi per la gestione dei Remote States.