Kaos Mühendisliği: Üretim Ortamında Kontrollü Hata Testleri
Üretim ortamında bir şeyler bozulacak. Bu kaçınılmaz. Soru şu: bunu siz mi kontrol edersiniz, yoksa o mu sizi? İşte kaos mühendisliği tam da bu noktada devreye giriyor. Netflix’in 2008’deki o meşhur veritabanı çöküşünden sonra doğan bu disiplin, “sistemleriniz gerçekten ne kadar dayanıklı?” sorusunu sormak için kasıtlı olarak hata üretmeyi esas alıyor.
Kaos Mühendisliği Nedir ve Neden Önemlidir
Kaos mühendisliği, bir sistemin beklenmedik koşullara dayanma kapasitesini ölçmek için kontrollü deneyler yapma pratiğidir. Yük testinden farklı olarak sadece “ne kadar trafik kaldırır?” sorusunu değil, “bir servis çökünce ne olur?”, “ağ gecikmesi artınca kullanıcı deneyimi nasıl etkilenir?”, “disk dolunca uygulama nasıl tepki verir?” gibi sorular sorar.
Bu yaklaşımın temel felsefesi şu: hataları prodüksiyonda bulmak yerine, siz onları bulun. Kontrollü bir ortamda, gece 2’de değil, ekibiniz hazırken.
Bir örnek verelim. Diyelim ki üç node’lu bir veritabanı kümeniz var. Failover mekanizmanız test edilmedi. Bir gün primary node çöktüğünde secondary’nin devreye girmesi 90 saniye sürdü, bu süre zarfında uygulamanız bağlantı havuzunu tüketti ve restart almadan toparlayamadı. Bu senaryo, kaos testi yapılmış olsaydı önceden keşfedilirdi.
Temel Kavramlar ve Prensipler
Kaos mühendisliğinde birkaç temel prensip var:
- Kararlı durum hipotezi: Sisteminizin normal çalışma durumunu tanımlayın. Örneğin “saniyede 500 istek işleniyor, hata oranı %0.1’in altında, P99 gecikme 200ms altında.”
- Kontrol ve deney grupları: Değişikliği her yere aynı anda uygulamayın. Bir kısmı normal çalışsın, bir kısmında hata üretin.
- Küçük başlayın: İlk testiniz prodüksiyon trafiğinin %1’ini etkilesin, %100’ünü değil.
- Otomatize edin: Manuel testler tutarsızdır. Kaos deneyleri tekrarlanabilir olmalı.
Ortam Hazırlığı
Kaos mühendisliğine başlamadan önce bazı altyapısal hazırlıklar şart:
Gözlemlenebilirlik (Observability) altyapısı kurulu olmalı. Metrik topluyorsanız, log aggregation yapıyorsanız, distributed tracing kullanıyorsanız hazırsınız demektir. Prometheus + Grafana yaygın tercih.
Otomatik rollback mekanizmanız olmalı. Bir şeyler yanlış gidince durduracak mekanizma olmadan kaos testi, sadece kaos olur.
Ekip ve iletişim kanalları hazır olmalı. Test sırasında on-call olan kişi kim? Slack kanalı var mı? Incident response playbook yazılı mı?
Basit bir Prometheus alert kuralı örneği:
cat > /etc/prometheus/rules/chaos_alerts.yml << 'EOF'
groups:
- name: chaos_experiments
rules:
- alert: ErrorRateTooHigh
expr: rate(http_requests_total{status=~"5.."}[1m]) / rate(http_requests_total[1m]) > 0.05
for: 2m
labels:
severity: critical
annotations:
summary: "Hata oranı %5 üzerine çıktı, kaos deneyi durdurulmalı"
- alert: P99LatencyHigh
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 2
for: 1m
labels:
severity: warning
annotations:
summary: "P99 gecikme 2 saniyeyi aştı"
EOF
Chaos Monkey ve Araç Ekosistemi
Netflix’in açık kaynak ettiği Chaos Monkey, bu alanın sembolü haline geldi. Ama günümüzde çok daha zengin bir araç ekosistemi var.
Chaos Monkey kurulumu (Spinnaker entegrasyonu olmadan basit versiyon):
# Chaos Monkey'i binary olarak indirme
wget https://github.com/Netflix/chaosmonkey/releases/download/v2.3.0/chaosmonkey_2.3.0_linux_amd64.tar.gz
tar -xzf chaosmonkey_2.3.0_linux_amd64.tar.gz
mv chaosmonkey /usr/local/bin/
# Konfigürasyon dosyası oluşturma
cat > /etc/chaosmonkey/config.toml << 'EOF'
[chaosmonkey]
enabled = true
schedule_enabled = true
leashed = false # true yaparsanız sadece log yazar, gerçekten öldürmez
[database]
host = "localhost"
port = 5432
name = "chaosmonkey"
user = "chaosmonkey"
password = "secure_password"
[spinnaker]
endpoint = "http://spinnaker-gate:8084"
EOF
Ancak dürüst olmak gerekirse, pek çok ekip için Chaos Toolkit daha erişilebilir bir başlangıç noktası. Python tabanlı, DSL kullanıyor ve Kubernetes, AWS, Azure gibi provider’larla doğrudan entegre oluyor.
# Chaos Toolkit kurulumu
pip install chaostoolkit
pip install chaostoolkit-kubernetes
pip install chaostoolkit-prometheus
# Basit bir deney dosyası
cat > pod_failure_experiment.json << 'EOF'
{
"version": "1.0.0",
"title": "Bir pod çökünce servis ayakta kalıyor mu?",
"description": "payment-service pod'larından birini öldürüp sistemin toparlanmasını test ediyoruz",
"steady-state-hypothesis": {
"title": "Uygulama sağlıklı çalışıyor",
"probes": [
{
"type": "probe",
"name": "uygulamaya-erisim-var",
"tolerance": 200,
"provider": {
"type": "http",
"url": "http://payment-service/health",
"timeout": 3
}
}
]
},
"method": [
{
"type": "action",
"name": "payment-pod-sil",
"provider": {
"type": "python",
"module": "chaosk8s.pod.actions",
"func": "terminate_pods",
"arguments": {
"label_selector": "app=payment-service",
"ns": "production",
"qty": 1
}
},
"pauses": {
"after": 30
}
}
],
"rollbacks": []
}
EOF
# Deneyi çalıştırma
chaos run pod_failure_experiment.json
Linux Seviyesinde Kaos: tc ve Fault Injection
Kernel seviyesinde ağ problemleri simüle etmek için tc (traffic control) aracı paha biçilmez. Bu yöntemi uygulamak için root yetkisi yeterli, ekstra araç gerekmez.
# Ağ arayüzüne gecikme ekleme (production'da dikkatli kullanın!)
# eth0 üzerinden çıkan trafike 100ms gecikme + 20ms jitter ekle
tc qdisc add dev eth0 root netem delay 100ms 20ms
# Gecikme eklendikten sonra durumu kontrol et
tc qdisc show dev eth0
# Paket kaybı simülasyonu (%10 paket kaybı)
tc qdisc change dev eth0 root netem loss 10%
# Bant genişliği kısıtlama (1mbit/s)
tc qdisc add dev eth0 root tbf rate 1mbit burst 32kbit latency 400ms
# Tüm kaosu temizle (rollback)
tc qdisc del dev eth0 root
# Sadece belirli bir IP'ye gecikme eklemek için (daha güvenli)
tc qdisc add dev eth0 root handle 1: prio
tc qdisc add dev eth0 parent 1:3 handle 30: netem delay 200ms
tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32
match ip dst 10.0.1.50/32 flowid 1:3
Gerçek dünya senaryosu: Bir e-ticaret platformunda ödeme servisinin banka API’sine olan bağlantısına gecikme eklediğimizde, uygulama 30 saniye timeout’ta timeout exception fırlatıyordu ama kullanıcıya “işlem başarılı” mesajı gönderiyordu. İkinci deneme yapıldığında çift ödeme alınıyordu. Bu bug, kaos testi sayesinde prodüksiyon öncesinde yakalandı.
Disk ve Kaynak Tükenmesi Testleri
Disk dolması, gerçekte sıkça yaşanan ve çoğunlukla hazırlıksız yakalayan bir senaryo:
#!/bin/bash
# disk_pressure_test.sh
# UYARI: Bu scripti test ortamında çalıştırın!
MOUNT_POINT="/var/log"
TARGET_FILL_PERCENT=90
LOG_FILE="/tmp/chaos_disk_test.log"
echo "$(date): Disk basıncı testi başlıyor" >> $LOG_FILE
# Mevcut disk kullanımını al
CURRENT_USAGE=$(df $MOUNT_POINT | awk 'NR==2 {print $5}' | tr -d '%')
TOTAL_SIZE=$(df $MOUNT_POINT | awk 'NR==2 {print $2}')
TARGET_SIZE=$(( (TOTAL_SIZE * TARGET_FILL_PERCENT / 100) - (TOTAL_SIZE * CURRENT_USAGE / 100) ))
echo "$(date): Hedef doldurma: ${TARGET_SIZE}KB" >> $LOG_FILE
# Test dosyası oluştur
dd if=/dev/zero of=$MOUNT_POINT/chaos_test_file bs=1M count=$((TARGET_SIZE/1024)) 2>&1 >> $LOG_FILE
echo "$(date): Disk doluluğu: $(df $MOUNT_POINT | awk 'NR==2 {print $5}')" >> $LOG_FILE
# Uygulamanın davranışını test et
sleep 60
# Temizlik
echo "$(date): Test dosyası siliniyor" >> $LOG_FILE
rm -f $MOUNT_POINT/chaos_test_file
echo "$(date): Test tamamlandı" >> $LOG_FILE
Bellek baskısı için:
# stress-ng ile bellek baskısı oluşturma
# 4GB bellek kullan, 60 saniye boyunca
stress-ng --vm 2 --vm-bytes 2G --timeout 60s --metrics-brief
# CPU basıncı testi
stress-ng --cpu 4 --timeout 30s --metrics-brief
# I/O basıncı
stress-ng --io 4 --timeout 30s --metrics-brief
Kubernetes Ortamında Kaos: Chaos Mesh
Kubernetes kullanıyorsanız, Chaos Mesh prodüksiyon sınıfı bir araç. CNCF projesi, Helm ile kurulumu kolay:
# Chaos Mesh kurulumu
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update
kubectl create ns chaos-testing
helm install chaos-mesh chaos-mesh/chaos-mesh
--namespace=chaos-testing
--set chaosDaemon.runtime=containerd
--set chaosDaemon.socketPath=/run/containerd/containerd.sock
# Kurulumu doğrula
kubectl get pods -n chaos-testing
Pod kaos deneyi tanımı:
# pod-kill-experiment.yaml
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: payment-service-pod-kill
namespace: chaos-testing
spec:
action: pod-kill
mode: one
selector:
namespaces:
- production
labelSelectors:
"app": "payment-service"
scheduler:
cron: "@every 10m"
duration: "30s"
---
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: payment-db-latency
namespace: chaos-testing
spec:
action: delay
mode: all
selector:
namespaces:
- production
labelSelectors:
"app": "payment-service"
delay:
latency: "150ms"
correlation: "25"
jitter: "50ms"
direction: to
externalTargets:
- "postgres-service"
duration: "5m"
# Deneyi uygula
kubectl apply -f pod-kill-experiment.yaml
# Sonuçları izle
kubectl describe podchaos payment-service-pod-kill -n chaos-testing
# Deneyi durdur
kubectl annotate podchaos payment-service-pod-kill
experiment.chaos-mesh.org/pause=true -n chaos-testing
Kaos Deney Döngüsü: Planlama ve Raporlama
İyi bir kaos deneyi rastgele yapılmaz. Her deneyin bir playbook’u olmalı:
#!/bin/bash
# chaos_experiment_runner.sh
EXPERIMENT_NAME="$1"
EXPERIMENT_FILE="$2"
MONITORING_ENDPOINT="http://grafana:3000"
SLACK_WEBHOOK="https://hooks.slack.com/services/XXX/YYY/ZZZ"
notify_slack() {
local message="$1"
curl -s -X POST $SLACK_WEBHOOK
-H 'Content-type: application/json'
--data "{"text":"[KAOS DENEY] $EXPERIMENT_NAME: $message"}"
}
check_steady_state() {
local error_rate
error_rate=$(curl -s "http://prometheus:9090/api/v1/query?query=rate(http_requests_total{status=~'5..'}[1m])/rate(http_requests_total[1m])"
| python3 -c "import sys,json; d=json.load(sys.stdin); print(float(d['data']['result'][0]['value'][1]) if d['data']['result'] else 0)")
if (( $(echo "$error_rate > 0.02" | bc -l) )); then
echo "FAIL"
else
echo "OK"
fi
}
echo "=== Kaos Deneyi Başlıyor: $EXPERIMENT_NAME ==="
notify_slack "Deney başlıyor. Lütfen dashboard'u izleyin: $MONITORING_ENDPOINT"
# Başlangıç durumunu kontrol et
BEFORE_STATE=$(check_steady_state)
echo "Başlangıç durumu: $BEFORE_STATE"
if [ "$BEFORE_STATE" != "OK" ]; then
echo "HATA: Sistem zaten sağlıksız, deney iptal ediliyor!"
notify_slack "Deney iptal edildi - sistem baştan sağlıksız"
exit 1
fi
# Deneyi çalıştır
chaos run $EXPERIMENT_FILE
CHAOS_EXIT=$?
# Sonrası durumu kontrol et
sleep 30
AFTER_STATE=$(check_steady_state)
# Rapor
echo "=== Deney Sonucu ==="
echo "Başlangıç: $BEFORE_STATE"
echo "Bitiş: $AFTER_STATE"
echo "Chaos exit code: $CHAOS_EXIT"
if [ "$AFTER_STATE" != "OK" ]; then
notify_slack "UYARI: Deney sonrası sistem hala sağlıksız! Manuel müdahale gerekebilir."
else
notify_slack "Deney tamamlandı. Sistem toparlandı. Detay: logs/$EXPERIMENT_NAME.log"
fi
Gerçek Dünya Senaryoları
Senaryo 1: DNS çözümleme başarısızlığı. Bir mikroservis mimarisinde servisler birbirini isim ile buluyor. DNS sunucusu bir anlığına yanıt vermezse ne olur? /etc/resolv.conf’u geçici olarak boşaltmak yerine, Chaos Mesh’in DNSChaos özelliği ile belirli domain’lerin çözümlenmesini engelleyebilirsiniz. Sonuç genellikle şaşırtıcıdır: retry mekanizması olmayan servisler anında çöküyor, circuit breaker kurulmamış çağrılar cascade failure yaratıyor.
Senaryo 2: Asimetrik ağ bölünmesi. Gerçek ağ bölünmeleri simetrik olmaz. Bir düğüm diğerine gönderebilir ama cevap alamayabilir. Bu senaryo distributed lock mekanizmalarında özellikle kritik. etcd veya ZooKeeper kullanan sistemlerde bu test yapılmazsa split-brain senaryosu prodüksiyonda sizi bekliyor.
Senaryo 3: Sertifika süresi dolması. mTLS kullanan mikroservislerde sertifikaların ne zaman dolduğunu gözden kaçırmak kolay. Chaos Toolkit ile bir sertifikayı geçersiz kılarak, servislerinizin graceful fallback yapıp yapmadığını test edebilirsiniz.
Game Day Organizasyonu
Kaos mühendisliğinin kurumsal versiyonu olan Game Day, tüm ekibin katıldığı planlı bir tatbikat. Google, Netflix, Amazon gibi şirketler düzenli olarak yapıyor.
Basit bir Game Day yapısı:
- Hazırlık (1 hafta önce): Deneyler belirlenir, hipotezler yazılır, runbook hazırlanır.
- Açılış brifing (gün başı): Herkes rolünü bilir, iletişim kanalları açık.
- Deney fazı (2-4 saat): Senaryolar sırayla uygulanır, gözlemlenir.
- Durdurma kriterleri netleştirilir: “Hata oranı %5’i geçerse deneyi durduruyoruz” gibi.
- Retrospektif (gün sonu): Ne öğrendik? Backlog’a ne eklendi?
Sonuç
Kaos mühendisliği, olgunluğa erişmiş sistemlerin yapması gereken bir pratik, başlangıç aşamasındaki projelerin kafasını karıştıracak bir egzersiz değil. Başlamak için Netflix ölçeğine sahip olmak gerekmiyor. Hatta başlangıç için tc ile bir gecikme ekleyip uygulamanızın nasıl tepki verdiğini gözlemlemek bile değerli bir deney.
Önemli olan zihniyeti oturtmak: sistemler başarısız olur, soru sadece ne zaman olduğudur. Siz önceden hazırlanırsanız, başarısızlık kontrollü bir öğrenme deneyimine dönüşür. Hazırlanmazsanız, gece 3’te bir production incident ile başarısızlığı öğrenirsiniz.
Adım adım gitmek en doğrusu. Önce gözlemlenebilirlik altyapısını sağlamlaştırın, sonra staging ortamında ilk küçük deneyleri yapın, ardından prodüksiyon trafiğinin küçük bir dilimine taşıyın. Her deneyden sonra bulguları belgeleyin ve düzeltici aksiyonları takip edin. Zamanla bu, ekibinizin refleksi haline gelir ve sistemlerinizin gerçek dayanıklılığı konusunda yanıltıcı değil, gerçekçi bir özgüvene sahip olursunuz.
