Architektur
| Julian Alarcon |
23 Juli 2019
Ich habe diesen Post für Leute geschrieben, die planen, bei einem Projekt Terraform zu verwenden – in der Hoffnung, dass er ihnen helfen kann, durch meine gesammelten Erkenntnisse etwas Zeit zu sparen. Und ja, der Titel stimmt: Ich wünschte, ich hätte die meisten dieser Dinge gewusst, bevor ich angefangen habe, mit Terraform zu arbeiten. Die 11 Lektionen sind auf zwei Posts verteilt und dies ist Teil 2 (Teil 1 hier).
5. OUTPUTS DEFINIEREN
Outputs zeigen die Informationen, die benötigt werden, nachdem Terraform-Templates deployt sind. Sie werden auch innerhalb von Modulen benutzt, um Informationen zu exportieren.
output "instance_id" {value = "${aws_instance.instance_ws.id}"
}
Wenn ihr sie in Modulen benutzt, müssen zwei Outputs definiert werden, einer im Modul und ein ähnlicher in den Config-Dateien. Diese Outputs sollten explizit definiert sein. Output-Informationen werden in einer State-Datei gespeichert und können von anderen Terraform-Templates abgefragt werden.
Ich empfehle, Outputs für Ressourcen auch dann zu definieren, wenn ihr sie gerade nicht benutzt. Prüft die Ressource und die von ihr bereitgestellten Outputs und überlegt euch gut, welche Informationen für eure Infrastruktur brauchbar sind, wenn ihr diese Terraform-Ressource benutzt. So müsst ihr seltener zurückgehen und eure Module und Ressourcen anpassen, weil ein Output von einer neuen Ressource benötigt wird, die ihr gerade definiert.
Da ihr eure Dateien vermutlich auch organisieren wollt, könnt ihr die Output-Dateien in einer speziellen Datei outputs.tf speichern.
6. DEFINIERT SELBST DIE KLEINSTEN KOMPONENTEN
Wenn man Terraform benutzt, gibt es die Tendenz, sich am Anfang auf die größeren Komponenten zu konzentrieren, was dazu führen kann, dass man kleinere Komponenten übersieht, was frustrierend und technisch aufwendig sein kann. Wenn ihr Komponenten erstellt, nutzt Terraform die Default-Optionen eures Providers, wenn die Optionen nicht vorher definiert sind. Es ist wichtig, die benutzten Default-Komponenten zu kennen und sie in Terraform zu definieren, da es sein kann, dass ihr sie in der Zukunft braucht. Die Default-Optionen könnten von eurem Provider ohne Vorankündigung verändert werden, mit dem Ergebnis, dass ihr dann eventuell zwei verschiedene Komponentensets habt oder die Properties der Komponenten verändert sind.
Ein Beispiel sind die Route Tables, die zu Beginn eines Projekts manchmal nicht im Fokus stehen, oder Elastic Container Repositories, die zwar einfach zu definieren sind, aber nicht immer die höchste Prio haben.
name = "name_of_repo"
}
7. TERRAFORM-INTERPOLATIONEN
Die Interpolationssyntax ist ein mächtiges Feature, mit dem ihr auf Variablen referenzieren, Attribute und Funktionen aufrufen könnt etc.String variables -> ${var.foo}
Map variables -> ${var.amis["us-east-1"]}
List variables -> ${var.subnets[idx]}
Wenn ihr Daten von einem Modul-Output oder vom State einer bestimmten Ressource abrufen müsst, könnt ihr die Syntax module. oder data. nutzen, um die gewünschten Attribute aufzurufen.
# Informationen von einem Modul holen
output "my_module_bar_value_from_module" {
value = ${module.my_module.bar}
}
# Informationen von einer Datenquelle holen
resource "aws_instance" "web" {
ami = "${data.aws_ami.my_amy.id}"
instance_type = "t3.micro"
}
Ihr könnt auch arithmetische oder logische Operationen mit Interpolationen benutzen. Im folgenden Code-Schnipsel wird die VPN-Ressource miteinbezogen, wenn die Evaluation der var.something wahr ergibt (1, true).
resource "aws_instance" "vpn" {
count = "${var.something ? 1 : 0}"
}
Weitere Informationen zu den unterstützen Interpolationen findet ihr in der Terraform-Dokumentation.
8. UMGEBUNGSMANAGEMENT
Workspaces in Terraform
Ursprünglich wurden diese Umgebungen in Version 0.9 von Terraform „Environments“ genannt, aber seit Version 0.10 hat Terraform dieses Feature umbenannt in Workspaces.
Mit dem Befehl terraform workspace kann man neue Workspaces definieren und bestehende Workspaces ändern oder löschen.
Usage: terraform workspace
Create, change and delete Terraform workspaces.
Subcommands:
delete Delete a workspace
list List Workspaces
new Create a new workspace
select Select a workspace
show Show the name of the current workspace
Die Nutzung von Workspaces hat einige Vorteile:
1. Sie werden von Hashicorp definiert, daher könnten in Zukunft verbesserte Features entwickelt werden.
2. Sie reduzieren den Bedarf an Code.
Allerdings bringen Workspaces auch einige Herausforderungen mit sich:
1. Sie sind noch in einer recht frühen Umsetzungsphase.
2. Sie werden noch nicht von allen Backends unterstützt.
3. Zum Zeitpunkt des Deployments (terraform apply) ist nicht klar, welcher Workspace genutzt wird (terraform workspace show).
Ordnerstruktur
Eine einfache und hilfreiche Option ist es, die Komponenten in Ordnern nach der Umgebung zu definieren.project-01
├── dev
│ ├── clusters
│ │ └── ecs_cluster
│ │ ├── service01
│ │ │ ├── main.tf
│ │ │ ├── outputs.tf
│ │ │ ├── variables.tf
│ │ ├── service02
│ │ ├── service03
│ │ ├── service04
│ ├── database
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── elasticsearch
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ └── vpc
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── global
│ │ └── web_login
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ └── terraform_state
│ ├── output.tf
│ ├── variables.tf
│ └── main.tf
├── prod
│ ├── clusters
│ ├── …
├── qa
│ ├── clusters
│ ├── …
└── README.md
Vorteile:
1. Eine klare Definition der zu deployenden Umgebung (im Ordnerpfad)
2. Die am häufigsten benutzte und eine ergebnissichere Option für public Deployments
3. Terraform-States können ohne Probleme für jeden Umgebungsorder definiert werden
4. Der Name der Outputs für jede Umgebung wird spezifiziert
Probleme:
1. Doppelter Code
2. Eine überwältigende Menge an Ordnern in größeren Projekten
3. Man muss immer Code kopieren und Kernwerte ersetzen
Welche Variante solltet ihr also wählen? Die Ordnerstruktur nach Umgebungen ist einfacher zu benutzen und eine aktuelle Empfehlung, um die verschiedenen Komponenten eurer Infrastruktur aufzuteilen.
9. EMPFOHLENER WORKFLOW FÜR BEFEHLE
Der Befehl terraform hat mehrere Optionen, deshalb möchte ich euch den folgenden Workflow für den Ablauf des Deployment empfehlen:
1. Module downloaden und Update erzwingen
Der Befehl terraform init initialisiert den Workspace, lädt die Provider und Module herunter und setzt das Terraform-State-Backend auf. Wenn aber ein Modul bereits heruntergeladen wurde, erkennt Terraform nicht, dass eine neuere Version des Moduls verfügbar ist. Mit terraform get kann man die Module herunterladen, aber es ist empfehlenswert, die Option ‑update zu nutzen, um ein Update zu erzwingen.
terraform get -update
2. Wenn ihr die neuesten Module habt, müsst ihr den Terraform-Workspace initialisieren, um die Provider und Module herunterzuladen (bereits mit dem ersten Befehl geschehen) und das Terraform-State-Backend zu initialisieren:
terraform init
Ihr könnt hier auch die Option -upgrade nutzen, um die Aktualisierung von Providern, Plugins und Modulen zu erzwingen.
3. Vor dem Deployment kann Terraform einen Plan für das Deployment definieren (was wird erstellt, modifiziert und gelöscht). Dieser Plan lässt sich gut in einer Pipeline benutzen, um die Änderungen vor Initialisierung des echten Deployments zu überprüfen.
Wichtiger Hinweis: Die Option plan ist nicht immer 1:1 deckungsgleich mit dem Deployment. Wenn eine Komponente bereits deployt ist oder wenn keine ausreichenden Berechtigungen vorhanden sind, dann könnte der Plan durchlaufen, aber das Deployment fehlschlagen.
terraform plan
Um die Dinge etwas zu vereinfachen, kann man auch alles in einer Bash-Line laufen lassen: terraform get -update && terraform init && terraform plan
4. Wenn ihr beim letzten Deployment-Schritt angekommen seid, wird Terraform einen Plan erstellen und euch fragen, ob ihr die gewünschte Architektur deployen wollt oder nicht (ja/nein).
terraform apply
Nach dem Deployment kann Terraform nichts rückgängig machen. Falls beim Deployment also ein Fehler angezeigt wird, sollte das Problem dann direkt gelöst werden. Man kann auch das Deployment „zerstören“ (terraform destroy), aber dabei wird alles zerstört und nicht die letzten Änderungen rückgängig gemacht.
Die Anwendung einer bestimmten Änderung oder die Zerstörung einer bestimmten Ressource kann man mit der Option -target spezifizieren.
10. EXTERNE DATEN ABRUFEN
Durch die Nutzung von Datenquellen kann eine Terraform-Konfiguration auch auf Daten aufbauen, die außerhalb von Terraform definiert sind oder durch eine andere, separate Terraform-Konfiguration. Dazu gehören:
■ Daten von einem Remote-State, um States von anderen Terraform-Deployments abzurufen >
backend = "s3"
config {
bucket = "ci-cd-terraform-state"
key = "vpc/terraform.tfstate"
region = "us-east-1"
}
}
■ Daten von AWS oder externen Systemen >
data "aws_ami" "linux" {
most_recent = true
filter {
name = "name"
values = ["amzn2-ami-hvm-2.0.20180810-x86_64-gp2*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["137112412989"]
}
11. NEUE UND INTERESSANTE INFORMATIONEN
Terratest
Um euren Code zu testen, könnt ihr ein großartiges Tool namens Terratest benutzen, welches das das Unit-Testing-Framework von Go verwendet.
Terraform Version 0.12
Die nächste Terraform-Version wurde vor einigen Monaten angekündigt und wird einige Interpolationen verbessern und einige Änderungen in der HCL-Sprache mit sich bringen.
Übersetzt aus dem Englischen