Loading...
Alle Artikel
Infrastructure · 6 min read

Terraform Best Practices: IaC skalierbar strukturieren

Lernen Sie, wie Sie Terraform-Projekte für Wartbarkeit, Teamarbeit und produktive Infrastruktur im großen Maßstab strukturieren.

Warum die Terraform-Struktur zählt

Jedes Infrastruktur-Team beginnt gleich: mit einer einzigen main.tf-Datei, die ein paar Ressourcen provisioniert. Für einen Proof of Concept funktioniert das prima. Dann wächst das Projekt, mehr Engineers steigen ein, und plötzlich hat diese monolithische Datei 2.000 Zeilen, niemand weiß mehr, was wovon abhängt, und jedes terraform plan dauert acht Minuten.

Kommt Ihnen bekannt vor? Die Art und Weise, wie Sie Ihren Terraform-Code strukturieren, hat direkten Einfluss darauf, wie schnell Ihr Team liefern kann, wie sicher Sie Änderungen umsetzen können und wie leicht neue Engineers eingearbeitet werden. In diesem Leitfaden teilen wir die Patterns, die wir bei DevOpsVibe über Dutzende Produktionsumgebungen hinweg einsetzen.

Die modulbasierte Architektur

Die wichtigste Einzelentscheidung, die Sie treffen können, ist, früh eine modulbasierte Architektur einzuführen. Module sind wiederverwendbare, testbare Infrastruktur-Einheiten, die eine logische Gruppierung von Ressourcen kapseln.

Verzeichnisstruktur

Dies ist die Struktur, die wir für mittlere bis große Projekte empfehlen:

infrastructure/
├── modules/
│   ├── networking/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── README.md
│   ├── compute/
│   ├── database/
│   └── monitoring/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   ├── staging/
│   └── production/
├── global/
│   ├── iam/
│   └── dns/
└── terragrunt.hcl  # optional

Jedes Environment-Verzeichnis komponiert Module mit umgebungsspezifischen Variablen. Die Module selbst enthalten keine fest eingebauten Werte.

Ein wiederverwendbares Modul schreiben

Ein gut strukturiertes Modul hat klare Inputs, Outputs und eine einzige Verantwortlichkeit:

# modules/networking/variables.tf
variable "vpc_cidr" {
  description = "CIDR block for the VPC"
  type        = string

  validation {
    condition     = can(cidrnetmask(var.vpc_cidr))
    error_message = "Must be a valid CIDR block."
  }
}

variable "environment" {
  description = "Environment name (dev, staging, production)"
  type        = string
}

variable "availability_zones" {
  description = "List of AZs to use"
  type        = list(string)
}

# modules/networking/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name        = "${var.environment}-vpc"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

resource "aws_subnet" "private" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 8, count.index)
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name        = "${var.environment}-private-${var.availability_zones[count.index]}"
    Environment = var.environment
    Type        = "private"
  }
}

# modules/networking/outputs.tf
output "vpc_id" {
  description = "ID of the created VPC"
  value       = aws_vpc.main.id
}

output "private_subnet_ids" {
  description = "List of private subnet IDs"
  value       = aws_subnet.private[*].id
}

State-Management-Strategie

Remote State ist für Teams nicht verhandelbar. Nutzen Sie S3 mit DynamoDB-Locking (AWS) oder GCS mit Locking (GCP):

# environments/production/backend.tf
terraform {
  backend "s3" {
    bucket         = "mycompany-terraform-state"
    key            = "production/networking/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

Schlüsselregeln für State Management:

  • Eine State-Datei pro Komponente und Umgebung. Legen Sie niemals Ihre gesamte Infrastruktur in einer einzigen State-Datei ab. Wenn Ihr VPC-State korrumpiert wird, soll er nicht Ihre Datenbank mit in den Abgrund reißen.
  • Encryption at Rest aktivieren. State-Dateien enthalten sensible Daten, darunter Passwörter und Private Keys.
  • State-Locking nutzen. Ohne es können zwei Engineers, die gleichzeitig terraform apply ausführen, Ihren State korrumpieren.
  • State-Dateien niemals in die Versionskontrolle einchecken. Fügen Sie *.tfstate und *.tfstate.backup zu Ihrer .gitignore hinzu.

Variablen-Management

Vermeiden Sie hart einkodierte Werte. Nutzen Sie einen mehrschichtigen Ansatz:

# environments/production/terraform.tfvars
environment        = "production"
vpc_cidr           = "10.0.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
instance_type      = "m6i.xlarge"
min_capacity       = 3
max_capacity       = 10

Für Secrets gilt: niemals in .tfvars-Dateien ablegen. Referenzieren Sie sie stattdessen aus einem Secrets Manager:

data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "production/database/master-password"
}

resource "aws_db_instance" "main" {
  password = data.aws_secretsmanager_secret_version.db_password.secret_string
  # ...
}

Tagging-Strategie

Konsistentes Tagging ist essenziell für Kostenallokation, Security-Auditing und Ressourcenmanagement. Definieren Sie ein gemeinsames Tagging-Modul:

# modules/tags/main.tf
variable "environment" { type = string }
variable "project" { type = string }
variable "team" { type = string }

locals {
  common_tags = {
    Environment = var.environment
    Project     = var.project
    Team        = var.team
    ManagedBy   = "terraform"
    Repository  = "github.com/mycompany/infrastructure"
  }
}

output "tags" {
  value = local.common_tags
}

Und nutzen Sie es überall:

module "tags" {
  source      = "../../modules/tags"
  environment = "production"
  project     = "platform"
  team        = "infrastructure"
}

resource "aws_instance" "app" {
  # ...
  tags = merge(module.tags.tags, {
    Name = "app-server"
    Role = "application"
  })
}

CI/CD-Integration

Terraform sollte in der Produktion niemals vom Laptop eines Entwicklers aus laufen. Setzen Sie eine Pipeline auf:

  1. Pull Request wird geöffnet -- terraform fmt -check und terraform validate laufen automatisch
  2. PR wird genehmigt -- terraform plan läuft und die Ausgabe wird als PR-Kommentar gepostet
  3. PR wird in main gemergt -- terraform apply -auto-approve wird in der Pipeline ausgeführt

Verwenden Sie Tools wie Atlantis oder Spacelift für diesen Workflow oder bauen Sie Ihren eigenen mit GitHub Actions:

# .github/workflows/terraform.yml
name: Terraform
on:
  pull_request:
    paths: ['infrastructure/**']

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
        working-directory: infrastructure/environments/production
      - run: terraform plan -no-color -out=tfplan
        working-directory: infrastructure/environments/production
      - uses: actions/github-script@v7
        with:
          script: |
            const output = require('fs').readFileSync('tfplan.txt', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Terraform Plan\n\`\`\`\n${output}\n\`\`\``
            });

Ihre Infrastruktur testen

Nutzen Sie Terratest oder terraform test (eingebaut seit Terraform 1.6), um Ihre Module zu validieren:

# modules/networking/tests/vpc.tftest.hcl
run "creates_vpc_with_correct_cidr" {
  command = plan

  variables {
    vpc_cidr           = "10.0.0.0/16"
    environment        = "test"
    availability_zones = ["us-east-1a"]
  }

  assert {
    condition     = aws_vpc.main.cidr_block == "10.0.0.0/16"
    error_message = "VPC CIDR block did not match expected value"
  }
}

Häufige Anti-Patterns, die es zu vermeiden gilt

  • Monolithische State-Dateien. Splitten Sie nach Komponente und Umgebung.
  • count verwenden, wo for_each besser passt. for_each mit Maps liefert stabile Ressourcen-Adressen, die sich nicht verschieben, wenn Einträge hinzugefügt oder entfernt werden.
  • Drift ignorieren. Lassen Sie terraform plan regelmäßig laufen, um manuelle Änderungen zu erkennen.
  • terraform fmt überspringen. Erzwingen Sie Formatierung in CI. Inkonsistente Formatierung erzeugt laute Diffs.
  • Provider-Versionen fest einkodieren. Pinnen Sie sie explizit, aber prüfen Sie Updates regelmäßig.

Fazit

Terraform von Anfang an gut zu strukturieren, spart später exponentiellen Aufwand. Die oben beschriebenen Patterns -- modulbasierte Architektur, isolierter State, geschichtete Variablen, CI/CD-Automatisierung und Tests -- bilden das Fundament jedes skalierbaren Infrastruktur-Projekts, das wir liefern.

Bei DevOpsVibe helfen wir Teams, Terraform-Architekturen zu designen und umzusetzen, die von einer Handvoll Ressourcen auf Tausende skalieren. Ob Sie frisch starten oder eine bestehende Codebasis entwirren – unsere Engineers bringen Ihre Infrastruktur auf solides Fundament. Nehmen Sie Kontakt mit uns auf, um mehr zu erfahren.

abgelegt unter
terraformterraformiacinfrastructureawsawsdevopsautomation
mit uns arbeiten

Soll unser Team Ihrer Infrastruktur helfen?

talk to an engineerFree 30-min discovery callBook
close