Introduzione
Il linguaggio di configurazione di HashiCorp (HCL), che Terraform utilizza, offre molte strutture e capacità utili presenti in altre lingue di programmazione. L’utilizzo di cicli nel codice dell’infrastruttura può ridurre drasticamente la duplicazione del codice e aumentare la leggibilità, permettendo un refactoring futuro più semplice e una maggiore flessibilità. HCL offre anche alcune strutture dati comuni, come liste e mappe (chiamate rispettivamente array e dizionari in altre lingue), nonché condizioni per il ramo delle dipendenze dell’esecuzione.
Unico a Terraform è il能力 di specificare manualmente le risorse a cui si deve una dipendenza. Anche se il grafo di esecuzione che costruisce quando si esegue il codice già contiene le connessioni rilevate (che sono corrette in molti scenari), potreste trovarvi in necessità di forzare una relazione di dipendenza che Terraform non è in grado di rilevare.
In questo articolo, review le strutture dati fornite da HCL, le sue funzionalità di iterazione per le risorse (la chiave count
, for_each
e for
), le condizioni per la gestione di valori noti e sconosciuti, e le relazioni di dipendenza tra le risorse.
Prerequisiti
- Un Token di Acceso Personale di DigitalOcean, che puoi creare tramite il pannello di controllo di DigitalOcean. Trovi le istruzioni nel documento prodotti DigitalOcean, Come Creare un Token di Acceso Personale.
- Terraform installato su tuo computer locale e un progetto impostato con il provider DigitalOcean. Completa Step 1 e Step 2 della guida Come Usare Terraform con DigitalOcean, e sicuramente nomina la cartella del progetto
terraform-flexibility
, al posto diloadbalance
. Durante Step 2, non devi includere la variabilepvt_key
e i risorse per le chiavi SSH quando configuri il provider.
Nota: Questo tutorial è stato testato specificamente con Terraform 1.0.2
.
Tipi di dati in HCL
Prima di imparare più sulle loop e altri featuti di HCL che rendono il tuo codice più flessibile, passeròi alla descrizione dei tipi di dati disponibili e loro usi.
Il linguaggio di configurazione di HashiCorp supporta tipi primitivi e tipi complessi. I tipi primitivi sono stringhe, numeri e valori booleani, che sono i tipi base che non possono essere ricavati da altri. I tipi complessi, invece, raggruppano molti valori in uno solo. I due tipi di valori complessi sono i tipi strutturali e i tipi di collezione.
I tipi strutturali consentono di raggruppare valori di tipi differenti. L’esempio principale sono le definizioni delle risorse che usi per specificare come apparirà la tua infrastruttura. Confrontati con i tipi strutturali, i tipi di collezione raggruppano anche loro valori, ma solo di tipo identico. I tre tipi di collezione disponibili in HCL che ci interessano sono le liste, i mapping e i set.
Liste
Le liste sono simili agli array in altri linguaggi di programmazione. Contengono un numero noto di elementi dello stesso tipo, che puoi accedere usando la notazione array ([]
) tramite l’indice intero, iniziando da 0. Ecco un esempio di dichiarazione di una variabile di lista che contiene i nomi dei Droplet che deployerai nei prossimi passi:
Per il type
, hai specificato che è una lista il cui elemento è una stringa, e poi hai fornito il suo default
value. In HCL, i valori elencati tra parentesi indicano una lista.
Mappature
Le mappe sono raccolte di coppie chiave-valore, dove ogni valore viene acceduto usando la sua chiave di tipo string
. Ci sono due modi per specificare le mappe all’interno di parentesi graffe: usando i colon (:
) o gli equals (=
) per specificare valori. Nelle due situazioni, il valore deve essere enclosure con quotation marks. Quando si usa il colon, anche la chiave deve essere enclosure.
Il seguente definizione della mappa che contiene i nomi dei Droplet per diverse ambientazioni è scritta usando l’equals:
Se la chiave comincia con un numero, devi usare la sintassi del colon:
Gruppi
I gruppi non supportano l’ordinamento, ovvero che l’iterazione sui gruppi non garantisce che venga restituito lo stesso ordine ogni volta e che i loro elementi non possono essere accessibili in maniera targetata. Contengono elementi unici ripetuti esattamente una volta e specificarli più volte risulterà nel loro esistere solo una volta.
Dichiarare un set è simile alla dichiarazione di una lista, l’unica differenza è il tipo di variabile:
Ora che hai appreso le tipologie di strutture dati offerti da HCL e revisionato la sintassi delle liste, mappe e set, che utilizzerai durante questo tutorial, passerai ad alcune forme flessibili di deploy di più istanze dello stesso risorsa in Terraform.
Impostare il numero di risorse usando la chiave count
Nella sezione successiva, creerai più istanze della stessa risorsa utilizzando la chiave count
. La chiave count
è disponibile su tutte le risorse e specifica quanti oggetti creati.
Avvertili come funziona scrivendo una risorsa Droplet che si salverà nel file droplets.tf
nell’directory del progetto che hai creato come parte dei requisiti preliminari. Crea e apri per modifica eseguendo:
Scrivi i seguenti linee:
Questo codice definisce una risorsa Droplet chiamata test_droplet
, che esegue Ubuntu 20.04 con 1GB di RAM e un nucleo CPU. Aggiungi le seguenti righe:
Notate che il valore di count
è impostato su 3
, che significa che Terraform tentera di creare tre istanze della stessa risorsa. Quando avviene, salva e chiude il file.
Puoi pianificare il progetto per vedere quale azioni Terraform farebbe eseguendo:
L’output sarà simile a questo:
Il risultato dettagliato che Terraform creerà tre istanze di test_droplet
, tutte con lo stesso nome web
. Mentre è possibile, non è preferito, quindi modifichiamo la definizione del Droplet per rendere il nome di ogni istanza unica. Apri droplets.tf
per l’editing:
Modifica la riga indicata:
Salva e chiudi il file.
Il oggetto count
offre il parametro index
, che contiene l’indice corrente, che viene sostituito nel nome del Droplet usando interpolazione delle stringhe, che consente di costruire una stringa dinamicamente aggiungendo variabili. Potete pianificare nuovamente il progetto per vedere le modifiche:
L’output sarà simile a questo:
Questo volta, le tre istanze di test_droplet
avverranno il loro indice nel loro nome, facendoli più facili da tracciare.
Ora sappiate come creare più istanze di un risorsa usando la chiave count
, così come recuperare e usare l’indice di un’istanza durante la provvisioning. Successivamente, imparate come recuperare il nome del Droplet da un elenco.
Risoluzione dei nomi di singoli Droplet da una lista
Nelle situazioni in cui sono necessari più istanze dello stesso risorsa con nomi personalizzati, è possibile recuperarli dinamicamente da una variabile elenco che hai definito. Nel resto del tutorial vedrai diverse maniere di automatizzare la distribuzione dei Droplet da un elenco di nomi, promettendo flessibilità e facilità d’uso.
Prima di tutto devi definire un elenco contenente i nomi dei Droplet. Apri un file chiamato variables.tf
e lo sblocca per modificarlo:
Aggiungi le seguenti linee:
Salva e chiudi il file. Questo codice definisce un elenco chiamato droplet_names
, che contiene le stringhe first
, second
, third
, e fourth
.
Apri droplets.tf
per modificarlo:
Modifica le righe rosse:
Per migliorare la flessibilita, invece di specificare manualmente un numero costante di elementi, passa il valore della lunghezza dell’elenco droplet_names
al parametro count
, che sempre restituirà il numero di elementi nell’elenco. Per il nome, prefetch l’elemento dell’elenco posizionato all’indice count.index
, usando la notazione degli array. Salva e chiude il file quando hai finito.
Prova a pianificare nuovamente il progetto. Riceverai un output simile a questo:
Come risultato di queste modifiche, saranno create quattro Droplet, denominati in sequenza con i nomi degli elementi dell’elenco droplet_names
.
Hai appreso riguardo alla funzione count
, le sue caratteristiche e la sintassi e l’hai utilizzata insieme ad un elenco per modificare le istanze del resource. Ora vedrai i suoi svantaggi e come superarli.
Comprendere i Disavantaggi della Funzione count
Adesso che sai come la funzione count è utilizzata, esamina i suoi svantaggi quando viene utilizzata per modificare l’elenco con cui è utilizzata.
Prova a distribuire i Droplet nel cloud:
Inserisci yes
quando viene richiesto. La fine dell’output sarà simile a questo:
OutputApply complete! Resources: 4 added, 0 changed, 0 destroyed.
Ora creiamo un’altra istanza di Droplet aggiungendo un elemento all’elenco droplet_names
. Apri variables.tf
per modificarlo:
Aggiungi un nuovo elemento all’inizio dell’elenco:
Una volta fatto, salva e chiudi il file.
Il progetto è stato pianificato come segue:
riceverà un’output come questo:
L’output mostra che Terraform rinominerebbe i primi quattro Droplet e crearne uno quintale chiamato zero
, perché considera le istanze come una lista ordinata e identifica gli elementi (Droplet) tramite il loro numero di indice nella lista. Così facendo, Terraform considera inizialmente i quattro Droplet come se fossero un’ordinata lista:
Index Number | 0 | 1 | 2 | 3 |
---|---|---|---|---|
Droplet Name | first | second | third | fourth |
Quando viene aggiunto il nuovo Droplet zero
all’inizio, la sua rappresentazione interna in tabella rispecchia così:
Index Number | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
Droplet Name | zero | first | second | third | fourth |
I quattro Droplet iniziali sono ora spostati di uno spazio a destra. Terraform poi confronta due stati rappresentati in tabelle: al posto 0
, il Droplet era chiamato first, e perché è diverso nell’altra tabella, pianifica un’azione di aggiornamento. Questo continua fino alla posizione 4
, che non ha un elemento comparabile nell’ prima tabella, e invece pianifica un’azione di provvista Droplet:
Questo significa che aggiungere un nuovo elemento alla lista ovunque esclusivamente all’ultimo risulterà in modifiche di risorse quando non sono necessarie. Le azioni di aggiornamento simili saranno pianificate se un elemento della lista droplet_names
fosse rimosso.
Il principale problema di usare count
per la distribuzione di un numero dinamico di istanze identiche dello stesso risorsa è l’incompleta tracciatura delle risorse. Per un numero costante e costanti di istanze della stessa risorsa, count
è una soluzione semplice che funziona bene. Nelle situazioni come queste, tuttavia, quando alcuni attributi sono stati estratti da variabili, il loop for_each
, che ti verra spiegato più tardi nel tutorial, è molto meglio scelta.
Riferimento al Risorsa Attuale (self
)
Un altro svantaggio di count
è che non è possibile fare riferimento ad una istanza arbitraria di una risorsa tramite indice nella maggior parte dei casi.
L’esempio principale sono i provvigionisti di tempo di destruzione, che si eseguono quando la risorsa è pianificata per essere distrutta. La ragione è che l’istanza richiesta potrebbe non esistere (è già distrutta) o creare un ciclo di dipendenza reciproca. In tali situazioni, invece di riferirsi all’oggetto attraverso la lista di istanze, puoi accedere solo alla risorsa corrente tramite il carattere self
.
Per dimostrare il suo uso, ora aggiungerai un provvisore locale di tempo di distruzione alla definizione test_droplet
, che mostrerà un messaggio quando viene eseguito. Apri droplets.tf
per la modifica:
Aggiungi le seguenti righe evidenziate:
Salva e chiudi il file.
Il provvisore local-exec
esegue un comando sulla macchina locale in cui Terraform è in esecuzione. Poiché il parametro when
è impostato su destroy
, viene eseguito solo quando il resource è destinato ad essere distrutto. Il comando eseguito stampa una stringa sullo stdout
, che sostituisce il nome del resource corrente usando self.name
.
Poiché nella prossima sezione creerai i Droplet in modo diverso, distruggi gli attualmente deployati eseguendo il seguente comando:
Inserisci yes
quando richiesto. riceverai l’esecuzione del provvisore local-exec
quattro volte:
In questo passaggio, hai appreso i limiti del count
. Ora imparerai riguardo alla struttura di ciclo for_each
, che li supera e funziona su un ampio range di tipi di variabili.
Utilizzo del Ciclo for_each
In questa sezione, considerareai il ciclo for_each
, la sua sintassi e come aiuta la flessibilità nella definizione di risorse con molti istanze.
for_each
è un parametro disponibile su ogni risorsa, ma a differenza di count
, che richiede un numero di istanze da creare, for_each
accetta una mappa o un insieme. Ogni elemento della collezione fornita viene esplorato una sola volta e viene creata una istanza per esso. for_each
rende la chiave e il valore disponibili sotto la parola chiave each
come attributi (la chiave e il valore del pezzo come each.key
e each.value
, rispettivamente). Quando è fornito un insieme, la chiave e il valore saranno gli stessi.
Poiché fornisce l’elemento corrente nell’oggetto each
, non dovrai accedere manualmente all’elemento desiderato come facevi con le liste. Nel caso di insiemi, questo non è nemmeno possibile, poiché non ha un ordinamento interno riconoscibile. Le liste possono anche essere passate, ma devono prima essere convertite in un insieme utilizzando la funzione toset
.
L’unico vantaggio dell’uso di for_each
, oltre a poter enumerare tutti e tre i tipi di dati di collezione, è che solo gli elementi coinvolti verranno modificati, creati o eliminati. Se cambi l’ordine degli elementi nell’input, non verranno pianificate alcuna azione, e se aggiungi, rimuovi o modifichi un elemento dall’input, verranno pianificate solo azioni appropriate per quell’elemento.
Convertiamo la risorsa Droplet da count
a for_each
e vediamo come funziona in pratica. Apri droplets.tf
per la modifica eseguendo:
Modifica le righe evidenziate:
Puoi rimuovere il provider local-exec
. Una volta fatto, salva e chiudi il file.
Il primo rigo sostituisce count
e invoca for_each
, passando la lista di nomi di Droplet come insieme usando la funzione toset
. Per il nome Droplet specificare each.value
, che contiene il valore dell’elemento corrente dall’insieme dei nomi di Droplet.
Progetta il progetto facendo:
L’output mostrera i passaggi che Terraform avrebbe fatto:
Invece di usare count
, ora Terraform considera ogni istanza individualmente, e non come elementi di un elenco ordinato. Ogni istanza è collegata ad un elemento del set dato, come indicato dal numero mostrato tra parentesi accanto a ogni risorsa che verrà creata.
Applica il piano al cloud eseguendo:
Inserisci sì
quando vi sarà chiesto. Quando finisce, eliminerai uno degli elementi dalla lista droplet_names
per dimostrare che le altre istanze non saranno affettate. Apri variables.tf
per modifica:
Il testo è stato tradotto in italiano come segue:
Modifica la lista così come questo:
Salva e chiude il file.
Riprogettare il progetto e riceverai l’output seguente:
Questa volta, Terraform distrugge solo l’istanza rimossa (zero
), e non tocca alcuna delle altre istanze, che è il comportamento corretto.
Nel passo precedente, hai appreso le nozioni di for_each
, come usarlo e i suoi vantaggi rispetto al count
. Adesso imparare aiuto con il ciclo for
, la sua sintassi d’uso e quando può essere utilizzato per automatizzare determinati task.
Looping Usando for
Il ciclo for
funziona su collezioni e crea una nuova collezione applicando una trasformazione ad ogni elemento dell’input. La tipologia esatta dell’output dipenderà se il loop è racchiuso tra parentesi ([]
) o tra accappatoio ({}
), che donano un elenco o un mapping, rispettivamente. In tal modo è adatto per interrogare le risorse e formare output strutturati per ulteriori processi.
La sintassi generale del ciclo for
è:
Come in altri linguaggi di programmazione, prima di tutto devi nominare la variabile di traversamento (element
) e specificare la raccolta da enumerare. Il corpo del loop è il passo di trasformazione, ed è opzionale un clausola if
per filtrare la collezione di input.
Ora proverò ad esempio alcuni usi utilizzando le output. Verranno salvati in un file chiamato outputs.tf
. Crea il file per l’editing con il seguente comando:
Aggiungi le seguenti linee per salvare coppie di nomi dei Droplet deployati e loro indirizzi IP:
Questo codice definisce un’output chiamata ip_addresses
, e specifica un for
loop che itera sui risorse dell’istanza test_droplet
che hai personalizzato nei passi precedenti. Perché il loop è racchiuso tra parentesi quadrate, il passo di trasformazione per i map è simile a funzioni lambda in altri linguaggi di programmazione, e qui crea una coppia chiave-valore combinando il nome dell’istanza come chiave con il suo indirizzo IP privato come valore.
Salva e chiude il file, quindi rifresca lo stato di Terraform per tenere conto della nuova output eseguendo:
Il comando Terraform refresh
aggiorna lo stato locale con lo stato reale dell’infrastruttura nel cloud.
Poiché, controlla i contenuti degli output:
Il risultato mostrato da Terraform è il contenuto dell’output ip_addresses
, che è costruito come un map dal loop for
. L’ordine delle entry potrebbe essere diverso per te. Il loop funziona senza problemi per ogni numero di entry – in altri termini, se aggiungi un nuovo elemento alla lista droplet_names
, il nuovo Droplet sarà creato senza ulteriori input manuali e apparirà automaticamente nell’output di questo modulo.
Enclosing the for
loop in square brackets allows you to output the list. For example, you could output only the Droplet IP addresses, which is useful for external software that might be parsing the data. The code would look like this:
Here, the transformational step selects the IP address attribute. It would give the following output:
Come era stato notato prima, puoi anche filtrare la raccolta di input usando la clausola if
. Ecco come scriverei il loop per filtrarla per la regione fra1
:
Nel HCL, l’operatore ==
controlla l’equivalenza dei valori delle due parti – qui verifica se instance.region
è uguale a fra1
. Se si, la verifica passa e l’istanza viene trasformata e aggiunta al risultato di output, altrimenti viene ignorata. Il risultato di questo codice sarebbe lo stesso degli esempi precedenti, perché tutte le istanze Droplet sono nella regione fra1
, secondo la definizione del risorsa test_droplet
. La condizionale if
è anche utile quando vuoi filtrare la raccolta di input per altri valori nel tuo progetto, come la dimensione o la distribuzione.
Per poter creare risorse differenti nell’ultima sezione, distruggere quelle attualmente deployate eseguendo il comando seguente:
Inserisci sì
quando vi sia richiesto per completare il processo.
Hanno fatto un tour sul ciclo for
, la sua sintassi e esempi di uso nei risultati. Ora imparare su come funzionano le condizionali e come possono essere utilizzate insieme al count
.
Direttive e Condizionali
Nel precedente segmento, hai visto che la chiave count
funziona. Ora imparare su come funziona l’operatore condizionale ternario, che puoi usare altrove nel tuo codice Terraform e come puoi usarlo con count
.
La sintassi dell’operatore ternario è:
condizione
è un’espressione che calcola un valore booleano (true o false). Se la condizione è vera, allora l’espressione valuta value_if_true
. Al contrario, se la condizione è falsa, il risultato sarà value_if_false
.
L’uso principale degli operatori ternari è consentire o disattivare la creazione singola di risorse secondo i contenuti della variabile. Questo può essere ottenuto passando il risultato della comparazione (either 1
or 0
) alla chiave count
sul desiderato risorsa.
Nell’evento in cui stai usando l’operatore ternario per recuperare un singolo elemento da una lista o un insieme, puoi utilizzare la funzione one
. Se la raccolta passata è vuota, ritorna null
. Altrimenti, ritorna l’elemento singoletto nella raccolta, oppure lancia un errore se ci sono più di uno.
Abbiamo un variabile chiamata create_droplet
, che controllera se verrà creato un Droplet. Prima di tutto, apri variables.tf
per modificarlo:
Aggiungi le linee sottolineate:
Questo codice definisce la variabile create_droplet
di tipo bool
. Salva e chiudi il file.
Poi, per modificare la dichiarazione del Droplet, apri droplets.tf
per modificarla eseguendo:
Modificalo come segue:
Per count
, usa un operatore ternario per restituire 1
se la variabile create_droplet
è vera, altrimenti 0
, che risulterà in nessun Droplet provvisto. Salva e chiudi il file quando hai finito.
Eseguisci pianificazione dell’esecuzione del progetto con la variabile impostata su false eseguendo:
Riceverai l’output seguente:
Dato che create_droplet
è stato passato nel valore di false
, il contatore di istanze è 0
, quindi non saranno creati Droplet, quindi non saranno disponibili gli IP address da mostrare.
Ti hai mostrato come usare l’operatore condizionale terzario insieme alla chiave count
per poter scegliere con maggior flessibilita se deployare le risorse desiderate. Successivamente, impararerai ad esplicitarli le dipendenze delle risorse.
Esplicita delle dipendenze delle risorse
Durante la creazione pianificazione di esecuzione del progetto, Terraform rileva le catene di dipendenze tra le risorse e li ordina automaticamente in modo che possano essere costruite nell’ordine appropriato. Nella maggior parte dei casi, Terraform riesce a rilevare relazioni tra risorse analizzando tutti i valori espressi nei risorsa e a costruire un grafo.
Tuttavia, quando una risorsa richiede acceso controllato al provider di servizi cloud per poter essere provvisto, non c’è alcun segno chiaro per Terraform che sono relazioni dipendenti. In tali casi, la dipendenza deve essere specificata manualmente utilizzando l’argomento depends_on
.
Il valore depends_on
è disponibile su ogni risorsa e viene utilizzato per specificare le relazioni nascoste tra risorse specifiche. Le relazioni dipendenti nascoste si verificano quando una risorsa dipende dall’altra per comportamento, senza usare alcuna informazione della risorsa in una dichiarazione, che avrebbe incoraggiato Terraform a collegarle in maniera unica.
Ecco un esempio di come depends_on
viene specificato nel codice:
Accepta una lista di riferimenti ad altri risorse, e non accetta espressioni arbitrarie.
dipende_da
deve essere usato sparingemente, e solo quando tutte le altre opzioni sono esaurite. Il suo uso significa che cosa tenti di dichiarare sta entrando fuori dal sistema di dettaglio automatico di dipendenze di Terraform; potrebbe significare che la risorsa sta esplicitamente dipendendo da più risorse che non ne ha bisogno.
Tu hai ora appreso come specificare esplicitamente le dipendenze per una risorsa usando il valore dipende_da
. E’ utilizzato raramente, e solo quando nessun’altra opzione è disponibile. La sua utilizzazione indica che ciò che tenti di dichiarare sta entrando fuori dai confini del sistema automatico di dettaglio delle dipendenze di Terraform; potrebbe significare che la risorsa sta esplicitamente dipendendo da più risorse che non ne ha bisogno.
Conclusione
Nell’articolo, abbiamo discusso le caratteristiche di HCL che migliorano la flessibilità e la scalabilità del tuo codice, come count
per specificare il numero di istanze della risorsa da deploy e for_each
come metodo avanzato per iterare su tipi di dati collezionati personalizzati e personalizzare le istanze. Quando usato correttamente, riducono significativamente la duplicazione del codice e l’overhead operativo di gestire le infrastrutture deployate.
Hai anche imparato a usare i condizionali e operatori ternari, e come possono essere utilizzati per controllare se una risorsa verrà deployata. Mentre il sistema di analisi automatica delle dipendenze di Terraform è molto capace, potrebbero esistere casi in cui devi manualmente specificare le dipendenze della risorsa usando il valore dipende_da
.
Questo tutorial fa parte della serie Come gestire l’infrastruttura con Terraform. La serie copre un numero di argomenti di Terraform, dalla prima installazione di Terraform a gestire progetti complessi.