Fallstricke von Terraform

Bild

Wir werden einige Fallstricke hervorheben, einschließlich derer, die sich auf Schleifen, if-Anweisungen und Bereitstellungstechniken beziehen, sowie allgemeinere Probleme, die Terraform als Ganzes betreffen:

  • Die Parameter count und for_each haben Einschränkungen.
  • Keine Einschränkungen bei der Bereitstellung von Ausfallzeiten
  • sogar ein guter Plan kann scheitern;
  • Refactoring kann seine Tricks haben;
  • Aufgeschobene Konsistenz ist konsistent ... mit Aufschub.

Die Parameter count und for_each haben Einschränkungen


In den Beispielen in diesem Kapitel werden der Parameter count und der Ausdruck for_each aktiv in Schleifen und in der bedingten Logik verwendet. Sie haben eine gute Leistung, aber zwei wichtige Einschränkungen, die Sie kennen müssen.

  • In count und for_each können keine Ressourcenausgabevariablen referenziert werden.
  • count und for_each können nicht in der Modulkonfiguration verwendet werden.

Count und for_each können keine Ausgabevariablen einer Ressource referenzieren


Stellen Sie sich vor, Sie müssen mehrere EC2-Server bereitstellen und möchten aus irgendeinem Grund kein ASG verwenden. Ihr Code könnte folgendermaßen aussehen:

resource "aws_instance" "example_1" {
   count             = 3
   ami                = "ami-0c55b159cbfafe1f0"
   instance_type = "t2.micro"
}

Wir werden sie der Reihe nach betrachten.

Da dem Parameter count ein statischer Wert zugewiesen ist, funktioniert dieser Code problemlos: Wenn Sie den Befehl apply ausführen, werden drei EC2-Server erstellt. Wenn Sie jedoch einen Server in jeder Verfügbarkeitszone (Verfügbarkeitszone oder AZ) in der aktuellen AWS-Region bereitstellen möchten? Sie können Ihren Code die Liste der Zonen aus der Datenquelle aws_availability_zones laden lassen und dann durch jede dieser Zonen blättern und einen EC2-Server darin erstellen, indem Sie den Parameter count verwenden und über den Index auf das Array zugreifen:

resource "aws_instance" "example_2" {
   count                   = length(data.aws_availability_zones.all.names)
   availability_zone   = data.aws_availability_zones.all.names[count.index]
   ami                     = "ami-0c55b159cbfafe1f0"
   instance_type       = "t2.micro"
}

data "aws_availability_zones" "all" {}

Dieser Code funktioniert genauso gut, da der Parameter count problemlos auf Datenquellen verweisen kann. Aber was passiert, wenn die Anzahl der Server, die Sie erstellen müssen, von der Ausgabe einer Ressource abhängt? Um dies zu demonstrieren, ist es am einfachsten, die Ressource random_integer zu verwenden, die, wie Sie anhand des Namens erraten können, eine zufällige Ganzzahl zurückgibt:

resource "random_integer" "num_instances" {
  min = 1
  max = 3
}

Dieser Code generiert eine Zufallszahl von 1 bis 3. Mal sehen, was passiert, wenn wir versuchen, die Ergebnisausgabe dieser Ressource im Parameter count der Ressource aws_instance zu verwenden:

resource "aws_instance" "example_3" {
   count             = random_integer.num_instances.result
   ami                = "ami-0c55b159cbfafe1f0"
   instance_type = "t2.micro"
}

Wenn Sie den Terraform-Plan für diesen Code ausführen, wird der folgende Fehler angezeigt:

Error: Invalid count argument

   on main.tf line 30, in resource "aws_instance" "example_3":
   30: count = random_integer.num_instances.result

The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.

Terraform erfordert, dass count und for_each während der Planungsphase berechnet werden, bevor Ressourcen erstellt oder geändert werden. Dies bedeutet, dass count und for_each auf Literale, Variablen, Datenquellen und sogar Listen von Ressourcen verweisen können (vorausgesetzt, ihre Länge kann während der Planung bestimmt werden), nicht jedoch auf berechnete Ausgabevariablen der Ressource.

count und for_each können nicht in der Modulkonfiguration verwendet werden


Manchmal könnten Sie versucht sein, den Parameter count zur Modulkonfiguration hinzuzufügen:

module "count_example" {
     source = "../../../../modules/services/webserver-cluster"

     count = 3

     cluster_name = "terraform-up-and-running-example"
     server_port = 8080
     instance_type = "t2.micro"
}

Dieser Code versucht, mit count innerhalb des Moduls drei Kopien der Webserver-Cluster-Ressource zu erstellen. Oder Sie möchten die Modulverbindung abhängig von einer booleschen Bedingung optional machen und den Zählparameter 0 zuweisen. Dieser Code sieht ziemlich vernünftig aus, aber als Ergebnis der Ausführung des Terraform-Plans wird der folgende Fehler angezeigt:

Error: Reserved argument name in module block

   on main.tf line 13, in module "count_example":
   13: count = 3

The name "count" is reserved for use in a future version of Terraform.

Zum Zeitpunkt der Veröffentlichung von Terraform 0.12.6 wurde die Verwendung von count oder for_each in der Modulressource leider nicht unterstützt. Laut den Versionshinweisen zu Terraform 0.12 (http://bit.ly/3257bv4) plant HashiCorp, diese Funktion in Zukunft hinzuzufügen. Je nachdem, wann Sie dieses Buch lesen, ist sie möglicherweise bereits verfügbar. Um dies sicher herauszufinden, lesen Sie das Terraform-Änderungsprotokoll hier .


Die Verwendung des Blocks create_before_destroy in Kombination mit ASG ist eine hervorragende Lösung für die Organisation von Bereitstellungen ohne Ausfallzeiten, mit Ausnahme einer Nuance: Regeln für die automatische Skalierung werden nicht unterstützt. Genauer gesagt wird dadurch die ASG-Größe bei jeder Bereitstellung auf min_size zurückgesetzt. Dies kann ein Problem sein, wenn Sie mithilfe von Regeln für die automatische Skalierung die Anzahl der ausgeführten Server erhöht haben.

Beispielsweise enthält das Webserver-Cluster-Modul ein Paar aws_autoscaling_schedule-Ressourcen, die um 9 Uhr die Anzahl der Server im Cluster von zwei auf zehn erhöhen. Wenn Sie beispielsweise um 11 Uhr bereitstellen, wird der neue ASG nicht mit zehn, sondern nur mit zwei Servern gestartet und bleibt bis 9 Uhr am nächsten Tag in diesem Zustand.

Diese Einschränkung kann auf verschiedene Arten umgangen werden.

  • Ändern Sie den Wiederholungsparameter in aws_autoscaling_schedule von 0 9 * * * ("um 9 Uhr ausführen") in 0-59 9-17 * * * ("jede Minute von 9 bis 17 Uhr ausführen"). Wenn die ASG bereits über zehn Server verfügt, ändert die erneute Ausführung dieser automatischen Skalierungsregel nichts, was wir benötigen. Wenn die ASG-Gruppe kürzlich bereitgestellt wurde, stellt diese Regel sicher, dass in einer Minute die Anzahl ihrer Server zehn erreicht. Dies ist kein ganz eleganter Ansatz, und große Sprünge von zehn auf zwei Server und umgekehrt können ebenfalls Probleme für Benutzer verursachen.
  • Erstellen Sie ein benutzerdefiniertes Skript, das die AWS-API verwendet, um die Anzahl der aktiven Server in der ASG zu bestimmen, rufen Sie es über eine externe Datenquelle auf (siehe Abschnitt "Externe Datenquelle" auf Seite 249) und setzen Sie den gewünschten Kapazitätsparameter der ASG-Gruppe auf den von diesem Skript zurückgegebenen Wert. Somit wird jede neue ASG-Instanz immer mit der gleichen Kapazität wie der alte Terraform-Code ausgeführt und erschwert die Wartung.

Im Idealfall sollte Terraform natürlich eine integrierte Unterstützung für Bereitstellungen ohne Ausfallzeiten haben, aber ab Mai 2019 hatte das HashiCorp-Team nicht vor, diese Funktionalität hinzuzufügen ( Details finden Sie hier ).

Der richtige Plan kann möglicherweise nicht erfolgreich implementiert werden.


Wenn Sie den Befehl plan ausführen, erhalten Sie manchmal einen vollständig korrekten Bereitstellungsplan, aber der Befehl apply gibt einen Fehler zurück. Versuchen Sie beispielsweise, die Ressource aws_iam_user mit demselben Namen hinzuzufügen, den Sie für den IAM-Benutzer verwendet haben, den Sie zuvor in Kapitel 2 erstellt haben:

resource "aws_iam_user" "existing_user" {
   #       IAM,
   #      terraform import
   name = "yevgeniy.brikman"
}

Wenn Sie nun den Plan-Befehl ausführen, zeigt Terraform auf den ersten Blick einen sehr vernünftigen Bereitstellungsplan an:

Terraform will perform the following actions:

   # aws_iam_user.existing_user will be created
   + resource "aws_iam_user" "existing_user" {
         + arn                  = (known after apply)
         + force_destroy   = false
         + id                    = (known after apply)
         + name               = "yevgeniy.brikman"
         + path                 = "/"
         + unique_id         = (known after apply)
      }

Plan: 1 to add, 0 to change, 0 to destroy.

Wenn Sie den Befehl apply ausführen, wird der folgende Fehler angezeigt:

Error: Error creating IAM User yevgeniy.brikman: EntityAlreadyExists:
User with name yevgeniy.brikman already exists.

   on main.tf line 10, in resource "aws_iam_user" "existing_user":
   10: resource "aws_iam_user" "existing_user" {

Das Problem ist natürlich, dass bereits ein IAM-Benutzer mit diesem Namen vorhanden ist. Dies kann nicht nur bei IAM-Benutzern, sondern auch bei nahezu jeder Ressource der Fall sein. Es ist möglich, dass jemand diese Ressource manuell oder über die Befehlszeile erstellt hat. Wie auch immer, übereinstimmende Bezeichner führen zu Konflikten. Es gibt viele Varianten dieses Fehlers, die die Terraform-Anfänger oft überraschen.

Der entscheidende Punkt ist, dass der Befehl terraform plan nur Ressourcen berücksichtigt, die in der Terraform-Statusdatei angegeben sind. Wenn Ressourcen auf andere Weise erstellt werden (z. B. manuell, indem Sie auf die AWS-Konsole klicken), werden sie nicht in die Statusdatei aufgenommen, und Terraform berücksichtigt sie daher bei der Ausführung des Planungsbefehls nicht. Infolgedessen ist der auf den ersten Blick korrekte Plan nicht erfolgreich.

Daraus können zwei Lehren gezogen werden.

  • Wenn Sie bereits mit Terraform gearbeitet haben, verwenden Sie nichts anderes. Wenn ein Teil Ihrer Infrastruktur mit Terraform verwaltet wird, können Sie ihn nicht mehr manuell ändern. Andernfalls laufen Sie nicht nur Gefahr, seltsame Terraform-Fehler zu erhalten, sondern negieren auch viele der Vorteile von IaC, da der Code keine genaue Darstellung Ihrer Infrastruktur mehr darstellt.
  • - , import. Terraform , terraform import. Terraform , . import . . , : _. ( aws_iam_user.existing_user). — , . , ID aws_iam_user (, yevgeniy.brikman), ID aws_instance EC2 ( i-190e22e5). , , .

    import, aws_iam_user, Terraform IAM 2 (, yevgeniy.brikman ):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform API AWS, IAM aws_iam_user.existing_user Terraform. plan Terraform , IAM , .

    , , , Terraform, . , Terraforming (http://terraforming.dtan4.net/), AWS .


    — , , . , , . — , . , Terraform IaC, , « » , .

    , — . IDE . , , Terraform , .

    , webserver-cluster cluster_name:

    variable "cluster_name" {
       description = "The name to use for all the cluster resources"
       type          = string
    }

    , foo. bar. , - .

    , webserver-cluster cluster_name , name ALB:

    resource "aws_lb" "example" {
       name                    = var.cluster_name
       load_balancer_type = "application"
       subnets = data.aws_subnet_ids.default.ids
       security_groups      = [aws_security_group.alb.id]
    }

    name - , Terraform . ALB, -. , , , .

    , , Terraform. aws_security_group webserver-cluster:

    resource "aws_security_group" "instance" {
      # (...)
    }

    instance. , ( ) cluster_instance:

    resource "aws_security_group" "cluster_instance" {
       # (...)
    }

    ? : .

    Terraform ID . , iam_user IAM AWS, aws_instance — ID AWS EC2. (, instance cluster_instance, aws_security_group), Terraform , . , Terraform , .

    , .

    • plan. . , Terraform , , , .
    • , . , , . , create_before_destroy. , : apply, apply .
    • . , (, aws_security_group instance cluster_instance), , Terraform. — terraform state. terraform state mv, :

      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE — , , NEW_REFERENCE — , . , aws_security_group instance cluster_instance :

      $ terraform state mv \
         aws_security_group.instance \
         aws_security_group.cluster_instance

      Terraform, , aws_security_group.instance, aws_security_group.cluster_instance. terraform plan , , .

    • . . , Terraform . , , . plan create_before_destroy.


    API , AWS, . , , . , ; , , API-.

    , , API- AWS EC2. API «» (201 Created) , . , , AWS , , . , , , (404 Not Found). , EC2 AWS, , .

    API , . , AWS SDK , Terraform 6813 (https://github.com/hashicorp/terraform/issues/6813):

    $ terraform apply
    aws_subnet.private-persistence.2: InvalidSubnetID.NotFound:
    The subnet ID 'subnet-xxxxxxx' does not exist

    , (, ) - ( ID ), Terraform . ( 6813) , , Terraform . , . terraform apply , .

    «Terraform: ».

All Articles