Best practice per la gestione delle dipendenze

Questo documento fornisce suggerimenti per esprimere le dipendenze tra le risorse nella configurazione Terraform.

Prediligi le dipendenze implicite rispetto alle dipendenze esplicite

Le dipendenze delle risorse sorgono quando una risorsa dipende dall'esistenza di altre risorse. Terraform deve essere in grado di comprendere queste dipendenze per garantire che le risorse vengano create nell'ordine corretto. Ad esempio, se la risorsa A ha una dipendenza dalla risorsa B, la risorsa B viene creata prima della risorsa A.

Le dipendenze della configurazione di Terraform possono essere stabilite tramite dichiarazioni delle dipendenze implicite ed esplicite. Le dipendenze implicite vengono dichiarate tramite riferimenti a espressione, mentre le dipendenze esplicite vengono specificate utilizzando l'argomento meta depends_on. L'argomento depends_on specifica che Terraform deve completare tutte le azioni sugli oggetti da cui dipende una risorsa o un modulo prima di procedere con l'oggetto dipendente.

Sebbene entrambi gli approcci garantiscano un corretto ordine delle operazioni, le dipendenze implicite spesso comportano una maggiore efficienza nella pianificazione degli aggiornamenti e della sostituzione delle risorse. Questo perché Terraform è in grado di monitorare in modo intelligente i campi specifici coinvolti in una dipendenza implicita, evitando potenzialmente modifiche alla risorsa dipendente se quei campi specifici rimangono inalterati all'interno della dipendenza.

Rispetto alle dipendenze implicite, quelle esplicite trasmettono informazioni meno specifiche. Ciò significa che Terraform può solo formulare piani più conservativi per la creazione, l'aggiornamento e la sostituzione delle risorse, in assenza di conoscenza degli attributi specifici che costituiscono la dipendenza. In pratica, questo influisce sulla sequenza con cui le risorse vengono create da Terraform e sul modo in cui Terraform determina se le risorse richiedono aggiornamenti o sostituzioni.

Ti consigliamo di utilizzare dipendenze esplicite con il meta argomento depends_on solo come ultima risorsa quando una dipendenza tra due risorse è nascosta e non può essere espressa tramite dipendenze implicite.

Nell'esempio seguente, i servizi di progetto richiesti devono essere abilitati prima di creare un set di dati BigQuery. Questa dipendenza viene dichiarata esplicitamente:

Sconsigliato:

module "project_services" {
  source  = "terraform-google-modules/project-factory/google//modules/project_services"
  version = "~> 14.4"

  project_id = var.project_id
  activate_apis = [
    "bigquery.googleapis.com",
    "bigquerystorage.googleapis.com",
  ]
}

module "bigquery" {
  source       = "terraform-google-modules/bigquery/google"
  version      = "~> 5.4"

  dataset_id   = "demo_dataset"
  dataset_name = "demo_dataset"
  project_id   = var.project_id
  depends_on = [module.project_services] # <- explicit dependency
}

L'esempio seguente sostituisce la dipendenza esplicita con una dipendenza implicita facendo riferimento all'argomento project_id come attributo di output project_id della risorsa project_services:

Consigliato:

module "bigquery" {
  source       = "terraform-google-modules/bigquery/google"
  version      = "~> 5.4"

  dataset_id   = "demo_dataset"
  dataset_name = "demo_dataset"
  project_id   = module.project_services.project_id # <- implicit dependency
}

L'uso delle dipendenze implicite consente di effettuare dichiarazioni precise delle dipendenze, ad esempio specificare le informazioni esatte da raccogliere da un oggetto a monte. In questo modo si riduce anche la necessità di apportare modifiche in più punti, il che a sua volta riduce il rischio di errori.

Riferimento per gli attributi di output delle risorse dipendenti

Quando crei dipendenze implicite facendo riferimento a valori di risorse upstream, assicurati di fare riferimento solo agli attributi di output, in particolare ai valori non ancora noti. In questo modo, Terraform attenderà la creazione delle risorse upstream prima di eseguire il provisioning della risorsa attuale.

Nell'esempio seguente, la risorsa google_storage_bucket_object fa riferimento all'argomento nome della risorsa google_storage_bucket. Gli argomenti hanno valori noti durante la fase del piano Terraform. Ciò significa che quando Terraform crea la risorsa google_storage_bucket_object, non attende il completamento della creazione della risorsa google_storage_bucket, perché il riferimento a un argomento noto (il nome del bucket) non crea una dipendenza implicita tra google_storage_bucket_object e google_storage_bucket. Ciò consente di annullare lo scopo della dichiarazione di dipendenza implicita tra le due risorse.

Sconsigliato:

# Cloud Storage bucket
resource "google_storage_bucket" "bucket" {
  name = "demo-bucket"
  location = "US"
}

resource "google_storage_bucket_object" "bucket_object" {
  name   = "demo-object"
  source = "./test.txt"
  bucket = google_storage_bucket.bucket.name # name is an input argument
}

La risorsa google_storage_bucket_object deve invece fare riferimento all'attributo di output id della risorsa google_storage_bucket_object. Poiché il campo id è un attributo di output, il suo valore viene impostato solo dopo l'esecuzione della creazione della risorsa. Pertanto, Terraform attenderà il completamento della creazione della risorsa google_storage_bucket_object prima di iniziare la creazione della risorsa google_storage_bucket_object.

Consigliato:

resource "google_storage_bucket_object" "bucket_object" {
  name   = "demo-object"
  source = "./test.txt"
  bucket = google_storage_bucket.bucket.id # id is an output attribute
}

A volte non esiste un attributo di output evidente a cui fare riferimento. Considera ad esempio l'esempio seguente, in cui module_a prende il nome del file generato come input. All'interno di module_a, il nome file viene utilizzato per leggere il file. Se esegui il codice così com'è, riceverai un'eccezione no such file or directory, causata dal tentativo di Terraform di leggere il file durante la fase di pianificazione, momento in cui il file non è stato ancora creato. In questo caso, un esame dell'attributo di output della risorsa local_file rivela che non ci sono campi evidenti che puoi utilizzare al posto dell'argomento di input del nome file.

Sconsigliato:

resource "local_file" "generated_file" {
 filename = "./generated_file.text"
 content = templatefile("./template.tftpl", {
   project_id = var.project_id
 })
}

module "module_a" {
 source = "./modules/module-a"
 root_config_file_path = local_file.generated_file.filename
}

Puoi risolvere questo problema introducendo una dipendenza esplicita. Come best practice, assicurati di aggiungere un commento sul motivo per cui è necessaria la dipendenza esplicita:

Consigliato:

module "module_a" {
 source = "./modules/module-a"
 root_config_file_path = local_file.generated_file.filename
 depends_on = [local_file.generated_file] # waiting for generated_file to be created
}

Passaggi successivi