Tutorial: Terraform zur Openstack-Verwaltung installieren & nutzen

Version vom 14. Mai 2025, 22:08 Uhr von Rb (Diskussion | Beiträge) (→‎Beispiel: Datenbankserver & Webserver)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Diese Seite befindet sich derzeit in Bearbeitung.

Terraform zur Openstack-Verwaltung installieren & nutzen

Terraform ist ein leistungsstarkes Tool zur Infrastrukturautomatisierung, das es ermöglicht, Ressourcen in der Cloud deklarativ zu erstellen, zu verwalten und zu organisieren. In Kombination mit OpenStack bietet Terraform eine Möglichkeit, eine eigene Cloud-Umgebung effizient zu verwalten. In diesem Artikel zeigen wir, wie Sie Terraform mit OpenStack konfigurieren und nutzen können.

Falls Sie zusätzlich die Openstack CLI verwenden möchten, haben wir hierzu auch ein Tutorial.


Voraussetzungen

Um Terraform in Verbindung mit OpenStack zu nutzen, sollten folgende Voraussetzungen erfüllt sein:

  1. Zugriff auf unsere OpenStack-Umgebung (af.stack).
  2. Ein Server oder lokaler Rechner mit Linux, macOS oder Windows.
  3. Terraform installiert.
  4. Ein SSH-Schlüsselpaar für den Zugriff auf die Instanzen. Wir empfehlen die Verwendung von **ed25519**-Schlüsseln.

Schritt 1: API Zugang aktivieren

Um den API Zugang zu aktivieren, müssen Sie ein Passwort für diesen anlegen.

Afstack api activate.png

  1. Ihr API-Username.
  2. Ihr API-Passwort.
  3. Ihre Konfigurationsdatei, um auf die API zuzugreifen.
  4. Hiermit aktivieren Sie den API-Zugang.

Schritt 2: Terraform konfigurieren

Hauptkonfigurationsdatei erstellen

Erstellen Sie eine Datei namens main.tf mit folgendem Inhalt:

terraform {
  required_version = ">= 0.14.0"
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 1.53.0"
    }
  }
}

# OpenStack-Provider-Konfiguration vorher mit source $openrc_dateiname laden
provider "openstack" {}

# Image-Datenquelle für Ubuntu-Image; hier muss der vollständige Image-Name verwendet werden
data "openstack_images_image_v2" "ubuntu_image" {
  name        = "Ubuntu 20.04 LTS (20240821)"
  most_recent = true
}

# OpenStack-Provider-Konfiguration direkt in der Konfig
#provider "openstack" {
#  user_name = "API-Username"
#  password = "Passwort"
#  auth_url = "https://api.afstack.io:5000/v3"
#  region = "RegionOne"
#}


# OpenStack-Volume für den Server erstellen
resource "openstack_blockstorage_volume_v3" "example_volume" {
  name     = "example-volume"
  size     = 20
  image_id = data.openstack_images_image_v2.ubuntu_image.id
}

# Hochladen des erstellten SSH-Keys, siehe Punkt 1.6.1
resource "openstack_compute_keypair_v2" "provisioning-key" {
  name       = "my-ed25519-key"
  public_key = file("~/.ssh/my-ed25519-key.pub")
}

# Instanz erstellen
resource "openstack_compute_instance_v2" "Beispielname" {
  name      = "Beispielname"
  flavor_name = "a1.xs"
  key_pair    = openstack_compute_keypair_v2.provisioning-key.name
  # Volume als Boot-Device verwenden
  block_device {
    uuid = openstack_blockstorage_volume_v3.example_volume.id
    source_type = "volume"
    destination_type = "volume"
    boot_index = 0
    delete_on_termination = false
  }
Erläuterung der Konfiguration
  • terraform: Gibt die erforderliche Terraform-Version sowie das Provider-Plugin an.
  • provider "openstack": Im Best-Practive-Fall reicht hier das Ausführen der openrc-Datei vorher. Hier werden ansonsten die Verbindungsdetails für Ihre OpenStack-Umgebung angegeben (optional):
    • auth_url: Die Authentifizierungs-URL Ihres OpenStack-Anbieters.
    • region: Region der Cloud-Ressourcen.
    • user_name, password: Ihre Zugangsdaten.
  • data "openstack_images_image_v2" "ubuntu_image": Dies ist eine Datenquelle, mit der ein Image in OpenStack abgefragt wird. In diesem Fall wird automatisch das neueste passende Image mit dem Namen Ubuntu 20.04 LTS (20240821) referenziert.
    • name: Der vollständige Name des Images im OpenStack (muss exakt stimmen).
    • most_recent: Falls mehrere Images mit gleichem Namen existieren, wird das neueste verwendet.
  • resource "openstack_blockstorage_volume_v3" "example_volume": Erstellt ein Volume, das später zum Booten einer Instanz verwendet wird.
    • name: Name des Volumes.
    • size: Größe des Volumes in GB.
    • image_id: Referenziert das zuvor definierte Image aus der Datenquelle (siehe oben).
  • resource "openstack_compute_keypair_v2" "provisioning-key": Lädt ein SSH-Schlüsselpaar in OpenStack hoch, das zum Zugriff auf die Instanz dient.
    • name: Der Name des Schlüsselpaars.
    • public_key: Pfad zum public SSH-Key, z.B. ~/.ssh/my-ed25519-key.pub.
  • resource "openstack_compute_instance_v2": Definiert die zu erstellende Instanz:
    • name: Der Name der Instanz.
    • flavor_name: Das zu verwendende Flavor, z. B. a1.xs.
    • key_pair: Verwendet das zuvor definierte SSH-Keypair.
    • block_device: Konfiguriert das Booten von einem existierenden Volume:
      • uuid: Die UUID des Images, Volumes oder Snapshots.
      • source_type: Der source type muss entweder "blank", "image", "volume" oder "snapshot" sein.
      • destination_type: Der Typ, der kreiert wird.
      • boot_index: Der Boot-Index des Volumes.
      • delete_on_termination: Wenn true, wird das Volume gelöscht, sobald die Instanz gelöscht wird. Bei false bleibt es erhalten.


Das Anlegen von 2 Beispielinstanzen wird unten bei den Beispielen genauer erklärt.

Schritt 3: Terraform ausführen

  • Initialisierung: Terraform muss das Provider-Plugin herunterladen und das Arbeitsverzeichnis initialisieren. Führen Sie dazu den folgenden Befehl aus:
  terraform init
  • Konfiguration validieren: Stellen Sie sicher, dass Ihre Konfiguration keine Fehler enthält:
  terraform validate
  • Test: Testen Sie Ihre Konfig hiermit, um zu sehen, welche Ressourcen genau erstellt und/oder gelöscht werden. Hierbei wird noch nichts angewendet, und nur eine Vorschau der Änderungen angezeigt:
  terraform plan
  • Ressourcen anwenden: Hiermit werden die Ressourcen in der Openstack-Umgebung verteilt:
  terraform apply
  • Verifikation: Überprüfen Sie, ob die Ressourcen erfolgreich erstellt wurden. Sie können dazu entweder die OpenStack-CLI oder das OpenStack-Dashboard verwenden.

Schritt 4: Ressourcen verwalten und löschen

Terraform bietet Funktionen, um Ressourcen zu ändern oder zu löschen:

  • Änderungen anwenden: Passen Sie Ihre main.tf-Datei an und führen Sie erneut terraform apply aus. Vorher sollten Sie sicherheitshalber mit terraform plan prüfen, ob die Änderungen tatsächlich korrekt sind und Sie nicht aus Versehen etwas anpassen, was Sie gar nicht anpassen möchten.
  • Ressourcen löschen: Vorsicht! Der Befehl terraform destroy entfernt alle Ressourcen, die in der Konfiguration definiert sind.

SSH-Key zum Verwenden erstellen

Option 1: SSH-Key lokal erstellen und über Terraform-Konfiguration hochladen

1. SSH-Key erstellen (ed25519):

   ssh-keygen -t ed25519 -f ~/.ssh/my-ed25519-key

2. Key in Terraform über Konfigurationsdatei hochladen:

   resource "openstack_compute_keypair_v2" "my_key" {
     name       = "my-ed25519-key"
     public_key = file("~/.ssh/my-ed25519-key.pub")
   }

3. SSH-Key in der Instanz verwenden:

   resource "openstack_compute_instance_v2" "my_instance" {
     key_pair = openstack_compute_keypair_v2.my_key.name
   }

Option 2: SSH-Key lokal erstellen und per OpenStack-CLI hochladen

Hierfür wird die OpenStack CLI benötigt, siehe hier für ein Tutorial, wie man die OpenStack CLI einrichten kann.

1. SSH-Key erstellen (ed25519):

   ssh-keygen -t ed25519 -f ~/.ssh/my-ed25519-key

2. Key in OpenStack per OpenStack CLI hochladen:

   openstack keypair create --public-key ~/.ssh/my-ed25519-key.pub my-ed25519-key

3. Key in Terraform verwenden:

   resource "openstack_compute_instance_v2" "my_instance" {
     key_pair = "my-ed25519-key"  # Name des in OpenStack hochgeladenen SSH-Keys
   }

Beispiele für Terraform-Konfigurationen

Beispiel: Datenbankserver & Webserver

Die folgende Konfiguration erstellt zwei OpenStack-Instanzen. Die erste Instanz ist ein Datenbankserver, der nur über das interne Netz erreichbar ist. Die zweite Instanz ist ein Webserver, der per HTTP/HTTPS erreichbar ist. Für beide Server wird in diesem Falle ein SSH-Key verwendet.

# Terraform-Konfigurationsblock
terraform {
required_version = ">= 0.14.0"
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 1.53.0"
    }
  }
}

# Best Practice: OpenStack-Provider-Konfiguration mit source $openrc_dateiname laden
provider "openstack" {}

# OpenStack-Provider-Konfiguration direkt in der Konfig
#provider "openstack" {
#  user_name = "API-Username"
#  password = "Passwort"
#  auth_url = "https://api.afstack.io:5000/v3"
#  region = "RegionOne"
#}

# Image-Datenquelle für Ubuntu-Image; hier muss der vollständige Image-Name verwendet werden
data "openstack_images_image_v2" "ubuntu_image" {
  name        = "Ubuntu 20.04 LTS (20250403)"
  most_recent = true
}

# Datenbankserver

# OpenStack-Volume für den Datenbankserver erstellen
resource "openstack_blockstorage_volume_v3" "db_volume" {
  name     = "db-volume"
  size     = 20
  image_id = data.openstack_images_image_v2.ubuntu_image.id
}

# Security Group für Datenbankserver, Port 22 (SSH), 3306 (MySQL)
resource "openstack_networking_secgroup_v2" "db_sg" {
  name        = "db_sg"
  description = "Security Group für DB-Server"
}

resource "openstack_networking_secgroup_rule_v2" "db_sg_ssh" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 22
  port_range_max    = 22
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.db_sg.id
}

resource "openstack_networking_secgroup_rule_v2" "db_sg_mysql" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 3306
  port_range_max    = 3306
  remote_ip_prefix   = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.db_sg.id
}

# Internes Netzwerk erstellen
resource "openstack_networking_network_v2" "internal_network" {
  name           = "internal_network"
  admin_state_up = "true"
}

# Hochladen des erstellten SSH-Keys, siehe Punkt 1.6.1
resource "openstack_compute_keypair_v2" "provisioning-key" {
  name       = "my-ed25519-key"
  public_key = file("~/.ssh/my-ed25519-key.pub")
}

# Subnetz für das interne Netzwerk erstellen 
resource "openstack_networking_subnet_v2" "internal_subnet" {
  network_id = openstack_networking_network_v2.internal_network.id
  cidr       = "192.168.0.0/24"
  enable_dhcp = "false"
  no_gateway = "true"
}

# Datenbankserver-Instanz erstellen
resource "openstack_compute_instance_v2" "db_instance" {
  name      = "db_instance"
  flavor_name = "a1.xs"
  key_pair    = openstack_compute_keypair_v2.provisioning-key.name
  security_groups = [openstack_networking_secgroup_v2.db_sg.name]
  # Volume als Boot-Device verwenden
  block_device {
    uuid = openstack_blockstorage_volume_v3.db_volume.id
    source_type = "volume"
    destination_type = "volume"
    boot_index = 0
    delete_on_termination = false
  }

  # Mit dem öffentlichen Netzwerk verbinden
  network {
    name = "shared-public-network"
  }
  # Mit dem internen Netzwerk verbinden
  network {
    uuid = openstack_networking_network_v2.internal_network.id
  }
}

# Floating-IP für den Datenbankserver erstellen
resource "openstack_compute_floatingip_v2" "db_floatip" {
  pool = "af-external-customer"
}

# Floating IP mit der Instanz verknüpfen
resource "openstack_compute_floatingip_associate_v2" "db_floatip" {
  floating_ip = openstack_compute_floatingip_v2.db_floatip.address
  instance_id = openstack_compute_instance_v2.db_instance.id
}

# Webserver
 
# Volume für den Webserver erstellen
resource "openstack_blockstorage_volume_v3" "web_volume" {
  name     = "web-volume"
  size     = 20
  image_id = data.openstack_images_image_v2.ubuntu_image.id
}

# Security Group für den Webserver erstellen, Port 21 (FTP), 22 (SSH), 80/443 (HTTP/S)
resource "openstack_networking_secgroup_v2" "web_sg" {
  name        = "web_sg"
  description = "Security Group für Webserver"
}

resource "openstack_networking_secgroup_rule_v2" "web_sg_ftp" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 21
  port_range_max    = 21
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.web_sg.id
}

resource "openstack_networking_secgroup_rule_v2" "web_sg_ssh" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 22
  port_range_max    = 22
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.web_sg.id
}

resource "openstack_networking_secgroup_rule_v2" "web_sg_http" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 80
  port_range_max    = 80
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.web_sg.id
}

resource "openstack_networking_secgroup_rule_v2" "web_sg_https" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 443
  port_range_max    = 443
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.web_sg.id
}

# Webserver-Instanz erstellen 
resource "openstack_compute_instance_v2" "web_instance" {
  name      = "web_instance"
  flavor_name = "a1.xs"
  key_pair    = openstack_compute_keypair_v2.provisioning-key.name
  security_groups = [openstack_networking_secgroup_v2.web_sg.name]
  # Volume als Boot-Device verwenden
  block_device {
    uuid = openstack_blockstorage_volume_v3.web_volume.id
    source_type = "volume"
    destination_type = "volume"
    boot_index = 0
    delete_on_termination = false
  }

  # Mit dem öffentlichen Netzwerk verbinden
  network {
    name = "shared-public-network"
  }
  
  # Mit dem internen Netzwerk verbinden
  network {
    uuid = openstack_networking_network_v2.internal_network.id
  }
}

# Floating IP für den Webserver erstellen
resource "openstack_compute_floatingip_v2" "web_floatip" {
  pool = "af-external-customer"
}

# Floating IP mit der Instanz verknüpfen
resource "openstack_compute_floatingip_associate_v2" "web_floatip" {
  floating_ip = openstack_compute_floatingip_v2.web_floatip.address
  instance_id = openstack_compute_instance_v2.web_instance.id
}