Terraform AWS Cloud: Vernünftiges Infrastrukturmanagement
Veröffentlicht: 2022-03-11Das Schreiben einer Bewerbung ist nur ein Teil der Geschichte. Damit es von Wert ist, muss es an einem Ort bereitgestellt werden, an dem es skalierbar ist, es muss mit hoher Verfügbarkeit ausgeführt werden, es braucht Backups und so weiter.
Immer mehr Entwickler müssen diesen Bereitstellungsprozess zumindest verstehen. Dies zeigt sich zum Beispiel darin, dass DevOps heutzutage eine oft nachgefragte Rolle wird, wenn die Systemkomplexität zunimmt. Wir dürfen diese Veränderungen nicht ignorieren und müssen uns darüber im Klaren sein, wie Anwendungen so gestaltet werden, dass sie einfach bereitgestellt werden können.
Dies liegt auch im Interesse unserer Kunden: Sie beauftragen uns als Experten auf unseren Gebieten und erwarten von uns die Lieferung des gesamten Produkts, oft von Anfang bis Ende. Sie haben Anforderungen und wissen oft nicht, auf welchem Stack ihre Geschäftslösung läuft. Am Ende zählt der Geschäftswert des Produkts.
Einführung von Terraform
Bereitstellung und Infrastrukturverwaltung sind kein einfacher Prozess. Zusätzlich zu vielen sich ständig ändernden Fachkenntnissen müssen wir noch ein weiteres Tool oder einen neuen Workflow lernen. Wenn Sie dies bisher aufgeschoben haben, bietet Ihnen dieser Artikel die Gelegenheit, sich mit einem Ansatz für das Infrastrukturmanagement vertraut zu machen. Ich hoffe, dass Sie am Ende sicherer im Umgang mit Terraform sind und mehr über die möglichen Ansätze und Herausforderungen wissen. Sie sollten in der Lage sein, dieses Tool zu verwenden, um zumindest mit der Verwaltung eines Teils Ihrer Cloud-Infrastruktur zu beginnen.
Terraform ist eine Abstraktionsschicht, ja, und Abstraktionen sind undicht, da stimme ich zu. Aber am Ende sind wir im Geschäft, Probleme zu lösen und Abstraktionen zu verwalten. Dieser zielt darauf ab, uns bei unseren täglichen Aufgaben mehr Vernunft zu bieten.
Das Ziel
Ich werde erklären, was Terraform ist, wie es in das gesamte Ökosystem passt und wie es im Vergleich zu anderen, ähnlichen Tools abschneidet. Dann zeige ich Ihnen die Schritte, die zum Konfigurieren eines Terraform-Setups mit mehreren Umgebungen und für die Produktion für ein Team erforderlich sind. Ich erkläre die Grundlagen des Schreibens einer Terraform-Konfiguration – wie man Komplexität und doppelten Code mit gemeinsam nutzbaren Modulen handhabt.
Die Beispiele konzentrieren sich alle auf einen Cloud-Anbieter: Amazon Web Services (AWS). Dies ist nur eine Cloud, mit der ich die meiste Erfahrung habe, aber alle Informationen sollten auch für andere Clouds gelten.
Ich schließe mit einigen Anmerkungen, die ich gerne gewusst hätte, als ich anfing: einige Syntaxfehler, Macken und Fälle, in denen Terraform nicht mein bevorzugtes Werkzeug wäre.
Ich werde mich nicht auf die wesentlichen Details der Syntax konzentrieren. Sie können sich schnell damit vertraut machen, indem Sie die fantastische Dokumentation und Anleitungen lesen, die HashiCorp für Terraform bereitstellt. Ich möchte mich auf Dinge konzentrieren, die vielleicht nicht von Anfang an offensichtlich sind und die ich gerne gewusst hätte, bevor ich anfing, mit Terraform zu arbeiten. Hoffentlich weist Sie dies in die richtige Richtung und ermöglicht es Ihnen, Terraform als Werkzeug für zukünftige Projekte in Betracht zu ziehen.
Infrastrukturmanagement ist schwierig
Das Einrichten einer Umgebung für eine App in der Cloud umfasst mehrere Schritte. Wenn Sie sie nicht alle als detaillierte Checklisten aufschreiben und sie die ganze Zeit genau befolgen, werden Sie Fehler machen; wir sind schließlich Menschen. Diese Schritte sind schwer zu teilen. Sie müssen viele manuelle Verfahren dokumentieren, und Dokumente können schnell veraltet sein. Multiplizieren Sie das alles nun mit der Gesamtzahl der Umgebungen für eine einzelne App: dev , test/qa , stage und prod . Sie müssen auch über die Sicherheit für jeden von ihnen nachdenken.
Hat nicht jedes Tool eine Benutzeroberfläche, die ich verwenden und die Komplexität vergessen kann?
Sie haben UIs – die AWS Management Console ist ein Paradebeispiel. Aber diese Tools tun viel unter der Haube. Ein Klick auf die Benutzeroberfläche kann tatsächlich eine Kaskade von Änderungen hervorrufen, die schwer zu verstehen sind. Es gibt normalerweise keine Möglichkeit, Ihre Aktionen in der Benutzeroberfläche rückgängig zu machen (die üblichen „Sind Sie sicher?“-Eingabeaufforderungen reichen oft nicht aus). Außerdem ist es immer eine gute Idee, ein zweites Paar Augen zu haben, um die Änderungen zu überprüfen, aber wenn wir UIs verwenden, müssten wir mit dieser Person zusammensitzen oder unsere Änderungen überprüfen, nachdem sie vorgenommen wurden, was eher eine Prüfung als eine ist Beurteilung. Jeder Cloud-Anbieter hat seine eigene Benutzeroberfläche, die Sie beherrschen müssen. Benutzeroberflächen sind so konzipiert, dass sie einfach zu verwenden sind (nicht unbedingt einfach!) und daher anfällig für wahnhafte Ansätze wie „Dies ist nur eine winzige Änderung“ oder ein schneller Produktions-Hotfix, den Sie in 48 Stunden vergessen werden. Ein solches manuelles Klicken ist auch sehr schwer zu automatisieren.
Was ist mit CLI-Tools?
Sie wären für unsere Anwendungsfälle besser als UI-Tools. Sie neigen jedoch immer noch dazu, Änderungen von Hand vorzunehmen oder Bash-Skripte zu schreiben, die leicht außer Kontrolle geraten können. Außerdem hat jeder Anbieter seine eigenen CLI-Tools. Mit diesen Tools können Sie Ihre Änderungen nicht sehen, bevor Sie sie übernehmen. Glücklicherweise ist dies kein neues Problem und es gibt Tools, die dabei helfen.
Orchestrierung vs. Konfigurationsmanagement
Ich werde einige Kategorien von Tools und Praktiken definieren, die zur Verwaltung der Infrastruktur verwendet werden. Dies sind Orchestrierung und Konfigurationsmanagement. Sie können sie sich grob als deklarative und imperative Programmiermodelle vorstellen (denken Sie an Prolog vs. Python). Jedes hat seine eigenen Vor- und Nachteile, aber es ist am besten, sie alle zu kennen und das beste Werkzeug für einen bestimmten Job anzuwenden. Außerdem beinhaltet die Orchestrierung normalerweise eine höhere Abstraktionsebene als das Konfigurationsmanagement.
Orchestrierung
Die Orchestrierung ähnelt eher einem deklarativen Programmierparadigma. Ich stelle es mir gerne als Dirigent eines Orchesters vor. Die Arbeit eines Dirigenten wird von Wikipedia (Link) schön zusammengefasst als „die Kunst, die gleichzeitige Darbietung mehrerer Spieler oder Sänger durch den Einsatz von Gesten zu lenken“. Dirigenten kommunizieren den Takt und das Tempo, die Dynamik und das Cueing der Musik, aber die eigentliche Arbeit wird von einzelnen Musikern ausgeführt, die Experten im Spielen ihrer Musikinstrumente sind. Ohne eine solche Koordination wären sie nicht in der Lage, ein perfektes Stück aufzuführen.
Hier passt Terraform. Sie verwenden es, um einen Teil der IT-Infrastruktur zu verwalten: Sie sagen ihm, was bereitgestellt werden soll, und Terraform verbindet alles miteinander und führt alle erforderlichen API-Aufrufe durch. Es gibt ähnliche Tools in diesem Bereich; Eines der bekanntesten ist AWS Cloud Formation. Es hat eine bessere Unterstützung für Wiederherstellung und Rollbacks im Fehlerfall als Terraform, aber meiner Meinung nach auch eine steilere Lernkurve. Es ist auch nicht Cloud-agnostisch: Es funktioniert nur mit nur mit AWS.
Konfigurationsmanagement
Die komplementäre Seite dieser Praktiken ist der Ansatz des Konfigurationsmanagements. In diesem Paradigma geben Sie die genauen Schritte an, die ein Werkzeug ausführen muss, um zu einer bestimmten gewünschten Konfiguration zu gelangen. Die Schritte selbst mögen klein sein und mehrere Betriebssysteme unterstützen, aber Sie müssen aktiv über die Reihenfolge ihrer Ausführung nachdenken. Sie haben kein Bewusstsein für den aktuellen Zustand und die Umgebung (es sei denn, Sie programmieren sie damit) und werden als solche blind alle Schritte ausführen, die Sie ihnen geben. Dies kann zu einem als Configuration Drift bekannten Problem führen, bei dem Ihre Ressourcen langsam mit dem desynchronisieren, was sie ursprünglich darstellen sollten, insbesondere wenn Sie einige manuelle Änderungen an ihnen vorgenommen haben. Sie sind hervorragend darin, Dienste auf einzelnen Instanzen zu verwalten und bereitzustellen. Beispiele für Tools, die sich bei diesem Workflow auszeichnen, sind Chef, Puppet, Ansible und Salt.
Die Orchestrierung erzwingt einen Ansatz für Ihre Infrastruktur, bei dem Sie Ihre Ressourcen wie Rinder und nicht wie Haustiere behandeln. Anstatt jeden VPS manuell zu „pflegen“, können Sie ihn durch eine exakte Kopie ersetzen, wenn etwas schief geht. Ich meine nicht, dass es Ihnen einfach egal ist und Sie das Ding neu starten und auf das Beste hoffen.
Stattdessen sollten Sie das Problem im Code untersuchen und beheben und ihn dann bereitstellen.
Ansible (und andere CM-Tools) können zur Verwaltung der AWS-Infrastruktur verwendet werden, dies wäre jedoch mit viel Arbeit verbunden und fehleranfälliger, insbesondere wenn sich die Infrastruktur häufig ändert und an Komplexität zunimmt.
Eine wichtige Sache, an die Sie sich erinnern sollten, ist, dass Orchestrierungs- und Konfigurationsmanagementansätze nicht miteinander in Konflikt stehen. Sie sind kompatibel. Es ist vollkommen in Ordnung, eine Gruppe von EC2 (VPS)-Instances in einer AutoScaling-Gruppe zu haben, die von Terraform verwaltet wird, aber ein AWS Application Image (AMI) ausführt, das ein Snapshot der Festplatte ist, der mit zwingenden Schritten mit z. B. Ansible vorbereitet wurde . Terraform hat sogar ein Konzept von „Anbietern“, mit denen Sie externe Bereitstellungstools ausführen können, sobald eine Maschine hochgefahren ist.
Die Terraform-Dokumentation leistet hervorragende Arbeit, um dies weiter zu erklären und Ihnen dabei zu helfen, Terraform im gesamten Ökosystem zu platzieren.
Was ist Terraform?
Es ist ein von HashiCorp erstelltes Open-Source-Tool, mit dem Sie Ihre Infrastruktur als deklarative Konfigurationsdateien kodifizieren können, die versioniert und freigegeben sind und überprüft werden können.
Der Name HashiCorp sollte bekannt sein – sie stellen auch Nomad, Vault, Packer, Vagrant und Consul her. Wenn Sie eines dieser Tools verwendet haben, kennen Sie bereits die Qualität der Dokumentation, die lebendige Community und den Nutzen, den Sie von ihren Lösungen erwarten können.
Infrastruktur als Code
Terraform ist plattformunabhängig; Sie können damit Bare-Metal-Server oder Cloud-Server wie AWS, Google Cloud Platform, OpenStack und Azure verwalten. Im Terraform-Jargon werden diese Anbieter genannt. Sie können sich einen Eindruck von der Größenordnung verschaffen, indem Sie eine vollständige Liste der unterstützten Anbieter lesen. Es können mehrere Anbieter gleichzeitig verwendet werden, beispielsweise wenn Anbieter A VMs für Sie konfiguriert, Anbieter B jedoch DNS-Einträge konfiguriert und delegiert.
Bedeutet das, dass man mit einer Änderung in einer Konfigurationsdatei den Cloud-Anbieter wechseln kann? Nein, ich glaube nicht einmal, dass Sie das wollen würden, zumindest nicht auf automatisierte Weise. Das Problem ist, dass verschiedene Anbieter unterschiedliche Fähigkeiten, unterschiedliche Angebote, Abläufe, Ideen usw. haben können. Dies bedeutet, dass Sie unterschiedliche Ressourcen für einen anderen Anbieter verwenden müssen, um dasselbe Konzept auszudrücken. All dies kann jedoch weiterhin in einer einzigen, vertrauten Konfigurationssyntax erfolgen und Teil eines zusammenhängenden Arbeitsablaufs sein.
Wesentliche Teile eines Terraform-Setups
- Die Terraform-Binärdatei selbst, die Sie installieren müssen
- Die Quellcodedateien, dh Ihre Konfiguration
- Der Status (entweder lokal oder remote), der die von Terraform verwalteten Ressourcen darstellt (dazu später mehr)
Terraform-Konfiguration schreiben
Sie schreiben Terraform-Konfigurationscode in *.tf
-Dateien unter Verwendung der HCL-Sprache. Es gibt eine Option zur Verwendung des JSON-Formats ( *.tf.json
), aber es richtet sich eher an Maschinen und die automatische Generierung als an Menschen. Ich empfehle Ihnen, bei HCL zu bleiben. Ich werde nicht tief in die Syntax der HCL-Sprache eintauchen; Die offiziellen Dokumente beschreiben hervorragend, wie man HCL schreibt und wie man Variablen und Interpolationen verwendet. Ich erwähne nur das Nötigste, um die Beispiele zu verstehen.
Innerhalb von Terraform-Dateien haben Sie es hauptsächlich mit Ressourcen und Datenquellen zu tun. Ressourcen stellen Komponenten Ihrer Infrastruktur dar, z. B. eine AWS EC2-Instance, eine RDS-Instance, einen Route53-DNS-Eintrag oder eine Regel in einer Sicherheitsgruppe. Sie ermöglichen es Ihnen, sie innerhalb der Cloud-Architektur bereitzustellen und zu ändern.
Angenommen, Sie haben Terraform eingerichtet, wenn Sie ein terraform apply
ausgeben, würde der folgende Code eine voll funktionsfähige EC2-Instanz erstellen (nur bestimmte Eigenschaften werden angezeigt):
resource "aws_instance" "bastion" { ami = "ami-db1688a2" # Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM), SSD Volume Type - ami-db1688a2 instance_type = "t2.nano" key_name = "${var.key_name}" subnet_ vpc_security_group_ids = ["${aws_security_group.bastion.id}"] monitoring = "false" associate_public_ip_address = "true" disable_api_termination = "true" tags = { Name = "${var.project_tag}-bastion-${var.env}" Env = "${var.env}" Application ApplicationRole = "Bastion Host" Project = "${var.project_tag}" } }
Andererseits gibt es Datenquellen, die es ermöglichen, Daten über bestimmte Komponenten auszulesen, ohne sie zu verändern. Sie möchten die AWS-ID (ARN) eines von ACM ausgestellten Zertifikats erhalten? Sie verwenden eine Datenquelle. Der Unterschied besteht darin, dass Datenquellen das Präfix data_
vorangestellt wird, wenn auf sie in den Konfigurationsdateien verwiesen wird.
data "aws_acm_certificate" "ssl_cert" { domain = "*.example.com" statuses = ["ISSUED"] }
Das Obige verweist auf ein ausgestelltes ACM-SSL-Zertifikat, das zusammen mit AWS ALBs verwendet werden kann. Bevor Sie das alles tun, müssen Sie jedoch Ihre Umgebung einrichten.
Ordnerstruktur
Terraform-Umgebungen (und ihre Zustände) werden durch Verzeichnisse getrennt. Terraform lädt alle *.tf
Dateien in einem Verzeichnis in einen Namensraum, sodass die Reihenfolge keine Rolle spielt. Ich empfehle folgende Verzeichnisstruktur:
/terraform/ |---> default_variables.tf (1) /stage/ (2) |---> terraform.tfvars (3) |---> default_variables.tf (4) |---> terraform.tf (5) |---> env_variables.tf (6) /prod/ /<env_name>/
-
default_variables.tf
– definiert alle Top-Level-Variablen und optional ihre Standardwerte. Sie können in jeder Umgebung (verschachteltes Verzeichnis) mit Symlinks wiederverwendet werden. -
/stage/
– ein Verzeichnis, das die Konfiguration für eine ganz separate Umgebung enthält (hier mit dem Namenstage
, aber es kann alles sein). Alle Änderungen, die in diesem Ordner vorgenommen werden, sind völlig unabhängig von anderen Umgebungen (env – wieprod
), was Sie wollen, um zu vermeiden, dass die Produktionsumgebung durch Änderungen an derstage
durcheinander gebracht wird! -
terraform.tfvars
– Variablenwerte definieren..tfvars
Dateien ähneln.env
Dateien darin, dass sie diekey=val
-Paare für definierte Variablen enthalten. Dies gibt beispielsweise das AWSprofile
, den AWS-key_name
und den AWSkey_path
, die ich verwende. Es kann in Git ignoriert werden. -
default_variables.tf
– dies ist ein Symlink auf die Datei (2), die es uns ermöglicht, umgebungsunabhängige Variablen zu teilen, ohne uns zu wiederholen. -
terraform.tf
– dies ist die Hauptkonfiguration jeder Umgebung; Es enthält denterraform {}
, der das Back-End konfiguriert. Ich konfiguriere hier auch Anbieter . -
env_variables.tf
– diese Datei enthält env-spezifische Variablen. Ich tagge alle Ressourcen mitEnv=<env_name>
in AWS, daher definiert diese Datei normalerweise nur eine Variable:env
.
Natürlich ist dies nicht die einzige Möglichkeit, Ihre Umgebung zu strukturieren. Das ist einfach etwas, das für mich gut funktioniert hat, indem es eine klare Trennung von Anliegen ermöglicht hat.
Backend-Konfiguration
Ich habe den Terraform-Zustand bereits erwähnt. Dies ist ein wesentlicher Bestandteil des Terraform-Workflows. Sie fragen sich vielleicht, ob der Zustand tatsächlich erforderlich ist. Könnte Terraform nicht einfach ständig die AWS-API abfragen, um den aktuellen Zustand der Infrastruktur zu erfahren? Nun, wenn Sie darüber nachdenken, muss Terraform eine Zuordnung zwischen dem, was es in deklarativen Konfigurationsdateien verwaltet, und dem, was diese Dateien tatsächlich entsprechen (in der Umgebung des Cloud-Anbieters), aufrechterhalten. Beachten Sie, dass Sie sich beim Schreiben von Terraform-Konfigurationsdateien nicht um die IDs z. B. einzelner EC2-Instances oder die ARNs kümmern, die für von Ihnen veröffentlichte Sicherheitsgruppen erstellt werden. Intern muss Terraform jedoch wissen, dass ein bestimmter Ressourcenblock eine konkrete Ressource mit ID/ARN darstellt. Dies ist erforderlich, um Änderungen zu erkennen. Darüber hinaus wird state verwendet, um Abhängigkeiten zwischen Ressourcen zu verfolgen (ebenfalls etwas, worüber Sie normalerweise nicht nachdenken müssen!). Sie werden verwendet, um einen Graphen zu konstruieren, der (normalerweise) parallelisiert und ausgeführt werden kann. Wie immer empfehle ich Ihnen, die hervorragende Dokumentation zum Zustand von Terraform und seinem Zweck zu lesen.
Da State die Single Source of Truth für Ihre Architektur ist, müssen Sie sicherstellen, dass Sie und Ihr Team immer an der aktuellsten Version arbeiten und dass Sie keine Konflikte durch unsynchronisierten Zugriff auf den State erzeugen. Sie wollen Merge-Konflikte nicht in der Statusdatei lösen, glauben Sie mir.
Standardmäßig speichert Terraform den Zustand in einer Datei auf der Festplatte, die sich im aktuellen Arbeitsverzeichnis (jeder env) als Datei terraform.tfstate
befindet. Dies ist in Ordnung, wenn Sie wissen, dass Sie der einzige Entwickler bei der Arbeit sein werden oder nur mit Terraform lernen und experimentieren. Technisch gesehen könnten Sie es in einem Team zum Laufen bringen, da Sie den Status in ein VCS-Repository übertragen können. Aber dann müssten Sie dafür sorgen, dass alle immer an der neuesten Version des Staates arbeiten und dass niemand gleichzeitig Änderungen vornimmt! Dies ist im Allgemeinen ein großes Kopfzerbrechen und ich rate dringend davon ab. Außerdem müssten Sie, wenn jemand Ihrem Einzelentwicklerbetrieb beitritt, immer noch einen alternativen Ort für den Zustand konfigurieren.
Glücklicherweise ist dies ein Problem mit einer guten Lösung, die in Terraform integriert ist: dem sogenannten Remote State . Damit der Remote-Zustand funktioniert, müssen Sie das Back-End mit einem der verfügbaren Back-End-Anbieter konfigurieren. Das folgende Back-End-Beispiel basiert auf AWS S3 und AWS DynamoDB (AWS NoSQL-Datenbank). Sie könnten nur S3 verwenden, aber dann verlieren Sie den Mechanismus der Zustandssperre und Konsistenzprüfung (nicht empfohlen). Wenn Sie zuvor nur den lokalen Status verwendet haben, bietet Ihnen die Konfiguration eines Remote-Back-Ends die Möglichkeit, Ihren Status beim ersten Mal zu migrieren, sodass Sie nichts verlieren. Hier können Sie mehr über die Backend-Konfiguration lesen.
Leider gibt es ein Henne-Ei-Problem: Der S3-Bucket und die DynamoDB-Tabelle müssen manuell erstellt werden. Terraform kann sie nicht automatisch erstellen, da es noch keinen Zustand gibt! Nun, es gibt einige Lösungen wie https://github.com/gruntwork-io/terragrunt, die dies mit AWS CLI automatisieren, aber ich möchte nicht vom Hauptthema dieses Blogbeitrags abweichen.
Die wichtigsten Dinge, die Sie über die S3- und DynamoDB-Back-End-Konfiguration wissen sollten, sind:
- Aktivieren Sie die Versionsverwaltung für den S3-Bucket, um vor menschlichem Versagen und Murphy's Law geschützt zu sein.
- Die DynamoDB-Tabelle hat eine Ratenbegrenzung für Lese- und Schreibvorgänge (als Kapazität bezeichnet). Wenn Sie viele Änderungen am Remote-Status vornehmen, stellen Sie sicher, dass Sie entweder DynamoDB AutoScaling für diese Tabelle aktivieren oder ausreichend hohe R/W-Grenzwerte konfigurieren. Andernfalls erhält Terraform HTTP 400-Fehler von der AWS-API, wenn viele Aufrufe ausgeführt werden.
Zusammenfassend lässt sich sagen, dass die folgende Back-End-Konfiguration in terraform.tf
platziert werden kann, um den Remote-Zustand auf S3 und DynamoDB zu konfigurieren.
terraform { # Sometimes you may want to require a certain version of Terraform required_version = ">= 0.11.7" # Stores remote state, required for distributed teams # Bucket & dynamoDB table have to be created manually if they do not exist # See: https://github.com/hashicorp/terraform/issues/12780 backend "s3" { bucket = "my-company-terraform-state" key = "app-name/stage" region = "eu-west-1" # 5/5 R/W Capacity might not be enough for heavy, burst work (resulting in 400s). Consider enabling Auto Scaling on the table. # See: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ProvisionedThroughput.html dynamodb_table = "terraform-state-lock-table" } }
Dies ist eine Menge auf einmal, aber denken Sie daran, dass Sie dies einmal für jede Umgebung tun und es dann vergessen können. Wenn Sie noch mehr Kontrolle über die Zustandssperre benötigen, gibt es HashiCorp Terraform Enterprise, aber ich werde es hier nicht behandeln.
Anbieter
Damit dieses Backend überhaupt erreichbar ist und überhaupt mit unserem Cloud-Provider kommunizieren kann, müssen wir den sogenannten Provider konfigurieren. Der folgende Block kann in der Datei terraform.tf
platziert werden (für jede Umgebung):
provider "aws" { profile = "${var.profile}" region = "${var.region}" version = "~> 1.23.0" }
Variablen für profile
und region
werden in der Datei terraform.tfvars
gespeichert, die ignoriert werden kann. Die profile
bezieht sich auf ein benanntes Profil mit Sicherheitsanmeldeinformationen für die AWS-Cloud unter Verwendung der Standard-Anmeldeinformationsdatei. Beachten Sie, dass ich auch einige Versionseinschränkungen festlege. Sie möchten nicht, dass Terraform Ihre Provider-Plug-ins ohne Ihr Wissen bei jeder Back-End-Initialisierung aktualisiert. Zumal es eine Version 2.x des AWS-Anbieters gibt, die ein sorgfältiges Upgrade erfordert.

Backend-Initialisierung
Jede Backend-Konfiguration erfordert zu Beginn und bei jeder Änderung einen Initialisierungsschritt. Bei der Initialisierung werden auch Terraform-Module konfiguriert (dazu später mehr). Wenn Sie diese also hinzufügen, müssen Sie auch den Init-Schritt erneut ausführen. Dieser Vorgang kann sicher mehrmals ausgeführt werden. Beachten Sie, dass während der Initialisierung des Back-Ends nicht alle Variablen von Terraform gelesen werden können, um den Status zu konfigurieren, und dies auch nicht aus Sicherheitsgründen (z. B. geheime Schlüssel) sein sollte. Um dies zu umgehen und in unserem Fall ein anderes AWS-Profil als das Standardprofil zu verwenden, können Sie die Option -backend-config
verwenden, wobei k=v
Variablenpaare akzeptiert werden. Dies wird Teilkonfiguration genannt.
terraform init -backend-config=profile=<aws_profile_name>
Terraform-Dateien teilen sich einen Bereich, der auf ein bestimmtes Verzeichnis beschränkt ist. Das bedeutet, dass ein Unterordner nicht direkt mit dem Code des übergeordneten Verzeichnisses verbunden ist. Es definiert jedoch ein Modul, das die Wiederverwendung von Code, das Komplexitätsmanagement und die gemeinsame Nutzung ermöglicht.
Der Arbeitsablauf
Der allgemeine Arbeitsablauf beim Arbeiten mit Terraform-Code sieht wie folgt aus:
- Schreiben Sie die Konfiguration für Ihre Infrastruktur.
- Sehen Sie, welche tatsächlichen Änderungen vorgenommen werden (
terraform plan
). - Führen Sie optional genau die Änderungen aus, die Sie in Schritt 2 gesehen haben (
terraform apply
). - GOTO 1
Terraform-Plan
Der Befehl Terraform plan
zeigt Ihnen eine Liste der Änderungen an, die an Ihrer Infrastruktur vorgenommen werden, wenn Sie den Befehl apply
ausführen. Es ist sicher, den plan
mehrmals auszugeben, da er allein nichts ändert.
Wie man einen Plan liest
Objekte in Terraform (Ressourcen und Datenquellen) sind anhand ihrer vollständig qualifizierten Namen leicht zu identifizieren.
- Im Falle von Ressourcen könnte die ID wie folgt aussehen:
<resource_type>.<resource_name>
– z. B.aws_ecs_service.this
. - Im Fall von Ressourcen innerhalb von Modulen haben wir einen zusätzlichen Modulnamen:
module.<module_name>.<resource_type>.<resource_name>
– z. B.module.my_service_ecs_service_task.aws_ecs_service.this
. - Datenquellen (innerhalb und außerhalb eines Moduls):
(module.<module_name>).data.<resource_type>.<resource_name>
– z. B.module.my_service_ecs_service_task.data.aws_ecs_task_definition.this
.
Der Ressourcentyp ist spezifisch für einen bestimmten Anbieter und enthält normalerweise seinen Namen ( aws_…
). Die vollständige Liste der verfügbaren Ressourcen für AWS finden Sie in den Dokumenten.
Es gibt fünf Aktionen, die ein Plan für bestimmte Ressourcen anzeigt:
-
[+]
Hinzufügen – Eine neue Ressource wird erstellt. -
[-]
Zerstören – Eine Ressource wird vollständig zerstört. -
[~]
An Ort und Stelle ändern – Eine Ressource wird geändert und ein oder mehrere Parameter werden geändert. Dies ist im Allgemeinen sicher. Terraform zeigt Ihnen, welche Parameter geändert werden und wie (falls möglich). -
[- / +]
– Die Ressource wird entfernt und dann mit neuen Parametern neu erstellt . Dies geschieht, wenn eine Änderung an einem Parameter vorgenommen wurde, der nicht direkt geändert werden kann. Terraform zeigt Ihnen, welche Änderungen eine Neuerstellung einer Ressource erzwingen, mit dem folgenden Kommentar in Rot:(forces new resource)
. Dies ist potenziell gefährlich, da es einen Zeitraum gibt, in dem die Ressource überhaupt nicht existiert. Es kann auch andere, verbundene Abhängigkeiten unterbrechen. Ich würde empfehlen, solche Änderungen zu umgehen, es sei denn, Sie wissen, was die Folgen sein werden, oder kümmern sich nicht um eine Ausfallzeit. -
[<=]
– Eine Datenquelle wird gelesen. Dies ist ein schreibgeschützter Vorgang.
Oben ist ein Beispielplan. Die Änderungen, die ich vorgenommen habe, waren:
- Der
instance_type
der ersten Bastion-Instanz wurde geändert - Eine zweite Bastion-Instanz wurde hinzugefügt
- Der Name einer Sicherheitsgruppe wurde geändert
Beachten Sie, dass die letzte Änderung eine Sicherheitsgruppenregel ist, die automatisch eine Änderung des Namens der übergeordneten Gruppe erkannt hat. Meiner Meinung nach ist der ganze Plan sehr lesenswert. Einige Parameterwerte werden als <computed>
angezeigt – dies bedeutet nicht, dass sie geändert werden, sondern dass sie zu diesem Zeitpunkt nicht abgerufen und angezeigt werden können (wie der SG-Name, der noch nicht erstellt wurde). Denken Sie daran, dass während des plan
nur geänderte Ressourcen angezeigt werden. Alles, was weggelassen wird, wird nicht berührt.
Normalerweise akzeptieren Terraform-Befehle zusätzliche Parameter. Der wichtigste Parameter des Befehls plan ist die Option -out
, die Ihren Plan auf der Festplatte speichert. Dieser gespeicherte Plan kann dann (genau wie gespeichert) durch den Apply-Befehl ausgeführt werden. Dies ist sehr wichtig, da es sonst zwischen Ihrer plan
und der apply
zu einer Änderung kommen könnte, zB durch Kollegen. Beispiel:
-
plan
ausstellen und prüfen, ob er gut aussieht. - Jemand ändert den Status, ohne dass Sie es wissen.
- Issue
apply
und – hoppla, es hat etwas anderes als geplant funktioniert!
Glücklicherweise wurde dieser Arbeitsablauf in Terraform v0.11.0 verbessert. Seit dieser Version präsentiert Ihnen der Befehl apply
automatisch einen Plan, den Sie dann genehmigen müssen (indem Sie explizit yes
eingeben). Der Vorteil ist, dass, wenn Sie diesen Plan anwenden, er genau wie dargestellt ausgeführt wird.
Eine weitere nützliche Option ist -destroy
, die Ihnen den Plan zeigt, der zur Zerstörung aller von Terraform verwalteten Ressourcen führen wird. Mit der Option -target
können Sie auch bestimmte Ressourcen für die Zerstörung auswählen.
Terraform anwenden
Wenn wir einen bestimmten Plan anwenden, geht Terraform aus und sperrt unseren Status, um die Exklusivität zu gewährleisten. Anschließend ändert es die Ressourcen und überträgt am Ende einen aktualisierten Zustand. Beachten Sie, dass einige Ressourcen länger dauern als andere. Beispielsweise kann die Erstellung einer AWS RDS-Instance länger als 12 Minuten dauern, und Terraform wartet, bis dies abgeschlossen ist. Offensichtlich ist Terraform schlau genug, nicht jede andere Operation dadurch zu blockieren. Es erstellt einen gerichteten Graphen der angeforderten Änderungen und verwendet Parallelität, um die Ausführung zu beschleunigen, wenn es keine gegenseitigen Abhängigkeiten gibt.
Ressourcen importieren
Häufig werden Terraform und andere „Configuration as Code“-Lösungen schrittweise in eine bereits vorhandene Umgebung eingeführt. Der Übergang ist wirklich einfach. Grundsätzlich wird alles, was nicht in Terraform definiert ist, nicht verwaltet und unverändert gelassen. Terraform kümmert sich nur um das, was es verwaltet. Natürlich ist es möglich, dass dies zu Problemen führt – zum Beispiel, wenn Terraform auf einen Endpunkt angewiesen ist, der außerhalb seiner Konfiguration existiert und dann manuell zerstört wird. Terraform weiß davon nichts und kann diese Ressource daher bei Zustandsänderungen nicht neu erstellen, was zu Fehlern von der API während der Planausführung führt. Das ist nichts, worüber ich mir Sorgen machen würde; Die Vorteile der Einführung von Terraform überwiegen die Nachteile bei weitem.
Um den Einführungsprozess ein wenig zu vereinfachen, enthält Terraform den import
. Es wird verwendet, um bereits vorhandene externe Ressourcen in den Terraform-Zustand einzuführen und es ihm zu ermöglichen, diese Ressourcen zu verwalten. Leider ist Terraform nicht in der Lage, Konfigurationen für diese importierten Module automatisch zu generieren, zumindest zum Zeitpunkt des Schreibens. Es gibt Pläne für diese Funktionalität, aber im Moment müssen Sie zuerst die Ressourcendefinition in Terraform schreiben und diese Ressource dann importieren, um Terraform anzuweisen, mit der Verwaltung zu beginnen. Nach dem Import in den Status (und während der Planung der Ausführung) sehen Sie alle Unterschiede zwischen dem, was Sie in die .tf
Dateien geschrieben haben, und dem, was tatsächlich in der Cloud vorhanden ist. Auf diese Weise können Sie die Konfiguration weiter optimieren. Idealerweise sollten sich keine Änderungen zeigen, was bedeuten würde, dass die Terraform-Konfiguration 1:1 widerspiegelt, was bereits in der Cloud ist.
Module
Module sind ein wesentlicher Bestandteil der Terraform-Konfiguration, und ich empfehle Ihnen, sie anzunehmen und häufig zu verwenden. Sie bieten Ihnen die Möglichkeit, bestimmte Komponenten wiederzuverwenden. Ein Beispiel wäre der AWS ECS-Cluster, der zum Ausführen von Docker-Containern verwendet wird. Damit ein solcher Cluster funktioniert, müssen viele separate Ressourcen konfiguriert werden: der Cluster selbst, die Startkonfiguration, die separate EC2-Instances verwalten würde, Container-Repositories für Images, die Autoscaling-Gruppe und -Richtlinie und so weiter. Normalerweise benötigen Sie separate Cluster für separate Umgebungen und/oder Anwendungen.
Eine Möglichkeit, dies zu umgehen, wäre das Kopieren und Einfügen der Konfiguration, aber das ist offensichtlich eine kurzsichtige Lösung. Bei den einfachsten Aktualisierungen würden Sie Fehler machen.
Mit Modulen können Sie all diese separaten Ressourcen in einem Konfigurationsblock (Modul genannt) kapseln. Module definieren Eingänge und Ausgänge , die die Schnittstellen sind, über die sie mit „der Außenwelt“ (oder dem aufrufenden Code) kommunizieren. Darüber hinaus können Module in anderen Modulen verschachtelt werden, sodass Sie schnell ganze separate Umgebungen erstellen können.
Lokale und Remote-Module
Ähnlich wie bei state können Sie lokale Module oder Remote-Module haben. Lokale Module werden zusammen mit Ihrer Terraform-Konfiguration gespeichert (in einem separaten Verzeichnis, außerhalb jeder Umgebung, aber im selben Repository). Dies ist in Ordnung, wenn Sie eine einfache Architektur haben und diese Module nicht gemeinsam nutzen.
Dies hat jedoch Einschränkungen. Es ist schwierig, diese Module zu versionieren und gemeinsam zu nutzen. Die Versionsverwaltung ist wichtig, da Sie vielleicht v1.0.0 Ihres ECS-Moduls in der Produktion verwenden möchten, aber mit v1.1.0 in einer Staging-Umgebung experimentieren möchten. Wenn das Modul neben Ihrem Code gespeichert wurde, würde sich jede Änderung am Modulcode in jeder Umgebung widerspiegeln (sobald apply
ausgeführt wird), was normalerweise unerwünscht ist.
Ein praktischer Ansatz zur Versionierung von Modulen besteht darin, sie alle in einem separaten Repository abzulegen, z. B. your-company/terraform-modules. Wenn Sie dann in Ihrer Terraform-Konfiguration auf diese Module verweisen, können Sie einen VCS-Link als Quelle verwenden:
module "my-module" { source = "[email protected]:your-company/terraform-modules.git//modules/my-module?ref=v1.1.0" ... }
Hier verweise ich auf v1.1.0 von my-module (bestimmter Pfad), das ich unabhängig von anderen Versionen desselben Moduls in verschiedenen Umgebungen testen kann.
Abgesehen davon gibt es das Problem der Auffindbarkeit und gemeinsamen Nutzung von Modulen. Sie sollten sich bemühen, gut dokumentierte und wiederverwendbare Module zu schreiben. Normalerweise haben Sie unterschiedliche Terraform-Konfigurationen für verschiedene Apps und möchten möglicherweise dasselbe Modul für sie freigeben. Ohne sie in ein separates Repo zu extrahieren, wäre dies sehr schwierig.
Module verwenden
Module können in Terraform-Umgebungen einfach referenziert werden, indem ein spezieller Modulblock definiert wird. Hier ist ein Beispiel für einen solchen Block für ein hypothetisches ECS-Modul:
module "my_service_ecs_cluster" { source = "../modules/ecs_cluster" cluster_name = "my-ecs-service-${var.env}" repository_names = [ "my-ecs-service-${var.env}/api", "my-ecs-service-${var.env}/nginx", "my-ecs-service-${var.env}/docs", ] service_name = "my-ecs-service-${var.env}" ecs_instance_type = "t2.nano" min_size = "1" max_size = "1" use_autoscaling = false alb_target_group_arn = "${module.my_alb.target_group_arn}" subnets = "${local.my_private_subnets}" security_groups = "${aws_security_group.my_ecs.id}" key_name = "${var.key_name}" env_tag = "${var.env}" project_tag = "${var.project_tag}" application_tag = "${var.api_app_tag}" asg_tag = "${var.api_app_tag}-asg" }
Alle übergebenen Optionen (mit Ausnahme einiger globaler wie source
) wurden in der Konfiguration dieses Moduls als Eingänge (Variablen) definiert.
Modulregistrierung
Vor kurzem hat HashiCorp eine offizielle Terraform-Modulregistrierung gestartet. Das sind großartige Neuigkeiten, da Sie jetzt auf das Wissen der Community zurückgreifen können, die bereits kampferprobte Module entwickelt hat. Darüber hinaus haben einige von ihnen das Abzeichen „HashiCorp Verified Module“, was bedeutet, dass sie überprüft und aktiv gewartet werden und Ihnen zusätzliches Vertrauen geben.
Früher mussten Sie entweder Ihre eigenen Module von Grund auf neu schreiben (und aus Ihren Fehlern lernen) oder Module verwenden, die auf GitHub und anderen Orten veröffentlicht wurden, ohne Garantien bezüglich ihres Verhaltens (abgesehen vom Lesen des Codes!).
Gemeinsame Nutzung von Daten zwischen Umgebungen
Idealerweise sollten Umgebungen vollständig getrennt sein, auch wenn unterschiedliche AWS-Konten verwendet werden. Tatsächlich gibt es Fälle, in denen eine Terraform-Umgebung einige Informationen in einer anderen Umgebung verwendet. This is especially true if you are gradually converting your architecture to use Terraform. One example might be that you have a global
env that provides certain resources to other envs.
Let's say env global
shares data with stage
. For this to work, you can define outputs at the main level of the environment like so:
output "vpc_id" { value = "${module.network.vpc_id}" }
Then, in the stage
environment, you define a datasource that points to the remote state of global
:
data "terraform_remote_state" "global" { backend = "s3" config { bucket = "my-app-terraform-state" key = "terraform/global" region = "${var.region}" dynamodb_table = "terraform-state-lock-table" profile = "${var.profile}" } }
Now, you can use this datasource as any other and access all the values that were defined in global
's outputs:
vpc_
Words of Caution
Terraform has a lot of pros. I use it daily in production environments and consider it stable enough for such work. Having said that, Terraform is still under active development. Thus, you will stumble on bugs and quirks.
Where to Report Issues and Monitor Changes
First of all, remember: Terraform has a separate core repo and repositories for each provider (eg, AWS). If you encounter issues, make sure to check both the core repo and the separate provider repositories for issues and/or opened pull requests with fixes. GitHub is really the best place to search for bugs and fixes as it is very active and welcoming.
This also means that provider plugins are versioned separately, so make sure you follow their changelogs as well as the core one. Most of the bugs I have encountered were resolved by upgrading the AWS provider which already had a fix.
Can't Cheat Your Way out of Cloud Knowledge
You cannot use Terraform to configure and manage infrastructure if you have no knowledge of how a given provider works. I would say this is a misconception and not a downside, since Terraform has been designed to augment and improve the workflow of configuration management and not to be some magic dust that you randomly sprinkle around and—poof! Environments grow! You still need a solid knowledge of a security model of each cloud, how to write, eg, AWS policies, what resources are available, and how they interact.
Prefer Separate Resources That Are Explicitly linked
There are certain resources—for example, the AWS security group or AWS route table—that allow you to configure the security rules and routes respectively, directly inside their own block. This is tempting, as it looks like less work but in fact will cause you trouble. The problems start when you are changing those rules on subsequent passes. The whole resource will be marked as being changed even if only one route/security rule is being introduced. It also gives implicit ordering to those rules and makes it harder to follow the changes. Thankfully, mixing those both approaches is not allowed now (see the note).
Best-practice example, with explicitly linked resources:
resource "aws_security_group" "my_sg" { name = "${var.app_tag}-my-sg" ... } resource "aws_security_group_rule" "rule_one" { security_group_ ... } resource "aws_security_group_rule" "rule_two" { security_group_ ... }
Terraform plan
Doesn't Always Detect Issues and Conflicts
I already mentioned this in the case where you were managing resources with Terraform that were relying on other, unmanaged infrastructure. But there are more trivial examples—for example, you will get an error if your EC2 instance has Termination Protection enabled, even though plan
would show you it's OK to destroy it. You can argue that this is what Termination Protection has been designed for, and I agree, but there are more examples of things you can do in theory/on plan but when executed will deadlock or error out. For example, you cannot remove a network interface if something is using it—you get a deadlock without an option to gracefully recover.
Syntax Quirks
There are also quirks related to how HCLv1 (the syntax language Terraform uses) has been designed. It has a couple of frustrating quirks. There is work underway to provide an improved version of the parser for HCLv2. The best way to read on the current limitations and the plan to overcome them is this fantastic blog series. In the meantime, there are workarounds for most of those issues. They are not pretty and they will fail once v0.12 comes out, but hey, it is what it is.
When State Update Fails
It sometimes happens that Terraform is not able to correctly push an updated state. This is usually due to underlying network issues. The solution is to retry the state update instead of running apply again, which will fork the state .
Another issue might happen when state lock (the synchronization primitive that prevents multiple users to update the same state) fails to be taken down by Terraform. This involves running terraform force-unlock
with the lock ID to take it down manually.
Thankfully, in case of such problems, Terraform provides you with a good description and steps you need to make to fix it.
Not Everything Is Fun to Manage Through Terraform
There are certain cases where Terraform is not my tool of choice. For example, configuring AWS CodePipeline and CodeBuild projects (AWS equivalent of CI/CD pipeline) is cumbersome when done through Terraform. You need to define each step through very verbose configuration blocks and things like “Login via GitHub” are a lot more complicated than using the UI. Of course, it's still possible if you prefer to have it codified. Well, I guess it's a good candidate for a well-written module!
Same thing goes for managing AWS API Gateway endpoints. In this case, using a dedicated serverless framework would be a better option.
When configuring AWS resources with Terraform, you will find yourself writing a lot of policies. Policies that would otherwise often be auto-generated for you (when using the UI). For those, I'd recommend the AWS Visual Editor and then copying the resulting policy JSON into Terraform.
Fazit
Using Terraform has been fun and I'll continue doing so. Initial steps can be a bumpy ride, but there are more and more resources that help to ease you in.
I'd definitely recommend taking Terraform for a spin and simply playing with it. Remember, though—be safe and test it out on a non-essential account. If you are eligible for AWS Free Tier, use it as a 12-month free trial. Just be aware it has limitations as to what you can provision. Otherwise, just make sure you spin the cheapest resources, like t3.nano instances.
I highly recommend extensions for Terraform support in various code editors. For Visual Studio Code, there is one with syntax highlighting, formatting, validation and linting support.
It's always valuable to learn new things and evaluate new tools. I found that Terraform helped me immensely in managing my infrastructure. I think working with Terraform will only get easier and more fun, especially once v0.12.0 ships with a major upgrade to the HCL syntax and solve most of the quirks. The traction and community around Terraform are active and vibrant. You can find a lot of great resources on things I didn't manage to cover in a single blogs post, eg, a detailed guide on how to write modules.