Zum Inhalt springen

Infrastruktur als Code: Prinzipien, die skalieren

Behandle Infrastruktur wie Anwendungscode: versionieren, reviewen, testen und über eine Pipeline ausrollen, statt sich durch Konsolen zu klicken.

3 Min. Lesezeit
Workflow für Infrastruktur als Code von der Versionskontrolle bis zum Cloud-Deployment

Sich durch die Konsole eines Cloud-Anbieters zu klicken, um Ressourcen anzulegen, funktioniert genau einmal. Beim zweiten Mal, wenn du dasselbe Setup brauchst — in Staging, in einer neuen Region, nach einem Incident — rekonstruierst du es aus dem Gedächtnis. Infrastruktur als Code (IaC) löst dieses Problem, indem Infrastruktur in deklarativen Dateien definiert wird, die versioniert, reviewt und über dieselben Pipelines ausgerollt werden wie Anwendungscode.

Deklarativ vs. imperativ

IaC-Tools lassen sich in zwei Kategorien einteilen. Deklarative Tools (Terraform, CloudFormation, Pulumi) beschreiben den gewünschten Endzustand. Imperative Tools (Ansible, Shell-Skripte) beschreiben die Schritte, um diesen Zustand zu erreichen.

hclhcl
# Declarative (Terraform): "I want this to exist"
resource "aws_s3_bucket" "assets" {
  bucket = "myapp-assets-production"
 
  versioning {
    enabled = true
  }
 
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
 
  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}
shbash
# Imperative (shell script): "Do these steps in order"
aws s3api create-bucket --bucket myapp-assets-production --region us-east-1
aws s3api put-bucket-versioning --bucket myapp-assets-production \
  --versioning-configuration Status=Enabled
aws s3api put-bucket-encryption --bucket myapp-assets-production \
  --server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'

Der deklarative Ansatz gewinnt mit wachsender Größe, weil Terraform den Zustand nachverfolgt. Existiert der Bucket bereits mit einer anderen Konfiguration, aktualisiert Terraform ihn. Ändert jemand die Konfiguration manuell, zeigt terraform plan diese Abweichung. Das imperative Skript schlägt einfach fehl oder erzeugt Duplikate.

State-Management

Terraform verwaltet eine State-Datei, die deine deklarierten Ressourcen auf die tatsächlichen Cloud-Ressourcen abbildet. Diese Datei ist die maßgebliche Quelle für alles, was Terraform verwaltet.

hclhcl
# ❌ Local state file — works for one person, breaks for teams
terraform {
  # Default: terraform.tfstate stored locally
}
 
# ✅ Remote state with locking — safe for team collaboration
terraform {
  backend "s3" {
    bucket         = "mycompany-terraform-state"
    key            = "production/api/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-locks"  # Prevents concurrent modifications
  }
}

Modulstruktur

Mit wachsender Infrastruktur werden monolithische Konfigurationsdateien unhandlich. Module kapseln wiederverwendbare Infrastrukturmuster.

infrastructure/
├── modules/
│   ├── vpc/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── rds/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── ecs-service/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
├── environments/
│   ├── production/
│   │   └── main.tf
│   └── staging/
│       └── main.tf
└── global/
    └── iam/
        └── main.tf
hclhcl
# environments/production/main.tf — composes modules
module "vpc" {
  source = "../../modules/vpc"
 
  cidr_block          = "10.0.0.0/16"
  availability_zones  = ["us-east-1a", "us-east-1b", "us-east-1c"]
  environment         = "production"
}
 
module "database" {
  source = "../../modules/rds"
 
  vpc_id              = module.vpc.vpc_id
  subnet_ids          = module.vpc.private_subnet_ids
  instance_class      = "db.r6g.xlarge"
  engine_version      = "14.7"
  environment         = "production"
}
 
module "api_service" {
  source = "../../modules/ecs-service"
 
  vpc_id              = module.vpc.vpc_id
  subnet_ids          = module.vpc.private_subnet_ids
  container_image     = "registry.example.com/api:v1.2.0"
  desired_count       = 3
  environment         = "production"
}

Review-Prozess für Änderungen

Infrastrukturänderungen sollten denselben Review-Prozess durchlaufen wie Anwendungscode. Führe terraform plan in der CI aus, um vor dem Anwenden genau zu zeigen, was sich ändern wird.

ymlyaml
# .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
 
      - name: Terraform Init
        run: terraform init
        working-directory: infrastructure/environments/production
 
      - name: Terraform Plan
        run: terraform plan -no-color -out=tfplan
        working-directory: infrastructure/environments/production
 
      - name: Comment Plan on PR
        uses: actions/github-script@v7
        with:
          script: |
            const plan = 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${plan}\n\`\`\``
            });

Häufige Fehler vermeiden

hclhcl
# ❌ Hardcoded values scattered across files
resource "aws_instance" "api" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.medium"
  subnet_id     = "subnet-0bb1c79de3EXAMPLE"
}
 
# ✅ Variables with sensible defaults and validation
variable "instance_type" {
  description = "EC2 instance type for the API servers"
  type        = string
  default     = "t3.medium"
 
  validation {
    condition     = can(regex("^t3\\.", var.instance_type))
    error_message = "Only t3 instance types are allowed."
  }
}
 
resource "aws_instance" "api" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = var.instance_type
  subnet_id     = module.vpc.private_subnet_ids[0]
}

Die wichtigsten Punkte

  1. Deklaratives IaC erkennt Drift — Terraform zeigt, wenn die Realität vom Code abweicht
  2. Remote State mit Locking ist für Teams Pflicht — lokale State-Dateien führen zu Konflikten
  3. Module schaffen wiederverwendbare Infrastrukturmuster — kombiniere sie je nach Umgebung
  4. Führe plan bei jedem PR in der CI aus — Reviewer sollten genau sehen, was sich ändert
  5. Parametrisiere alles — hartcodierte Werte verhindern die Wiederverwendung über Umgebungen hinweg
  6. Versioniere deinen Infrastrukturcode — mit demselben Branching, Reviewing und CI/CD wie Anwendungscode
Wilfredo Rujel

Wilfredo Rujel

Full-Stack-Softwareentwickler

Diesen Beitrag teilenX