WAF++ WAF++
Back to WAF++ Homepage

Best Practice: Multi-AZ & High-Availability-Architektur

Kontext

Eine Availability Zone (AZ) ist ein physisch isoliertes Rechenzentrum innerhalb einer Region. AZ-Ausfälle sind die häufigsten cloud-infrastrukturellen Ereignisse und betreffen mehrmals jährlich Produktionssysteme. Ein Service, der in einer einzigen AZ deployed ist, erleidet während eines AZ-Ereignisses einen vollständigen Ausfall.

Häufige Probleme ohne Multi-AZ-Konfiguration:

  • RDS Single-AZ: Bei AZ-Ausfall mehrere Minuten bis Stunden nicht erreichbar

  • Single-AZ Compute: Bei AZ-Ausfall alle Instanzen gleichzeitig ausgefallen

  • Kubernetes ohne Pod Topology Spread: Alle Pods enden in derselben AZ

Zugehörige Controls

  • WAF-REL-030 – Multi-AZ High Availability Deployment

Zielbild

Jeder Produktions-Stack deployed über mindestens 2 AZs:

  • Compute: Auto Scaling Group oder Kubernetes mit AZ-Verteilung

  • Datenbank: Multi-AZ mit automatischem Failover

  • Cache: Multi-AZ Replication Group

  • Load Balancer: Subnets in min. 2 AZs

Technische Umsetzung

AWS: Multi-AZ RDS + Auto Scaling Group

# Subnet Group über 3 AZs
resource "aws_db_subnet_group" "main" {
  name       = "payment-db-subnet-group"
  subnet_ids = [
    aws_subnet.private_az1.id,
    aws_subnet.private_az2.id,
    aws_subnet.private_az3.id,
  ]
  tags = var.mandatory_tags
}

# Multi-AZ RDS
resource "aws_db_instance" "main" {
  identifier             = "payment-db-prod"
  engine                 = "postgres"
  engine_version         = "15.4"
  instance_class         = "db.t3.medium"
  allocated_storage      = 100
  storage_type           = "gp3"
  multi_az               = true        # Automatisches Failover in < 2 Minuten
  db_subnet_group_name   = aws_db_subnet_group.main.name
  backup_retention_period = 14
  deletion_protection    = true
  tags                   = var.mandatory_tags
}

# Auto Scaling Group über 2 AZs
resource "aws_autoscaling_group" "api" {
  name               = "payment-api-asg"
  min_size           = 2    # Mindestens 1 pro AZ
  max_size           = 10
  desired_capacity   = 2
  vpc_zone_identifier = [
    aws_subnet.private_az1.id,
    aws_subnet.private_az2.id,
  ]
  target_group_arns = [aws_lb_target_group.api.arn]

  # Gleichmäßige AZ-Verteilung erzwingen
  capacity_rebalance = true

  tag {
    key                 = "Name"
    value               = "payment-api"
    propagate_at_launch = true
  }
}

# ALB über 2 AZs
resource "aws_lb" "main" {
  name               = "payment-api-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets = [
    aws_subnet.public_az1.id,
    aws_subnet.public_az2.id,  # Mindestens 2 AZs
  ]
  tags = var.mandatory_tags
}

Kubernetes: Pod Topology Spread Constraints

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-api
spec:
  replicas: 4
  template:
    spec:
      # Pflicht: Pods gleichmäßig auf AZs verteilen
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule   # Deployment scheitert statt Single-AZ
          labelSelector:
            matchLabels:
              app: payment-api

        # Optional: Pods auf verschiedene Nodes innerhalb AZ
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: ScheduleAnyway  # Soft constraint
          labelSelector:
            matchLabels:
              app: payment-api

      # Anti-Affinity als Fallback
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values: ["payment-api"]
              topologyKey: topology.kubernetes.io/zone

Azure: Zone-Redundant PostgreSQL

resource "azurerm_postgresql_flexible_server" "main" {
  name                = "payment-db-prod"
  resource_group_name = azurerm_resource_group.main.name
  location            = "westeurope"
  version             = "15"
  sku_name            = "GP_Standard_D4s_v3"
  storage_mb          = 32768
  availability_zone   = "1"

  # Zone-Redundant High Availability
  high_availability {
    mode                      = "ZoneRedundant"
    standby_availability_zone = "2"    # Separates physisches Rechenzentrum
  }

  backup_retention_days        = 14
  geo_redundant_backup_enabled = true

  tags = var.mandatory_tags
}

GCP: Multi-Zone GKE + Cloud SQL

resource "google_container_cluster" "main" {
  name     = "payment-cluster"
  location = var.region  # Regional = Multi-Zone

  node_pool {
    name       = "default-pool"
    node_count = 1  # Pro Zone

    node_config {
      machine_type = "e2-standard-4"
    }

    # Automatisch auf alle AZs der Region verteilt
  }

  # Pod Anti-Affinity Default aktivieren
  workload_metadata_config {
    mode = "GKE_METADATA"
  }
}

resource "google_sql_database_instance" "main" {
  name             = "payment-db-prod"
  database_version = "POSTGRES_15"
  region           = var.region

  settings {
    tier              = "db-custom-4-15360"
    availability_type = "REGIONAL"   # Automatisches Zonen-Failover

    backup_configuration {
      enabled                        = true
      point_in_time_recovery_enabled = true
      start_time                     = "02:00"
    }
  }
}

Typische Fehlmuster

  • Multi-AZ nur für DB, Single-AZ für Compute: Bei AZ-Ausfall hat die DB zwar Failover, aber der App-Server ist weg

  • Auto Scaling Group ohne Min Size 2: Bei AZ-Ausfall läuft min_size = 1 in nur einer AZ

  • Kubernetes Pods ohne Topology Spread: Scheduler platziert alle Pods in einer AZ (Standard-Verhalten)

  • Staging Single-AZ, Produktion Multi-AZ: Produktionsunterschiede führen zu ungetesteten Failover-Pfaden

Metriken

  • AZ-Verteilung: % der Instanzen/Pods pro AZ (Ziel: gleichmäßig ±20%)

  • Single-AZ Resource Count: Anzahl Produktionsressourcen in nur einer AZ (Ziel: 0)

  • Failover Test Frequency: Anzahl getesteter AZ-Failover pro Quartal (Ziel: >= 1)

Reifegrad

Level 1 – Alle Ressourcen in einer AZ
Level 2 – Datenbanken Multi-AZ, Compute Single-AZ
Level 3 – Alles Multi-AZ, ASG min_size >= 2, Kubernetes Topology Spread
Level 4 – Automatisierter AZ-Failover-Test, AZ-Distribution-Metriken getrackt
Level 5 – Multi-Region Active-Active oder Active-Passive mit globalem Load Balancing