Zum Hauptinhalt springen
Terraform Remote Backends: Zusammenarbeit im Team ohne State Probleme

Terraform Remote Backends: Zusammenarbeit im Team ohne State Probleme

·4 min·
Terraform GitLab Hetzner DevOps Collaboration Tutorial
Inhaltsverzeichnis

Einleitung
#

Mit Terraform beschreiben wir Infrastruktur als Code. Wir führen terraform apply aus und die Server stehen bereit. Wenn wir alleine arbeiten, reicht die lokale State Datei auf der Festplatte aus.

Sobald aber mehrere Personen an einem Projekt arbeiten, gibt es Probleme:

  • Wer hat den aktuellen Stand der Infrastruktur?
  • Was passiert wenn zwei Personen gleichzeitig Änderungen machen?
  • Sensible Daten wie Passwörter liegen im Klartext in der Datei. Sie dürfen nicht in Git gespeichert werden.

Die Lösung sind Remote Backends. Sie speichern den Zustand der Infrastruktur an einem zentralen Ort. Sie sperren den Zustand bei Änderungen und halten Passwörter aus Git fern.

In diesem Artikel richten wir zwei Lösungen ein: Das GitLab HTTP Backend und Hetzner Object Storage.

Warum lokaler State im Team nicht funktioniert
#

Die lokale State Datei ist für Teams ungeeignet:

  1. Kein gemeinsamer Speicher: Wenn Entwickler A Server erstellt, weiß Entwickler B nichts davon. Führt Entwickler B ein apply aus, überschreibt er die Änderungen von Entwickler A.
  2. Fehlende Sperre: Wenn zwei Personen gleichzeitig Änderungen ausführen, wird die State Datei beschädigt.
  3. Sicherheitsrisiken: Passwörter liegen im Klartext im State. Wer die Datei in Git speichert, gibt sensible Daten preis.

Ein Remote Backend löst diese Probleme:

flowchart TD
    DevA["Entwickler A"]
    DevB["Entwickler B"]
    Lock["State Sperre"]
    State["Zentraler State"]

    DevA -->|Schritt 1: Sperre anfordern| Lock
    Lock -->|Schritt 2: Sperre erteilt| DevA
    DevA -->|Schritt 3: Zustand schreiben| State
    DevB -->|Schritt 4: Sperre anfordern| Lock
    Lock -.->|Schritt 5: Blockiert| DevB
    DevA -->|Schritt 6: Sperre freigeben| Lock

    style DevA fill:#1e40af,stroke:#1e3a8a,color:#fff
    style Lock fill:#ef4444,stroke:#b91c1c,color:#fff
    style State fill:#10b981,stroke:#047857,color:#fff

Lösung 1: GitLab HTTP Backend
#

Nutzt das Team bereits GitLab, ist das HTTP Backend die einfachste Wahl. GitLab hat eine eigene Oberfläche für Terraform States und unterstützt das Sperren über HTTP.

Die Terraform Konfiguration
#

Wir halten den Backend Block in der Konfiguration leer. Die Zugangsdaten übergeben wir später beim Start. So vermeiden wir Passwörter im Code.

Erstellen wir die Datei backend.tf:

terraform {
  backend "http" {
  }
}

Initialisierung auf der Kommandozeile
#

Wir benötigen ein Personal Access Token aus GitLab mit den Rechten für die API. Die Initialisierung erfolgt mit diesem Befehl:

terraform init \
  -backend-config=address=https://gitlab.com/api/v4/projects/<PROJECT_ID>/terraform/state/<STATE_NAME> \
  -backend-config=lock_address=https://gitlab.com/api/v4/projects/<PROJECT_ID>/terraform/state/<STATE_NAME>/lock \
  -backend-config=unlock_address=https://gitlab.com/api/v4/projects/<PROJECT_ID>/terraform/state/<STATE_NAME>/lock \
  -backend-config=username=<GITLAB_USERNAME> \
  -backend-config=password=<PERSONAL_ACCESS_TOKEN> \
  -backend-config=lock_method=POST \
  -backend-config=unlock_method=DELETE \
  -backend-config=retry_wait_min=5

Ersetzen Sie die Platzhalter:

  • <PROJECT_ID>: Die ID Ihres GitLab Projekts.
  • <STATE_NAME>: Der Name für den Zustand.

Der Zustand ist in GitLab unter Operate > Terraform states sichtbar.

Nutzung in GitLab Pipelines
#

In einer Pipeline nutzen wir das Token des Runners. Wir müssen keine privaten Passwörter hinterlegen.

Hier ist ein einfaches Beispiel für die Datei .gitlab-ci.yml:

image: hashicorp/terraform:light

variables:
  TF_STATE_NAME: "default"
  TF_ADDRESS: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}"

before_script:
  - terraform init \
      -backend-config="address=${TF_ADDRESS}" \
      -backend-config="lock_address=${TF_ADDRESS}/lock" \
      -backend-config="unlock_address=${TF_ADDRESS}/lock" \
      -backend-config="username=gitlab-ci-token" \
      -backend-config="password=${CI_JOB_TOKEN}" \
      -backend-config="lock_method=POST" \
      -backend-config="unlock_method=DELETE"

stages:
  - validate
  - plan
  - apply

validate:
  stage: validate
  script:
    - terraform validate

plan:
  stage: plan
  script:
    - terraform plan
  artifacts:
    paths:
      - plan.tfplan

apply:
  stage: apply
  script:
    - terraform apply -auto-approve plan.tfplan
  dependencies:
    - plan
  when: manual

Lösung 2: Hetzner Object Storage
#

Hetzner bietet günstigen und DSGVO konformen Object Storage an. Wir nutzen dafür das S3 Backend von Terraform. Da es sich nicht um AWS S3 handelt, müssen wir einige Prüfungen deaktivieren.

Einschränkungen beim Sperren
#

Das S3 Backend von Terraform sperrt den Zustand normalerweise über AWS DynamoDB. Da Hetzner Object Storage diese Datenbank nicht anbietet, gibt es kein automatisches Sperren.

Im Team sollten Änderungen daher nur über Pipelines laufen. So verhindern wir parallele Änderungen.

Die Terraform Konfiguration für Hetzner S3
#

Wir erstellen einen Bucket und Zugangsdaten in der Hetzner Cloud Console.

Die Datei backend.tf sieht so aus:

terraform {
  backend "s3" {
    bucket                      = "ihr-terraform-state-bucket"
    key                         = "env/production/terraform.tfstate"
    region                      = "eu-central-1" # Dummy Wert für Terraform
    
    endpoints = {
      s3 = "https://nbg1.your-objectstorage.com" # Region des Buckets
    }

    # AWS Prüfungen deaktivieren
    skip_credentials_validation = true
    skip_metadata_api_check     = true
    skip_region_validation      = true
    skip_requesting_account_id  = true
    
    use_path_style              = true
  }
}

Initialisierung und Zugangsdaten
#

Die Zugangsdaten speichern wir in Umgebungsvariablen. Terraform liest diese Variablen automatisch:

export AWS_ACCESS_KEY_ID="IHR_HETZNER_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="IHR_HETZNER_SECRET_KEY"

terraform init

Terraform speichert den Zustand nun auf den Servern von Hetzner.

Migration des lokalen Zustands
#

Wenn wir einen lokalen Zustand verschieben möchten, gehen wir so vor:

  1. Wir fügen den neuen Backend Block hinzu.
  2. Wir führen terraform init aus.
  3. Wir bestätigen die Abfrage mit yes um die Daten zu kopieren.

Terraform lädt den Zustand hoch und benennt die lokale Datei um.

Fazit
#

Ein Remote Backend ist für die Arbeit im Team notwendig.

  • GitLab HTTP ist einfach einzurichten und bietet ein automatisches Sperren des Zustands.
  • Hetzner Object Storage ist günstig und datenschutzfreundlich, benötigt aber Pipelines zur Koordination.

Kontaktieren Sie uns gerne über die unten genannten Kanäle bei Fragen.

Timo Staudinger
Autor
Timo Staudinger
Staff Engineer Cloud/DevOps

Verwandte Artikel

CI/CD in der Praxis
·5 min
DevOps CI-CD GitLab GitHub Actions Automation
Ein kleiner Überblick, was es bei CI/CD zu beachten gibt und wie Sie es erfolgreich einsetzen
uv: Die nächste Generation der Python-Entwicklung
·5 min
Python Uv Package Manager DevOps Tools How-To
Vorstellung des Python Paketmanagers uv. Wie funktioniert es? In welchen Bereichen kann man es verwenden? Und der Vergleich mit etablierten Werkzeugen wie pip und venv.
Blue-Green Deployment mit Kubernetes
·4 min
Kubernetes DevOps Deployment Zero Downtime How-To
Risiken minimieren, Verfügbarkeit maximieren. Releases mit möglichst wenig Downtime und schneller Reaktionszeit bei Problemen.