<img height="1" width="1" style="display:none;" alt="" src="https://px.ads.linkedin.com/collect/?pid=4958233&amp;fmt=gif">
 
RSS Feed

Architektur | Julian Alarcon |
25 Juni 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 hier ist Teil 1.

1. BENUTZT IMMER TERRAFORM 

Terraform ist ein Tool, um Infrastruktur sicher und effizient zu bauen, zu ändern und zu versionieren, und hilft euch dabei, eure Infrastructure as Code (IaC) zu definieren. Wenn ihr Terraform verwendet und dann andere Tools als Terraform für Änderungen nutzt (z. B. eine Web-Konsole, CLI-Tools oder SDKs), dann werden Inkonsistenzen entstehen und die Stabilität und Zuverlässigkeit der Infrastruktur beeinträchtigt.

Terraform wird versuchen, den vorher definierten Zustand beizubehalten, und alle manuellen Änderungen sind dann nicht auf dem definierten Version Control System (VCS). Das heißt, dass diese Änderungen bei einem notwendigen Re-Deployment verloren gehen.

Natürlich können Ausnahmen notwendig sein, aber diese sollten nur für besondere Anforderungen wie Sicherheitseinschränkungen (Schlüsselpaare) oder das Debugging spezifischer Probleme (Regeln für Sicherheitsgruppen) gelten. Und denkt daran, dass solche Änderungen sich auf kontrollierte Komponenten auswirken sollte.

2. NUTZT MODULE, UM REPETITIVE ARBEIT ZU VERMEIDEN 

Wie kann man vermeiden, dass man den Code für ein und dieselbe App kopieren muss, die in mehreren Umgebungen deployt ist, wie zum Beispiel stage/services/frontend-app und prod/services/frontend-app?

Mit Modulen könnt ihr in Terraform vorher definierte Ressourcenstrukturen wiederverwenden. Der Einsatz von Modulen reduziert den Schneeflocken-Effekt und bietet eine gute Möglichkeit, um vorhandenen Infrastruktur-Code wiederzuverwenden.

Module haben einige Variablen als Inputs, die an verschiedenen Orten liegen (z. B. ein anderer Ordner oder sogar ein anderes Repository). Sie definieren Elemente von einem Provider und können mehrere Ressourcen in sich definieren:

# my_module.tf:

resource "aws_launch_configuration" "launch_configuration" {
name = "${var.environment}-launch-configuration-instance"
image_id = "ami-04681a1dbd79675a5"
instance_type = "t3.micro"
}


resource "aws_autoscaling_group" "autoscaling_group" {
launch_configuration = "${aws_launch_configuration.launch_configuration.id}"
availability_zones = ["us-east-1a"]
min_size = "${var.min_size}"
max_size = "${var.max_size}"
}


Module werden über den Modulblock in unserer Terraform Config-Datei aufgerufen, Variablen werden je nach gewünschter Anforderung definiert. Im Beispiel oben rufen wir das Modul zweimal auf, aber mit unterschiedlichen Werten für die verschiedenen Umgebungen.

Hier wird das Modul my_module benutzt und eine AutoScaling Group (ASG) erstellt mit einer Mindestinstanzgröße von 1 und einem Maximum von 2 sowie einer Launch-Konfiguration. Beide Ressourcen werden mit einem speziellen Präfix definiert, in diesem Fall dev:

# my_dev.tf

module "develpment_frontend" {
source = "./modules/my_module"
min_size = 1
max_size = 2
environment = "dev"
}


Anschließend können wir das Modul wiederverwenden. In unserer Produktionsumgebung rufen wir dasselbe Modul my_module auf und erstellen eine ASG mit einer Mindestinstanzgröße von 2 und einem Maximum von 4 sowie einer Launch-Konfiguration, beides mit dem speziellen Präfix prod.

# my_prod.tf

module "development_frontend" {
source = "./modules/my_module"
min_size = 2
max_size = 4
environment = "prod"
}

Terraform config files and modules
Abbildung 1. Beziehung zwischen Terraform-Config-Dateien und -Modulen

Es ist empfehlenswert, verschiedene Versionen von speziellen Modulen zu definieren und zu nutzen, damit wir mit einem VCS arbeiten können.

Wenn wir unsere Module in einem VCS speichern, zum Beispiel Git, dann können wir Tags oder Branch-Namen nutzen, um eine spezielle Version mit der Option ?ref= aufzurufen:

module "my-db-module" {
source = "git::ssh://git@mygitserver.com/my-modules.git//modules/my_module?ref=feature-branch-001"
allocated_storage = "200"
instance_class = "db.t2.micro"
engine = "postgres"


3. MANAGT DIE STATE-DATEI VON TERRAFORM 

Die State-Datei ist wichtig bei Terraform, weil dort alle aktuellen Stände unserer Infrastruktur gespeichert sind. Es ist eine .json-Datei, die normalerweise im versteckten Ordner .terraform innerhalb der Terraform-Config-Dateien (.terraform/terraform.tfstate) liegt. Sie wird automatisch generiert, wenn ihr den Befehl terraform apply ausführt. Eine direkte Bearbeitung der State-Datei ist nicht zu empfehlen.

Hier ist ein Beispiel einer terraform.tfstate-Datei:

{
"version": 3,
"terraform_version": "0.11.8",
"lineage": "35a9fcf6-c658-3697-9d74-480408535ce6",
"modules": [
{
"path": ["root"],
……………………………………
"depends_on": []
}
]&
}


Während der Arbeit an der Infrastruktur kann es natürlich sein, dass andere Teammitglieder die Infrastruktur vielleicht modifizieren und Änderungen vornehmen müssen, was sich auf die State-Datei auswirkt. Deshalb empfehlen wir, die Datei im Shared Storage zu speichern. Terraform unterstützt mehrere Backends zur Speicherung der Datei, wie etcd, azurem, S3 oder Consul.

Hier ist ein Beispiel, wie man den Pfad der Terraform-State-Datei mit S3 als Provider definieren kann. Dabei wird auch DynamoDB genutzt, um den Zugriff zur Datei zu kontrollieren, für den Fall, dass jemand anderes die Infrastruktur gleichzeitig bearbeitet. So wird der Schreibzugriff nur einem User gleichzeitig gewährt.

terraform {
required_version = ">= 0.11.7"
backend "s3" {
encrypt = true
bucket = "bucket-with-terraform-state"
dynamodb_table = "terraform-state-lock"
region = "us-east-1"
key = "locking_states/terraform.tfstate"
}
}


Da die State-Datei sensible Informationen beinhalten könnte, ist es empfehlenswert, den Storage mit den vom Backend mitgelieferten Optionen zu verschlüsseln.

Wenn eure Infrastruktur wächst und ihr mehrere Umgebungen definieren müsst, könnte es auch sein, dass ihr euren Terraform-Status nach Umgebungen und nach Komponenten in den jeweiligen Umgebungen aufteilen müsst. So könnt ihr gleichzeitig an verschiedenen Umgebungen arbeiten und mehrere Leute könnten an verschiedenen Komponenten derselben Infrastruktur arbeiten, ohne dass der Zugriff gesperrt wird (zum Beispiel, wenn ein User die Datenbanken anpasst und ein anderer die Load Balancer).

Das könnt ihr erreichen, indem ihr in der Backend-Definition die spezifische Key-Komponente nutzt:

# my_infra/prod/database/main.tf:
...
key = "prod/database/terraform.tfstate"
...

# my_infra/dev/database/main.tf:
...
key = "dev/database/terraform.tfstate"
...

# my_infra/dev/loadbalancer/main.tf:
...
key = "dev/loadbalancer/terraform.tfstate"
...

Terraform backend files
Abbildung 2: Backend-Dateien in Terraform

4. TEILT ALLES AUF 

Wie bereits erwähnt kann euch das Aufteilen des Terraform-States nach Umgebungen und Komponenten helfen, all die verschiedenen Komponenten eurer Infrastruktur separat voneinander zu bauen. Aber welche Aufteilung solltet ihr vornehmen? Das hängt von der Größe und Komplexität eures Projekts und der Größe eures Teams ab.

Zum Beispiel können einige Optionen in den Komponenten selbst als Inline-Blöcke definiert werden. Manchmal ist es aber empfehlenswert, diese Strukturen in einer anderen Ressource zu definieren. In dem folgenden Beispiel seht ihr die Inline-Route-Definition für eine AWS Route Table:

resource "aws_route_table" "route_table" {
vpc_id = "${aws_vpc.vpc.id}"
route {
cidr_block = "10.0.1.0/24"
gateway_id = "${aws_internet_gateway.example.id}"
}
}


Alternativ könnt ihr dieselbe Route als separate AWS-Route-Ressource anlegen:

resource "aws_route_table" "route_table" {
vpc_id = "${aws_vpc.vpc.id}"
}

resource "aws_route" "route_1" {
route_table_id = "${aws_route_table.route_table.id}"
destination_cidr_block = "10.0.1.0/24"
gateway_id = "${aws_internet_gateway.example.id}"
}


So seid ihr flexibler bei der Definition eurer Infrastruktur, wenn die Komplexität zunimmt. Denkt dabei daran, dass es einfacher ist, Komponenten zu gruppieren, wenn ihr sie definiert, anstatt sie aufzuteilen, nachdem ihr eure Infrastruktur bereits deployt habt.

Wenn der Grad an Komplexität zunimmt, könnt ihr eure gesamte Infrastruktur mithilfe von Bash-Skripten oder Tools wie Ansible oder Terragrunt mit einem Befehl deployen.

Ich hoffe, das hilft euch weiter! Lest euch auch den zweiten Teil meiner Erfahrungen mit Terraform durch, wenn ihr mögt, und schaut euch die Terraform-Dokumentation (in Englisch) für weitere Hilfe zu den Basics an.

Übersetzt aus dem Englischen

Julian Alarcon

DevOps Engineer

Julian ist ein DevOps Engineer, der die Kultur hinter Open-Source-Software liebt, gern sein Wissen teilt und an sonder- und wunderbaren Projekten arbeitet, bei denen er über den Tellerrand schauen kann. Außerdem lernt er sehr gern von anderen Kulturen und versucht, jeden Tag eine neue Sache zu erleben. Mit einem technischen Hintergrund von etwa 10 Jahren helfen Julian und sein Team, große Ideen zum Leben zu erwecken. Außerdem ist er ein Kaffee-Liebhaber aus einem Kaffee-Land, ein Hobby-Fotograf und ein exzellenter Gesprächspartner, besonders wenn Bier im Spiel ist.

Categories
 

ALLE KATEGORIEN

 

VON DIESEM AUTOR

  • 23 Juli 2019

    11 Dinge, die ich vor der Arbeit mit Terraform gern gewusst hätte – Teil 2

 

Archiv

  • 23 Juli 2019

    11 Dinge, die ich vor der Arbeit mit Terraform gern gewusst hätte – Teil 2

  • 25 Juni 2019

    11 Dinge, die ich vor der Arbeit mit Terraform gern gewusst hätte – Teil 1

Ältere Artikel