Best Practice: Brownfield Cost Optimization
Kontext
Brownfield-Cost-Optimization ist die schwierigste Disziplin der Cost-Säule. Bestehende Infrastruktur hat Abhängigkeiten, politische Geschichte und oft eine Schuldenlast, die sich über Jahre aufgebaut hat. Gleichzeitig bietet sie die größten Quick-Win-Potenziale: Idle-Instanzen, fehlende Lifecycle-Policies und ungenutzte Reservierungen sind oft mit minimalem Aufwand adressierbar.
Dieser Leitfaden strukturiert Brownfield-Optimierung in drei Phasen: Discovery → Quick Wins → Strukturelle Verbesserungen.
Zugehörige Controls
Alle WAF-COST Controls sind relevant; priorisierte Reihenfolge für Brownfield: WAF-COST-010 → WAF-COST-020 → WAF-COST-040 → WAF-COST-030 → WAF-COST-100 → WAF-COST-050
Phase 1: Discovery (Wochen 1–4)
Schritt 1.1: Kosten-Baseline erstellen
Bevor optimiert wird, muss der Ist-Zustand klar sein:
#!/bin/bash
# scripts/cost-baseline.sh – AWS Kosten-Baseline
echo "=== Cost Baseline Report ==="
echo ""
# Gesamtkosten der letzten 3 Monate
echo "--- Gesamtkosten (letzte 3 Monate) ---"
aws ce get-cost-and-usage \
--time-period Start=$(date -d '3 months ago' +%Y-%m-01),End=$(date +%Y-%m-01) \
--granularity MONTHLY \
--metrics "BlendedCost" \
--query 'ResultsByTime[].{Month:TimePeriod.Start, Cost:Total.BlendedCost.Amount}' \
--output table
echo ""
echo "--- Top-10 Kostentreiber (Service) ---"
aws ce get-cost-and-usage \
--time-period Start=$(date -d '1 month ago' +%Y-%m-01),End=$(date +%Y-%m-01) \
--granularity MONTHLY \
--metrics "BlendedCost" \
--group-by Type=DIMENSION,Key=SERVICE \
--query 'sort_by(ResultsByTime[0].Groups, &Keys[0])[-10:] | reverse(@)[].{Service:Keys[0], Cost:Metrics.BlendedCost.Amount}' \
--output table
echo ""
echo "--- Untagged Costs ---"
aws ce get-cost-and-usage \
--time-period Start=$(date -d '1 month ago' +%Y-%m-01),End=$(date +%Y-%m-01) \
--granularity MONTHLY \
--metrics "BlendedCost" \
--filter '{"Not":{"Tags":{"Key":"workload","Values":[""],"MatchOptions":["ABSENT"]}}}' \
--query 'ResultsByTime[0].Total.BlendedCost.Amount' \
--output text
Schritt 1.2: Waste-Discovery
#!/bin/bash
# scripts/waste-discovery.sh – Häufige Waste-Quellen identifizieren
echo "=== S3 Buckets ohne Lifecycle-Policy ==="
aws s3api list-buckets --query "Buckets[].Name" --output text | \
tr '\t' '\n' | while read BUCKET; do
LIFECYCLE=$(aws s3api get-bucket-lifecycle-configuration --bucket "$BUCKET" 2>/dev/null)
if [ -z "$LIFECYCLE" ]; then
SIZE=$(aws s3api list-objects-v2 --bucket "$BUCKET" \
--query "sum(Contents[].Size)" --output text 2>/dev/null || echo "0")
echo "NO LIFECYCLE: $BUCKET (${SIZE} bytes)"
fi
done
echo ""
echo "=== CloudWatch Log Groups ohne Retention ==="
aws logs describe-log-groups \
--query 'logGroups[?retentionInDays==`null`].{Name:logGroupName, StoredBytes:storedBytes}' \
--output table
echo ""
echo "=== Unbenutzte Elastic IPs ==="
aws ec2 describe-addresses \
--query 'Addresses[?AssociationId==`null`].{AllocationId:AllocationId, PublicIp:PublicIp}' \
--output table
echo ""
echo "=== Unbenutzte EBS Volumes ==="
aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query 'Volumes[].{VolumeId:VolumeId, Size:Size, CreateTime:CreateTime}' \
--output table
Schritt 1.3: Tagging-Audit
#!/bin/bash
# scripts/tagging-audit.sh – Tagging-Compliance prüfen
MANDATORY_TAGS=("cost-center" "owner" "environment" "workload")
NON_COMPLIANT=0
TOTAL=0
# EC2 Instanzen prüfen
echo "=== EC2 Tagging Compliance ==="
aws ec2 describe-instances \
--query 'Reservations[].Instances[]' \
--output json | \
jq -r '.[] | {id: .InstanceId, tags: ([.Tags[]? | {(.Key): .Value}] | add)} |
select(.tags["cost-center"] == null or .tags["owner"] == null or
.tags["environment"] == null or .tags["workload"] == null) |
"NON-COMPLIANT: \(.id)"'
Phase 2: Quick Wins (Monate 1–3)
Quick Wins sind Maßnahmen mit hohem ROI und niedrigem Implementierungsaufwand. Sie schaffen sofortige Einsparungen und Momentum für die strukturellen Verbesserungen.
Quick-Win-Priorisierungsmatrix
| Kategorie | Maßnahme | Aufwand | Typische Einsparung | Control |
|---|---|---|---|---|
Idle Shutdown |
Dev/Test-Instanzen identifizieren und abschalten |
Niedrig |
200–2.000 EUR/Monat |
WAF-COST-030 |
Log-Retention setzen |
CloudWatch Log Groups mit retention_in_days != 0 konfigurieren |
Niedrig |
50–500 EUR/Monat |
WAF-COST-040, WAF-COST-070 |
S3 Lifecycle |
Lifecycle-Policies für Top-10 größte Buckets |
Niedrig–Mittel |
100–1.000 EUR/Monat |
WAF-COST-040 |
Unbenutzte Ressourcen |
EIP, Unattached EBS, Ungenutzte Load Balancer |
Niedrig |
50–500 EUR/Monat |
WAF-COST-030 |
Non-Prod Auto-Shutdown |
Schedule für Dev/Test: Mo–Fr 8–20 Uhr |
Mittel |
500–3.000 EUR/Monat |
WAF-COST-030 |
RI-Nutzung prüfen |
Ungenutzte Reserved Instances identifizieren, umwidmen |
Mittel |
Variabel, oft signifikant |
WAF-COST-080 |
Budget-Alerting sofort einrichten (WAF-COST-020)
# Sofortmaßnahme: Budget-Alert für alle Accounts
resource "aws_budgets_budget" "monthly_total" {
name = "monthly-total-budget"
budget_type = "COST"
limit_amount = var.monthly_budget_limit
limit_unit = "USD"
time_unit = "MONTHLY"
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = [var.finops_team_email]
}
notification {
comparison_operator = "GREATER_THAN"
threshold = 100
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = [var.finops_team_email, var.engineering_manager_email]
}
}
Phase 3: Strukturelle Verbesserungen (Monate 4–18)
Strukturelle Verbesserungen adressieren die Architektonische Kostenschuld und erfordern mehr Koordination, Architecture-Board-Beteiligung und Planungsaufwand.
Strukturelle Maßnahmen
| Maßnahme | Beschreibung | Zeithorizont | Control |
|---|---|---|---|
Tagging vollständig durchsetzen |
Mandatory-Tag-Modul einführen, CI-Gate aktivieren, Legacy-Ressourcen nachtaggen |
3–6 Monate |
WAF-COST-010 |
FinOps-Review-Zyklus |
Monatliche und quartalsweise Reviews etablieren, Action-Item-Tracker |
1–2 Monate |
WAF-COST-060 |
Cost-Debt-Register |
Bestandsschuld dokumentieren, Owner zuweisen, Paydown priorisieren |
1–3 Monate |
WAF-COST-100 |
ADR-Prozess erweitern |
Cost-Impact-Sektion in ADR-Template, rückwirkend für kritische Entscheidungen |
2–4 Monate |
WAF-COST-050 |
Reservierungs-Restrukturierung |
RI-Portfolio analysieren, falsch zugeordnete RIs umstrukturieren, Savings Plans einführen |
3–6 Monate |
WAF-COST-080 |
HA-Review |
Multi-AZ/Multi-Region-Services auf SLO-Grundlage prüfen, Oversized-HA als Cost Debt erfassen |
6–12 Monate |
WAF-COST-050, WAF-COST-100 |
Cost-Debt-Register für Brownfield initialisieren
# docs/cost-debt-register.yml – Brownfield-Start
version: "1.0"
last_reviewed: "2025-03-01"
status: "initial-population"
note: >
Initial population from Brownfield Discovery Phase (Jan–Mar 2025).
All entries need owner confirmation by 2025-04-01.
entries:
- id: CD-2025-INIT-001
title: "DISCOVERY: S3-Buckets ohne Lifecycle (23 Buckets, ~2 TB)"
category: "infinite-retention"
description: "Discovery Phase identifizierte 23 S3-Buckets ohne Lifecycle-Policy."
detected: "2025-03-01"
owner: "TBD - Owner-Identifikation läuft"
estimated_annual_impact_eur: 3600
status: "monitoring"
paydown_plan: "Lifecycle-Policies für alle 23 Buckets bis Q2 2025"
target_resolution: "2025-06-30"
related_controls: [WAF-COST-040]
- id: CD-2025-INIT-002
title: "DISCOVERY: 12 CloudWatch Log Groups ohne Retention"
category: "infinite-retention"
detected: "2025-03-01"
owner: "infrastructure-team"
estimated_annual_impact_eur: 800
status: "paydown"
paydown_plan: "Terraform-Config für alle 12 Log Groups - Sprint 2025-03-2"
target_resolution: "2025-04-01"
related_controls: [WAF-COST-040, WAF-COST-070]
Brownfield-spezifische Herausforderungen
Politische Koordination
Brownfield-Optimierung berührt Teams, die ihre Infrastruktur über Jahre gewachsen haben.
Empfehlungen:
-
Kosten sichtbar machen, nicht anklagen: Showback vor Chargeback
-
Quick Wins zuerst: Kleine Erfolge schaffen Vertrauen
-
Ownership klären: Wer bezahlt, wer entscheidet – muss klar sein
-
Architecture Board als neutrale Instanz: Cost-Debt-Register-Entscheidungen vom AB trägt politischen Druck
Legacy-Ressourcen ohne klaren Owner
#!/bin/bash
# Ressourcen ohne klaren Owner identifizieren
aws ec2 describe-instances \
--query 'Reservations[].Instances[?!Tags[?Key==`owner`]].{
InstanceId: InstanceId,
LaunchTime: LaunchTime,
InstanceType: InstanceType,
Name: Tags[?Key==`Name`].Value|[0]
}' \
--output table
Für Ressourcen ohne Owner: Letzten Modifier im CloudTrail identifizieren, Architecture Board als Default-Owner bis Klärung.
Metriken (Brownfield-spezifisch)
-
Untagged-Cost-Rate: Ziel monatlich -5% bis Ziel < 5% erreicht
-
Waste-Reduktion: EUR/Monat gegenüber Discovery-Baseline (Ziel: -20% in 6 Monaten)
-
Lifecycle-Coverage: % S3-Buckets mit Lifecycle-Policy (Ziel: 100% in 3 Monaten)
-
Cost-Debt-Register-Vollständigkeit: Alle Discovery-Findings erfasst mit Owner (Ziel: 100% in 2 Monaten)