Best Practices für das Abhängigkeitsmanagement

In diesem Dokument finden Sie Empfehlungen dazu, wie Sie Abhängigkeiten zwischen Ressourcen in Ihrer Terraform-Konfiguration ausdrücken.

Implizite Abhängigkeiten gegenüber expliziten Abhängigkeiten bevorzugen

Ressourcenabhängigkeiten entstehen, wenn eine Ressource von der Existenz anderer Ressourcen abhängt. Terraform muss diese Abhängigkeiten erkennen können, damit Ressourcen in der richtigen Reihenfolge erstellt werden. Wenn beispielsweise Ressource A von Ressource B abhängt, wird Ressource B vor Ressource A erstellt.

Abhängigkeiten in Terraform-Konfigurationen können durch implizite und explizite Abhängigkeitsdeklarationen festgelegt werden. Implizite Abhängigkeiten werden über Ausdrucksreferenzen deklariert, während explizite Abhängigkeiten mit dem Metaargument depends_on angegeben werden. Mit dem Argument depends_on wird angegeben, dass Terraform alle Aktionen an den Objekten ausführen muss, von denen eine Ressource oder ein Modul abhängt, bevor mit dem abhängigen Objekt fortgefahren wird.

Beide Ansätze sorgen für eine korrekte Abfolge der Vorgänge. Implizite Abhängigkeiten führen jedoch oft zu einer effizienteren Planung von Aktualisierungen und Ersatz von Ressourcen. Das liegt daran, dass Terraform die spezifischen Felder, die an einer impliziten Abhängigkeit beteiligt sind, intelligent verfolgen kann. So lassen sich Änderungen an der abhängigen Ressource möglicherweise vermeiden, wenn diese spezifischen Felder innerhalb der Abhängigkeit unverändert bleiben.

Im Vergleich zu impliziten Abhängigkeiten geben explizite Abhängigkeiten weniger spezifische Informationen weiter. Das bedeutet, dass Terraform nur konservativere Pläne für die Erstellung, Aktualisierung und den Ersatz von Ressourcen formulieren kann, wenn die entsprechenden Attribute, die die Abhängigkeit bilden, nicht bekannt sind. In der Praxis wirkt sich dies auf die Reihenfolge aus, in der Ressourcen von Terraform erstellt werden, und darauf, wie Terraform ermittelt, ob Ressourcen aktualisiert oder ersetzt werden müssen.

Wir empfehlen, explizite Abhängigkeiten mit dem Metaargument depends_on nur als letzten Ausweg zu verwenden, wenn eine Abhängigkeit zwischen zwei Ressourcen ausgeblendet ist und nicht durch implizite Abhängigkeiten ausgedrückt werden kann.

Im folgenden Beispiel müssen die erforderlichen Projektdienste aktiviert sein, bevor ein BigQuery-Dataset erstellt werden kann. Diese Abhängigkeit wird explizit deklariert:

Nicht empfohlen:

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
}

Im folgenden Beispiel wird die explizite Abhängigkeit durch eine implizite Abhängigkeit ersetzt, indem auf das project_id-Argument als project_id-Ausgabeattribut der project_services-Ressource verwiesen wird:

Empfohlen:

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
}

Die Verwendung impliziter Abhängigkeiten ermöglicht eine genaue Deklaration von Abhängigkeiten, z. B. die Angabe der genauen Informationen, die aus einem vorgelagerten Objekt erfasst werden müssen. Außerdem müssen Änderungen nicht an mehreren Stellen vorgenommen werden, was wiederum das Risiko von Fehlern reduziert.

Ausgabeattribute aus abhängigen Ressourcen referenzieren

Wenn Sie implizite Abhängigkeiten erstellen, indem Sie auf Werte aus vorgelagerten Ressourcen verweisen, sollten Sie nur auf Ausgabeattribute verweisen, insbesondere auf Werte, die noch nicht bekannt sind. So wartet Terraform darauf, dass die vorgelagerten Ressourcen erstellt werden, bevor die aktuelle Ressource bereitgestellt wird.

Im folgenden Beispiel verweist die google_storage_bucket_object-Ressource auf das Argument „name“ der google_storage_bucket-Ressource. Argumente haben während der Terraform-Planungsphase bekannte Werte. Das bedeutet, dass Terraform beim Erstellen der google_storage_bucket_object-Ressource nicht darauf wartet, dass die google_storage_bucket-Ressource erstellt wird. Denn der Verweis auf ein bekanntes Argument (den Bucketnamen) führt nicht zu einer impliziten Abhängigkeit zwischen google_storage_bucket_object und google_storage_bucket. Dadurch wird der Zweck der impliziten Abhängigkeitsdeklaration zwischen den beiden Ressourcen verfehlt.

Nicht empfohlen:

# 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
}

Stattdessen muss die google_storage_bucket_object-Ressource auf das id-Ausgabeattribut der google_storage_bucket_object-Ressource verweisen. Da das Feld id ein Ausgabeattribut ist, wird sein Wert erst festgelegt, nachdem die Ressource erstellt wurde. Daher wartet Terraform, bis die Erstellung der google_storage_bucket_object-Ressource abgeschlossen ist, bevor mit der Erstellung der google_storage_bucket_object-Ressource begonnen wird.

Empfohlen:

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

Manchmal gibt es kein offensichtliches Ausgabeattribut, auf das verwiesen werden kann. Betrachten Sie beispielsweise das folgende Beispiel, in dem module_a den Namen der generierten Datei als Eingabe nimmt. In module_a wird der Dateiname zum Lesen der Datei verwendet. Wenn Sie diesen Code unverändert ausführen, erhalten Sie eine no such file or directory-Ausnahme. Diese wird verursacht, weil Terraform versucht, die Datei während der Planungsphase zu lesen, zu der sie noch nicht erstellt wurde. In diesem Fall zeigt eine Prüfung des Ausgabeattributs der local_file-Ressource, dass es keine offensichtlichen Felder gibt, die anstelle des Eingabearguments „filename“ verwendet werden können.

Nicht empfohlen:

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
}

Sie können dieses Problem beheben, indem Sie eine explizite Abhängigkeit einführen. Fügen Sie als Best Practice einen Kommentar hinzu, warum die explizite Abhängigkeit erforderlich ist:

Empfohlen:

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
}

Nächste Schritte