1
 
 
Profil
In deinem persönlichen Profilbereich kannst du den Status deiner Bewerbung einsehen, unvollständige Bewerbungen zwischenspeichern und aktuelle News und Events einsehen
05. Juni 2023

Infrastructure as Code (IaC) - Terraform-Konfiguration DRY halten mit Terragrunt

Worum geht es in dem Artikel?

Terraform ist ein Infrastruktur-als-Code-Tool zur Bereitstellung von Cloud- oder On-Premise-Ressourcen unter Verwendung von Konfigurationsdateien, die leicht lesbar und wiederverwendbar sind. Unser Team verwendet Terraform, um GCP-Cloud-Ressourcen mit teilweise unterschiedlicher Konfiguration in verschiedenen Umgebungen (z.B. Produktion oder Testing) zu verwalten. Die Organisation von Terraform-Code kann jedoch mühsam werden, denn mit zunehmender Anzahl von Infrastrukturkomponenten und Umgebungen neigen die Konfigurationsdateien dazu, sich zu wiederholen. Unterschiede zwischen den Umgebungen sind daher möglicherweise schwieriger zu erkennen.

Das Ziel ist also, die Wiederholung von Code zu minimieren oder ganz zu eliminieren, indem die Terraform-Konfiguration DRY (Don't Repeat Yourself) gehalten wird. Idealerweise sollten Ressourcen und allgemeine Terraform-Deklarationen wie die Provider- und Remote-State-Konfiguration nur einmal geschrieben, parametrisiert und dann für mehrere Umgebungen wiederverwendet werden, während gleichzeitig der gesamte Code leicht lesbar und intuitiv bleibt.

Wir sind daher zu Terragrunt gewechselt, eine kleine Wrapper-Applikation um Terraform, die weniger Wiederholungen, eine bessere geteilte Konfiguration und eine Erleichterung der Arbeit mit Terraform-Modulen verspricht. Es kann eine praktikable Lösung sein, um Konfigurationsdateien besser zu organisieren und Unterschiede zwischen Umgebungen deutlich zu machen.

Organisation von GCP-Umgebungen

Wir unterteilen unsere GCP-Projekte nach ihrer jeweiligen Umgebung (Produktion, Testing, Entwicklung). Die Entwicklungs- und Testing-Projekte spiegeln alle Ressourcen wider, die wir für Produktionszwecke verwenden, allerdings teilweise kleiner dimensioniert, um die Gesamtkosten der Cloud-Infrastruktur zu senken. Zu den Ressourcen für diese Projekte gehören in der Regel virtuelle Maschinen, serverlose Code-Ausführungsumgebungen, Datenbanken, Speicher-Töpfe oder Überwachungskonfigurationen.

Darüber hinaus verwenden wir ein separates infra-Projekt für grundlegende Infrastrukturkomponenten, die von allen Umgebungen gemeinsam genutzt werden, wie z. B. eine gemeinsame Artifact Registry und ein Cloud-Build-Setup zum Erstellen und Speichern von Docker-Images sowie zentrale Ressourcen (Jenkins oder Grafana-VMs), die mit Ressourcen in allen anderen Umgebungen interagieren. Die Konfiguration für dieses Projekt weicht vom Rest unseres Setups leicht ab.

Schließlich verwenden alle Projekte gemeinsame und meist ähnlich angeordnete Basiskomponenten, wie Netzwerk-, Sicherheits- und Authorisierungs-Konfiguration. Der jeweilige Remote-Status für jede Umgebung wird in einem Terraform-State-Bucket innerhalb des infra-Projekts gehalten.

Infrastruktur als Code mit grundlegendem Terraform


Während wir die Ressourcen für regelmäßig bereitgestellte Microservices in der Nähe des eigentlichen Service-Repositorys aufbewahren (oder zumindest in einem separaten Repository, das von allen Microservices mit ähnlichen bereitstellenden Mechanismen gemeinsam genutzt wird), gibt es auch viele Cloud-Komponenten, die nur selten eingerichtet und geändert werden. Diese grundlegende Infrastruktur wird in einem eigenständigen GitHub-Repository verwaltet, das in spezielle Ordner für jede Umgebung aufgeteilt ist, die alle Ressourcen innerhalb einer Projekt-Umgebung bereitgestellt werden.

Anfangs war dieser Ansatz schnell eingerichtet und mit einer geringen Menge an Ressourcen war die Pflege der Terraform-Konfiguration und die Ausführung von Terraform-CLI-Befehlen einfach und intuitiv. Aber mit einer wachsenden Menge an Ressourcen wurde das Nachverfolgen von mehr und mehr Konfigurationsunterschieden zwischen den Umgebungen fehleranfällig und die Dateistruktur wurde immer umfangreicher und schwieriger zu pflegen (Abbildung 1).

Figure 1: Basic Terraform file structure without any optimisations
Figure 1: Basic Terraform file structure without any optimisations

Abbildung 1: Grundlegende Terraform-Dateistruktur ohne jegliche Optimierungen

Allein und ohne weitere Optimierungen offenbarte dieser Ansatz mehrere Designmängel.

  1. Die Ressourcen wurden jeweils in jedem Projekt gepflegt, was zu einer Vielzahl an Code-Wiederholung führte. Änderungen an einer Resource mussten für jedes Projekt vorgenommen werden, mit der Gefahr einzelne Projekte auszulassen oder Konfigurationen zu verwechseln.
  2. Auch die Provider- und Backend-Konfiguration wurde pro Projekt dupliziert. Obwohl diese größtenteils identisch waren, bestand eine hohe Wahrscheinlichkeit für Fehlkonfigurationen wie z. B. ein falsches Präfix für die Statusdatei, da Terraform die Verwendung von Variablen innerhalb der Remote-Backend-Konfiguration nicht zulässt.
  3. Konfigurationsparameter wie Variablen zum Sizing einer Resource wurden statisch in der config.tf des jeweiligen Projekts deklariert, was eine Änderung während der Terraform-Ausführung umständlich und unintuitiv machte.
  4. Eine wachsende Anzahl von Ressourcen in einer einzigen Terraform-Statusdatei erhöhte die Ausführungszeit aller Terraform-CLI-Befehle, da Terraform jede Ressource in der Statusdatei mit der tatsächlichen Cloud-Ressource vergleichen musste.
  5. Eine einzige Statusdatei für eine große Anzahl von Ressourcen erhöht das Risiko für alle anderen Resourcen im Falle von Konfigurationsfehlern oder menschlichen Fehlern. Die Ausführung eines fehlerhaften Destroy-Befehls für die falsche Statusdatei hätte katastrophale Folgen haben können.


Terraform bietet einige Mechanismen, um diese Probleme zu entschärfen. Workspaces erlauben es, eine Reihe von Ressourcen für mehrere Umgebungen nur einmal zu definieren und dann den Kontext für die Einsatzumgebung während der Laufzeit zu wechseln. Module wiederum kombinieren verwandte Ressourcen in einem parametrisierten wiederverwendbaren Blueprint, der lokal oder in einem entfernten Repository gespeichert werden kann.

Die Wahl einer oder einer Kombination aus beiden Methoden kann einem oder mehreren der oben genannten Probleme entgegenwirken. Module verringern definitiv die Wiederholung von Code und ermöglichen den parametrisierten Aufruf häufig verwendeter Ressourcen. Aber die Aufteilung von Ressourcen in eine logisch verknüpfte Gruppe, die jeden möglichen Anwendungsfall abdeckt, kann schwierig und manchmal unverständlich sein. Und die einfache Gruppierung von Ressourcen in Modulen reicht nicht aus, wenn am Ende alle diese Module innerhalb eines einzigen Terraform-State für jede Umgebung verwendet werden. Workspaces ermöglichen einen einfachen Wechsel des Umgebungskontextes für dieselbe Gruppe von Ressourcen, setzen aber voraus, dass der richtige Workspace mit der richtigen *.tfvars-Datei für die aktuelle Ausführungsumgebung verwendet wird, was bei manuellen Eingriffen gefährlich sein kann.

Wechsel zu Terragrunt

Stattdessen haben wir uns für Terragrunt entschieden, einen kleinen Wrapper für Terraform, der ein besseres dynamisches Remote-State-Handling und insgesamt eine DRY-Konfiguration von Terraform bietet. Terragrunt fördert einen modularen Ansatz zur Strukturierung des Terraform-Codes und verwendet seine eigenen Konfigurationsdateien (*.hcl), um den resultierenden Code so DRY wie möglich zu halten.

Figure 2: Terraform modules
Figure 2: Terraform modules

Abbildung 2: Terraform-Module

Für die Migration teilten wir zunächst alle Ressourcen in logisch zusammenhängende Module auf und legten sie in separaten Ordnern innerhalb eines Modulverzeichnisses ab (Abbildung 2). Obwohl ein Remote-Hosting dieser Module möglich gewesen wäre, schien ein lokaler Ansatz für den Anfang ausreichend. Die Organisation der Ressourcen und die Überarbeitung ihrer Konfiguration mit effizienter Parametrisierung durch die Erstellung dieser Module sorgte von sich aus bereits für eine verständliche Strukturierung.

Während die Terraform-Konfiguration und alle zugehörigen *.tf-Dateien in jedem Modul enthalten sind, wird die eigentliche Konfiguration einer Umgebung in einer einzigen terragrunt.hcl-Datei für jedes verwendete Modul zusammengefasst. Die resultierende Ordnerstruktur sieht ähnlich aus wie zuvor, allerdings wird die Anzahl der identischen Terraform-Ressourcendateien drastisch reduziert (Abbildung 3).

Figure 3: Terragrunt environment folders
Figure 3: Terragrunt environment folders

Abbildung 3: Terragrunt-Umgebungen

Jede terragrunt.hcl enthält nun die Konfiguration für das jeweilige Terraform-Modul, einschließlich der Provider- und Backend-Konfiguration sowie der Modulparameter, d.h. Sizing oder zonale Einstellungen für VMs. Neue Funktionen können entweder zu einem bestehenden Modul hinzugefügt werden, ohne die Terragrunt-Konfiguration zu ändern, oder man fügt eine neue Modulquelle und eine entsprechende terragrunt.hcl zu jeder betroffenen Umgebung hinzu.

Änderungen innerhalb der terragrunt.hcl (oder des entsprechenden Terraform-Moduls) können über das Terragrunt CLI für ein einzelnes Modul, mehrere Module innerhalb einer Umgebung oder mehrere Umgebungen/Projekte auf einmal vorgenommen werden. Unsere Deployment-Pipeline verteilt alle Modul-Unterordner mit ihren jeweiligen Ressourcen für jede Umgebung.

Für jedes Modul, das in einer Umgebung verwendet wird, wird ein separater Terraform-Remote-State beibehalten, um zu verhindern, dass der State für jedes Modul aufgebläht wird, und um die Verwaltung von Abhängigkeiten zwischen den Modulen beizubehalten. Z.B. wird das Netzwerkmodul zuerst ausgerollt und dessen Ergebnis als Eingabeparameter für das DNS-Modul verwendet (Abbildung 4).

Figure 4: Terragrunt HCL File including configuration, properties and Terraform functionality
Figure 4: Terragrunt HCL File including configuration, properties and Terraform functionality

Abbildung 4: Terragrunt HCL-Datei mit Konfiguration, Parametern und Terraform-Funktionalität

Nach diesem Schritt können wir nicht einfach eine bestehende Datei in eine neue Umgebung oder ein neues Projekt kopieren, ohne Anpassungen vorzunehmen, da jede terragrunt.hcl die Konfigurationseinstellungen für ihre jeweilige Umgebung beibehält. Die unterschiedlichen Konfigurationen sind immer noch über mehrere Unterverzeichnisse verstreut.

Terragrunt weiter optimieren

Terragrunt erlaubt eine umfangreiche Verwendung von Variablen innerhalb seiner eigenen Konfigurationsdateien, sogar für die Deklaration von Remote-Zuständen (im Gegensatz zu Terraform). Wir haben daher die verbleibenden Codewiederholungen eliminiert, indem wir zusätzliche *.hcl-Konfigurationsdateien verwendeten, die ähnliche Eigenschaften gruppierten. Das Ergebnis waren terragrunt.hcl-Dateien, die auf ihre eigentliche Funktionalität reduziert und vollständig von der Deklaration der von ihnen verwendeten Eigenschaften befreit waren (Abbildung 5).

Figure 5: Terragrunt HCL file, only including module functionality
Figure 5: Terragrunt HCL file, only including module functionality

Abbildung 5: Terragrunt HCL-Datei, nur mit Modulfunktionalität

Jede *.hcl-Datei befindet sich auf der Ebene über denjenigen Ordnern, auf die sich ihre jeweilige Konfiguration bezieht. Alle modul- und projektbezogenen Eigenschaften für Umgebungen werden in einer env.hcl-Datei auf der Umgebungsebene abgelegt, während gemeinsame, von einzelnen Umgebungen unabhängige Eigenschaften in einer common.hcl-Datei auf der Stammebene abgelegt werden. Zu den Umgebungseigenschaften gehören zum Beispiel die oben erwähnten Größenparameter für VMs sowie spezifische Bucket- oder Netzwerknamen (Abbildung 6). Zu den gemeinsamen Eigenschaften auf der Stammebene gehören globale Konfigurationsparameter, d. h. Team-Labels, die für jede unserer Ressourcen vergeben werden.

Figure 6: Terragrunt HCL file with environment properties
Figure 6: Terragrunt HCL file with environment properties

Abbildung 6: Terragrunt HCL-Datei mit Umgebungseigenschaften

Darüber hinaus wird eine einzige Backend- und Provider-Konfiguration in einer terragrunt.hcl-Datei auf der Root-Ebene deklariert, die dann von allen Untermodulen in jeder Umgebung verwendet wird. Da Terragrunt die Verwendung von Variablen in diesen Deklarationen erlaubt, enthält der eigentliche Code Platzhalter, die während der Laufzeit basierend auf dem Pfad jedes Moduls gefüllt werden (Abbildung 7).

Figure 7: Terragrunt HCL file with dynamic provider and backend configuration
Figure 7: Terragrunt HCL file with dynamic provider and backend configuration

Abbildung 7: Terragrunt HCL-Datei mit dynamischer Provider- und Backend-Konfiguration

Um eine deklarierte Variable zu füllen, durchläuft Terragrunt die Verzeichnisse ausgehend von dem Modulverzeichnis, aus dem der CLI-Befehl ausgeführt wurde, aufwärts, inkludiert alle *.hcl-Dateien auf dem Weg, bis es eine terragrunt.hcl-Datei auf der Stammebene findet. Der Wrapper erzeugt dann die statischen Terraform-Dateien und führt den entsprechenden Terraform-Befehl (plan, apply, destroy) aus.

Fazit

Die endgültige Terragrunt-Struktur bietet eine zentrale Deklaration gemeinsamer Eigenschaften, Backend- und Provider-Konfiguration, die für jedes Untermodul und jede Umgebung ohne zusätzliche Codezeilen funktioniert (Abbildung 8). Alle umgebungsspezifischen Eigenschaften und Konfigurationen sind in jeder env.hcl gebündelt und können leicht von anderen Umgebungen getrennt werden. Daher müssen Terragrunt-Befehle, die entweder von unseren Entwicklungsmaschinen oder durch automatisierte Pipelines ausgeführt werden, keine Variablendateien einschließen oder explizit eine Eigenschaft setzen (obwohl dies möglich wäre).

Figure 8: Final Terragrunt folder structure
Figure 8: Final Terragrunt folder structure

Abbildung 8: Endgültige Terragrunt-Ordnerstruktur

Außerdem können Module einfach zu Umgebungen hinzugefügt werden, indem man die entsprechende terragrunt.hcl aus einer anderen Umgebung kopiert. Zum Entfernen eines Moduls entfernt man einfach die passende terragrunt.hcl aus der jeweiligen Umgebung (nachdem terragrunt destroy ausgeführt wurde).

Terragrunt ist eine Lösung, um die Terraform-Konfiguration DRY zu halten, indem es alle Wiederholungen, die innerhalb umfangreicher Konfigurationen in einem Multi-Environment-Setup auftreten können, eliminiert. Es ist weniger fehleranfällig als der Versuch, verschiedene *.tfvars-Dateien korrekt zu handhaben oder Workspaces im laufenden Betrieb zu wechseln. Die Migration von Terraform-Ressourcen zu Terragrunt ist mit wenigen Schritten zu bewerkstelligen. Für uns hat Terragrunt die Verwaltung unserer Cloud-Ressourcen verbessert und für mehr Intuition und Stabilität beim Ändern, Erweitern und Warten unserer Infrastruktur gesorgt.

Wenn du eine Frage an mich oder das Team hast, melde dich gerne in den Kommentaren unter diesem Artikel. Ich melde mich dann schnellstmöglich bei dir zurück.

Möchtest du Teil des Teams werden?

5 Personen gefällt das

0Noch keine Kommentare

Dein Kommentar
Antwort auf:  Direkt auf das Thema antworten

Geschrieben von

Philipp Giesen
Philipp Giesen
Ehemaliger Software-Entwickler bei OTTO

Ähnliche Beiträge

We want to improve out content with your feedback.

How interesting is this blogpost?

We have received your feedback.

Cookies erlauben?

OTTO und drei Partner brauchen deine Einwilligung (Klick auf "OK") bei einzelnen Datennutzungen, um Informationen auf einem Gerät zu speichern und/oder abzurufen (IP-Adresse, Nutzer-ID, Browser-Informationen).
Die Datennutzung erfolgt für personalisierte Anzeigen und Inhalte, Anzeigen- und Inhaltsmessungen sowie um Erkenntnisse über Zielgruppen und Produktentwicklungen zu gewinnen. Mehr Infos zur Einwilligung gibt’s jederzeit hier. Mit Klick auf den Link "Cookies ablehnen" kannst du deine Einwilligung jederzeit ablehnen.

Datennutzungen

OTTO arbeitet mit Partnern zusammen, die von deinem Endgerät abgerufene Daten (Trackingdaten) auch zu eigenen Zwecken (z.B. Profilbildungen) / zu Zwecken Dritter verarbeiten. Vor diesem Hintergrund erfordert nicht nur die Erhebung der Trackingdaten, sondern auch deren Weiterverarbeitung durch diese Anbieter einer Einwilligung. Die Trackingdaten werden erst dann erhoben, wenn du auf den in dem Banner auf otto.de wiedergebenden Button „OK” klickst. Bei den Partnern handelt es sich um die folgenden Unternehmen:
Google Ireland Limited, Meta Platforms Ireland Limited, LinkedIn Ireland Unlimited Company
Weitere Informationen zu den Datenverarbeitungen durch diese Partner findest du in der Datenschutzerklärung auf otto.de/jobs. Die Informationen sind außerdem über einen Link in dem Banner abrufbar.
Du kannst deine Einwilligung auch jederzeit grundlos mit Wirkung für die Zukunft widerrufen, indem du auf den Button "Cookie-Einstellungen" im Footer der Website und "Cookies ablehnen" klickst.