1. Infrastructure as Code mit Terraform

1.1. Einführung

Infrastructure as Code

Infrastructure as Code (IaC) ist ein Konzept zur Verwaltung von IT-Infrastrukturen, das es ermöglicht, die Infrastruktur durch Code zu definieren und zu steuern. Im Gegensatz zur manuellen Konfiguration und Administration von IT-Infrastrukturen wird die Infrastruktur in IaC mithilfe von Skripten, Konfigurationsdateien oder Programmcode automatisiert.

IaC hilft dabei, menschliche Fehler und manuelle Konfigurationsprozesse zu reduzieren, indem die Infrastruktur durch Code automatisiert wird. Dies erhöht die Effizienz und Zuverlässigkeit der IT-Infrastruktur und bietet eine höhere Skalierbarkeit und Flexibilität.

Ein weiterer Vorteil von IaC besteht darin, dass die Infrastruktur in verschiedenen Cloud-Plattformen wie Amazon Web Services (AWS), Microsoft Azure und Google Cloud Platform (GCP) verwaltet werden kann. Es gibt verschiedene Tools und Frameworks, die für die Implementierung von IaC verwendet werden können, wie z.B. Terraform, Ansible, Chef und Puppet.

Microsoft Azure

In unserem Versuchsaufbau nutzen wir die Cloud-Plattform Microsoft Azure, da wir für diese eine Bildungslizenz von der Hochschule besitzen. Sie bietet eine große Auswahl an Diensten, darunter virtuelle Maschinen, Speicher, Datenbanken, Netzwerke, künstliche Intelligenz, IoT und vieles mehr.

Terraform

Terraform ist ein Open-Source-Tool, das von HashiCorp entwickelt wurde und zur Verwaltung von Infrastruktur als Code verwendet wird. Mit Terraform kann man eine Vielzahl von Ressourcen in der Cloud und On-Premises automatisch erstellen, ändern und löschen. Das Tool unterstützt eine breite Palette von Cloud-Plattformen und -Anbietern wie Amazon Web Services (AWS), Microsoft Azure, Google Cloud Platform (GCP) und viele weitere.

Terraform ermöglicht es, die gesamte Infrastruktur als Code zu definieren, was bedeutet, dass Sie die Konfiguration in einem Dateiformat schreiben können, das leicht zu versionieren, zu verwalten und zu teilen ist. Mit Terraform kann man auch Änderungen an der Infrastruktur planen und anzeigen, bevor diese tatsächlich durchgeführt werden. Dadurch wird ein unbeabsichtigtes Ändern oder Löschen wichtiger Infrastrukturressourcen verhindert.

1.2. Vorbereitungen

Um Terraform mit Azure zu verwenden, muss man einige Vorbereitungen treffen:

  1. Azure-Konto: Man benötigt ein Azure-Konto, in unserem Fall kann man die Microsoft Azure Dev Tools verwenden

  2. Azure CLI: Es muss die Azure CLI werden, um Befehle auf der Azure-Plattform ausführen zu können

  3. Terraform: Terraform muss auf dem Computer installiert werden

  4. Terraform Konfiguration: In der Terraform Konfiguration wird festgelegt, welche Plugins installiert, welche Infrastruktur erstellt und welche Daten abgerufen werden sollen. Im Versuchsaufbau wurde diese Konfiguration in den Dateien main.tf, outputs.tf und variables.tf abgelegt. Die Beispielkonfiguration findet man im GitLab Repository der Hochschule Augsburg (VPN Zugang wird benötigt).

1.3. Kernkonzepte

In diesem Kapitel sollen die Kernkonzepte von Terraform kurz beschrieben werden. Diese Liste ist längst nicht vollständig, sie soll nur einen groben Überblick über die wichtigsten Komponenten geben.

1.3.1. Sprache

Die Infrastruktur wird bei Terraform mit der Syntax HCL geschrieben, einer einfachen Konfigurationssprache, die auch in anderen Anwendungen, vor allem bei der Firma HashiCorp, verwendet wird. Um Terraform zu benutzen, ist es nicht wichtig, alle Einzelheiten der Sprache zu beherrschen, meist sind die volgenden Elemente vollkommen ausreichend:

  • Input Variablen: Schlüssel-Wert-Paar, das von Terraform-Modulen verwendet wird, um Anpassungen zur Laufzeit vorzunehmen, ohne den Code nochmals zu verändern. (Hilfreich zum Beispiel für Deployment auf verschiedenen Umgebungen)

  • Provider: Plugin, um mit Service-APIs zu interagieren und auf die zugehörigen Resourcen zuzugreifen (z.B. für Azure, AWS, GCP, ..)

  • Resourcen: Sind die wichtigsten Elemente in Terraform. Jede Resource beschreibt eine oder mehrere Infrastruktur Objekte, wie z.B. virtuelle Netzwerke, Virtuelle Maschinen oder auch DNS Einträge

  • Module: Ordner mit Terraform Dateien, in dem alle Konfigurationen definiert sind

  • Datenquellen: Wird von Anbietern implementiert, um Informationen zu externen Objekten an Terraform zurückzugeben

  • Output: Rückgabewerte von Terraform-Modulen, die auf der Commandline ausgegeben werden und an andere Module übergeben werden können

  • Locale Variablen: Schlüssel-Wert-Paar, das nur innerhalb eines Terraform-Moduls verwendet werden kann. Kann nicht zur Laufzeit verändert werden.

Diese Liste ist wie schon erwähnt bei weitem nicht vollständig, es gibt zum Beispiel auch Schleifen, Conditional Expressions und weitere Provider, mit denen die Konfiguration von Infrastruktur erleichtert werden kann. Für unseren Versuch waren die erklärten Sprachelemente bei weitem ausreichend.

1.3.2. Lebenszyklus

_images/lifecycle_terraform.png
  1. Init: Mit diesem Befehl wird die lokale Terraform Umgebung initialisiert. Dies beinhaltet das Herunterladen aller notwendigen Provider und Erstellen von .lock-Files.

  2. Plan: Hiermit wird der Ist-Zustand in der Cloud mit dem lokalen Zustand verglichen, ein Ausführungsplan erstellt und die möglichen Änderungen angezeigt. In diesem Schritt werden noch keine Änderungen durchgeführt.

  3. Apply: Jetzt wird der Plan in der Cloud ausgeführt. Neu hinzugekommene Resourcen werden erstellt, gelöschte werden auch in der Cloud entfernt.

  1. Destroy: Mit diesem Befehl werden all Resourcen, die in diesem Terraform Environment erstellt wurden, gelöscht.

1.4. Codebase

In diesem Versuch wurden drei Terraform Dateien erstellt: main.tf, variables.tf, outputs.tf. Im folgenden sollen die drei Dateien und deren Inhalt genauer erklärt werden:

1.4.1. variables.tf

Input Variablen helfen dabei Terraform Code modular zu gestalten und je nach Umgebung zu verändern. In diesem Beispiel wurden die beiden Variablen azure_region und zip_deploy_file erstellt. Beide Variablen sind vom Typ string und besitzen einen Standardwert. Somit wird standardmäßig die Webapplikation in der Region Deutschland mit der zip Datei im Ordner ./app/build.zip (relativer Pfad) erzeugt.

 1variable "azure_region" {
 2  description = "Region for azure to use"
 3  type = string
 4  default = "Germany West Central"
 5}
 6
 7variable "zip_deploy_file" {
 8  description = "Path to zip deploy file"
 9  type = string
10  default = "./app/build.zip"
11}

Der Pfad für die Variable zip_deploy_file kann ein relativer oder absoluter Pfad zu einer zip-Datei mit einem go-Binary sein. Außerdem gibt es derzeit in azure mehr als 60 verfügbare Regionen, die bei der Variable azure_region eingetragen werden können.

Input Variablen können zur Laufzeit von terraform plan und terraform apply mit dem argument -var ‚foo=bar‘ oder über eine .tfvars Datei überschrieben werden.

1.4.2. outputs.tf

Über Output Variablen können Informationen über die Infrastruktur auf der Commandline ausgegeben oder an andere Konfigurationsdateien weitergegeben werden. In unserem Versuch wird die URL, auf der die Webapplikation erreichbar ist, auf der Commandline ausgegeben und nicht in anderen Modulen weiterverwendet. Ein typischer Anwendungsfall für eine Weitergabe an ein anderes Modul wäre ein Frontendmodul, das eine API auf der definierten URL aufruft.

1output "dva_web_app_url" {
2  value = "https://${azurerm_linux_web_app.dva.default_hostname}"
3}

1.4.3. main.tf

In der main.tf wird dann der eigentlich Infrastruktur-Code beschrieben. Im ersten Absatz werden die benötigten Provider definiert. Dieser Schritt ist nicht notwendig, wird aber empfohlen um eine bestimmte Version der einzelnen Provider festzulegen. Damit kann garantiert werden, dass der Code mit den angegebenen Providern jederzeit funktioniert. In diesem Deployment wurden die Provider azurerm für das Deployment in der Azure Cloud und random, um eine random Zeichenfolge zu erstellen, definiert.

 1# We strongly recommend using the required_providers block to set the
 2# Azure Provider source and version being used
 3terraform {
 4  required_providers {
 5    azurerm = {
 6      source  = "hashicorp/azurerm"
 7      version = "=3.48.0"
 8    }
 9    random = {
10      source  = "hashicorp/random"
11      version = "=3.4.3"
12    }
13  }
14}
15
16# Configure the Microsoft Azure Provider
17provider "azurerm" {
18  features {}  
19}

Im nächsten Abschnitt werden lokale Variablen definiert. Diese können im Gegenteil zu den Variablen der variables.tf zur Laufzeit nicht mehr verändert werden. Zu Demozwecken wurden hier Tags für alle Resourcen festgelegt.

1locals {
2  tags = {
3    environment = "sandbox"
4    subject     = "dva_training"
5    team        = "group_5"
6  }
7}

Danach wird eine Azure Resource Group erstellt, die alle Resourcen der Webapplikation enthält. Resource Gruppen erleichtern das bereitstellen, aktualisieren und löschen einer ganzen Gruppe.

1# resource group
2resource "azurerm_resource_group" "dva" {
3  name     = "dva_resources"
4  location = var.azure_region
5  tags     = local.tags
6}

Im Service Plan wird festgelegt, welcher Server für die Webapplikation genutzt werden soll. Hierbei kann man das Betriebssystem, und die SKU (Stock-Keeping-Unit), also wie leistungsstark, skalierbar und ausfallsicher der Server sein soll, festlegen.

 1# service plan
 2resource "azurerm_service_plan" "dva" {
 3  name                = "dva_service_plan"
 4  # der service plan soll in der gerade definierten resource group erstellt werden
 5  resource_group_name = azurerm_resource_group.dva.name 
 6  location            = azurerm_resource_group.dva.location
 7  os_type             = "Linux" # Linux oder Windows?
 8  sku_name            = "B1" # Welcher Server soll benutzt werden?
 9  tags                = local.tags
10}

Derzeit verfügbare SKUs:

name

Tier

Full name

D1

Shared

an D1 Shared

F1

Free

an F1 Free

B1

Basic

an B1 Basic

B2

Basic

an B2 Basic

B3

Basic

an B3 Basic

S1

Standard

an S1 Standard

S2

Standard

an S2 Standard

S3

Standard

an S3 Standard

P1

Premium

an P1 Premium

P2

Premium

an P2 Premium

P3

Premium

an P3 Premium

P1V2

PremiumV2

an P1V2 PremiumV2

P2V2

PremiumV2

an P2V2 PremiumV2

P3V2

PremiumV2

an P3V2 PremiumV2

I1

Isolated

an I2 Isolated

I2

Isolated

an I2 Isolated

I3

Isolated

an I3 Isolated

Y1

Dynamic

a function consumption plan

Hier kann noch mehr über die verschiedenen Preispläne von Azure nachgelesen werden.

Im letzten Codeabschnitt wird die eigentliche Webapplikation definiert. Diese wird mit dem vorher definierten Serviceplan und Resourcegruppe angelegt. In Zeile 12 wird die zip-Datei definiert, die das Binary mit dem Go-HTTP-Server enthält. Außerdem soll die Applikation nur über HTTPS erreichbar sein (Zeile 14). Der Parameter WEBSITE_RUN_FROM_PACKAGE gibt an, dass die hochgeladene Zip-Datei, die nun physisch in einem S3 Bucket liegt, als Readonly Dateisystem in den Container gemountet wird, von wo aus das Go-Binary dann ausgeführt werden kann.

In der Site Config hilft die Option always_on = true, dass die Website jederzeit erreichbar ist und niemals heruntergefahren wird, auch wenn gerade kein Traffic auf der Seite ist. Preislich würde ein kurzzeitiges Herunterfahren auch keinen Vorteil bringen, da man jederzeit den vollen VM-Preis bezahlen muss. Zum Schluss kann man noch in Zeile 24 die verwendet Go-Version hinterlegen, um Kompatibilitätsproblem vorzubeugen.

 1# random id
 2resource "random_id" "dva" {
 3  byte_length = 4
 4}
 5
 6# linux web app
 7resource "azurerm_linux_web_app" "dva" {
 8  name                = "dva-app-${random_id.dva.id}"
 9  resource_group_name = azurerm_resource_group.dva.name
10  location            = azurerm_service_plan.dva.location
11  service_plan_id     = azurerm_service_plan.dva.id
12  zip_deploy_file     = var.zip_deploy_file
13  tags                = local.tags
14  https_only          = true # nur https erlauben
15
16  app_settings = {
17    WEBSITE_RUN_FROM_PACKAGE = 1 # readonly dateisystem mit zip inhalt
18  }
19
20  site_config {
21    always_on = true
22
23    application_stack {
24      go_version = 1.19
25    }
26  }
27}

1.5. Deployment

Um mit dem Deployment zu beginnen, öffnet man zunächst das Verzeichnis mit den .tf-Dateien in der Konsole. Nun kann man mit terraform init die lokale Umgebung initialisieren. Anschließend muss der Benutzer sich mit az login bei der AzureCli anmelden. Hierfür wird man auf die Microsoft Acountlogin Seite weitergeleitet, um sich dort mit dem passenden Account anzumelden. Ist dies erfolgreich erscheint im Konsolenfenster eine Zusammenfassung über den angemeldeten Benutzer. Nun kann mit terraform plan ein Ausführungsplan erstellt werden, der den aktuellen Stand in der Cloud mit den geforderten Ressourcen lokal vergleicht. Dieser Plan kann mit terraform plan -out=<outputpath> gespeichert werden. Dies kann hilfreich sein, da bei dem anschließenden Command terraform apply beim Auslassen eines Plans ein neuer erstellt wird, egal ob zuvor terraform plan ausgeführt wurde. Hier kann es auch passieren, dass nicht der identische Plan generiert wird. Um dies zu vermeiden kann mit terraform apply <path-to-plan> der erstellte Plan verwendet werden. Nach einer kurzen Wartezeit, in der alles installiert und deployt wird, wird eine Internetadresse ausgegeben, unter der die Webseite erreichbar ist. Diese ist sehr simpel gehalten und zeigt lediglich einen kleinen Text an. Dieser setzt sich aus Hello, ! und dem Path der Internetadresse zusammen.

_images/Website.png

Im Dashboard von Azure wird dann die erstellte Applikation angezeigt:

_images/AzureDashboard.png

Wenn die Ressourcen nicht mehr benötigt werden, können sie mit terraform destroy wieder freigegeben werden und alle Aktionen, die von terraform durchgeführt wurden werden rückgängig gemacht. Hier ist es wichtig, dass alle Änderungen stehts über terraform vorgenommen werden. Andernfalls kann es geschehen, dass nicht alles gelöscht wird.

1.6. Fazit

Zusammenfassend kann man sagen, dass mit terraform und allgemein durch Infrastructure as Code sehr einfach Anwendungen erstellt werden können, die leicht zu erweitern sind. Durch die deklarative Art von terraform ist es sehr einfach die gewünschte Umgebung zu erhalten, ohne sich über die genauen Schritte für die Erstellung Gedanken machen zu müssen.

1.7. Literaturangaben

Informationen:

https://developer.hashicorp.com/terraform

https://learn.microsoft.com/de-de/azure/?product=popular

https://geekflare.com/de/terraform-for-beginners/