etcd Watch Replay Búrky: Keď Obrovské ConfigMapy Zabíjajú Control Plane
etcd je tichy, kym sa watch replay nezmeni na burku. “Kubernetes apiserver je náhodne pomalý.” Pridali sme viac CPU, viac pamäte—nič nepomohlo. Príčina: 500KB ConfigMap s rotujúcimi certifikátmi, aktualizovaný každú hodinu, spúšťajúci kaskádovú watch replay búrku naprieč všetkými kontrolérmi.
Prostredie: Kubernetes 1.27, 3-node etcd cluster, 150+ kontrolérov sledujúcich rôzne zdroje
Problém
Symptómy
Čo sme pozorovali:
Normálny deň:
kubectl get pods → 50ms
API latency p99 → 100ms
etcd latency p99 → 10ms
Počas "náhodných spomalení":
kubectl get pods → 5-30 sekúnd
API latency p99 → 10+ sekúnd
etcd latency p99 → 500ms+
Vzor:
- Stáva sa zhruba raz za hodinu
- Trvá 2-5 minút
- Žiadny zjavný trigger v aplikačnej vrstve
- Koreluje s... rotáciou certifikátov?
Prečo Štandardný Monitoring Toto Nezachytí
# CPU a pamäť vyzerajú dobre
kubectl top pods -n kube-system
# etcd: CPU 30%, Memory 40%
# apiserver: CPU 50%, Memory 60%
# etcd health hovorí že všetko je v poriadku
etcdctl endpoint health
# healthy: true
# Problém je skrytý v mechanizme watches
# a viditeľný len v špecifických metrikách
Príčina
Ako Watches Fungujú
Normálny watch flow:
Kontrolér: "Sleduj pody od revízie 12345"
↓
apiserver: Proxuje watch do etcd
↓
etcd: "Tu sú eventy od 12345:
- Pod A vytvorený (rev 12346)
- Pod B zmazaný (rev 12347)
- ..."
↓
Kontrolér: Spracováva eventy jeden po druhom
Čo Sa Stane Pri Compaction
etcd compaction scenár:
Kontrolér: "Sleduj pody od revízie 12345"
↓
etcd: "Prepáč, revízia 12345 bola compactnutá!
Najstaršia dostupná je 50000"
↓
apiserver: Vráti chybu kontroléru
↓
Kontrolér: "Musím RELISTOVAŤ všetko!"
Listuje VŠETKY pody (môžu byť tisíce)
Vytvára nový watch od aktuálnej revízie
Teraz si predstav že 150 kontrolérov narazí na toto naraz...
Problém Obrovského Objektu
Kaskáda:
1. Obrovský ConfigMap (500KB) aktualizovaný
└─▶ Generuje veľkú etcd transakciu
2. Transakcia rýchlo zvyšuje revíziu
└─▶ Staršie revízie sa compactujú rýchlejšie
3. Pomalý watcher zaostáva
└─▶ Jeho sledovaná revízia sa compactuje
└─▶ Kontrolér nútený relistovať
4. Relist tisícov objektov
└─▶ Pridáva záťaž na apiserver
└─▶ Ďalší watcheri zaostávajú
└─▶ Viac compactions
└─▶ Viac relistov
└─▶ BÚRKA!
┌─────────────────────────────────────────────────┐
│ Búrka │
│ │
│ ConfigMap Veľký Rýchla │
│ update → nárast → compaction │
│ revízie │
│ ↓ │
│ Watcher 1 zaostáva → Relist 10000 objektov │
│ Watcher 2 zaostáva → Relist 5000 objektov │
│ Watcher N zaostáva → Relist ... │
│ ↓ │
│ API Server preťažený │
│ Viac watcherov zaostáva │
│ Opakuj... │
└─────────────────────────────────────────────────┘
Diagnostika
Krok 1: Nájdi Veľké Objekty
# Skontroluj veľkosti objektov v etcd
ETCDCTL_API=3 etcdctl get "" --prefix --keys-only | while read key; do
size=$(etcdctl get "$key" --print-value-only 2>/dev/null | wc -c)
echo "$size $key"
done | sort -rn | head -20
# Alebo cez kubectl
kubectl get configmaps -A -o json | \
jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name) \(.data | to_entries | map(.value | length) | add)"' | \
sort -t' ' -k2 -rn | head -20
Krok 2: Skontroluj Compaction Rate
# etcd compaction metriky
curl -s http://localhost:2379/metrics | grep etcd_mvcc_db_compaction
# Vysoká compaction_pause_duration = problém
# etcd_mvcc_db_compaction_pause_duration_milliseconds_bucket
# Skontroluj aktuálnu revíziu vs compactnutú revíziu
etcdctl endpoint status --write-out=table
# Pozri na RAFT INDEX a porovnaj s predchádzajúcim
Krok 3: Watch Restart Metriky
# Skontroluj ako často sa watches reštartujú
curl -s http://localhost:8080/metrics | grep watch
# Hľadaj:
# apiserver_watch_events_total
# apiserver_watch_events_sizes
# apiserver_longrunning_requests (pre watch counts)
# Vysoká miera watch establishment = problém
Krok 4: Nájdi Trigger
# Skontroluj čo sa aktualizuje často
kubectl get events -A --sort-by='.lastTimestamp' | tail -50
# Skontroluj frekvenciu aktualizácie veľkých ConfigMaps
kubectl get configmap <podozriva-cm> -n <ns> -o json | \
jq '.metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"]'
Riešenie
Možnosť 1: Zmenši Veľké Objekty
# Pred: Obrovský ConfigMap so všetkými certifikátmi
apiVersion: v1
kind: ConfigMap
metadata:
name: all-certificates
data:
ca-bundle.crt: |
-----BEGIN CERTIFICATE-----
... 500KB certifikátov ...
-----END CERTIFICATE-----
# Po: Rozdeľ na viacero menších ConfigMaps
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ca-cert-1
data:
ca.crt: |
... 50KB ...
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ca-cert-2
data:
ca.crt: |
... 50KB ...
Možnosť 2: Zníž Frekvenciu Aktualizácie
# Ak rotujete certifikáty každú hodinu, zváž:
# 1. Rotuj menej často (každých 6 hodín)
# 2. Aktualizuj len keď sa obsah skutočne mení
# Skontroluj či aktualizácie skutočne menia obsah
kubectl get configmap cert-bundle -o jsonpath='{.data}' | md5sum
# Spusti znova po "aktualizácii" - ak rovnaký hash, update je zbytočný
Možnosť 3: Presuň Veľké Dáta Von z etcd
# Použi externé úložisko pre veľké dáta
apiVersion: v1
kind: Secret
metadata:
name: ca-bundle-reference
type: Opaque
stringData:
# Ulož URL alebo referenciu, nie skutočné dáta
bundle-url: "https://internal-ca-server/bundle.pem"
bundle-hash: "sha256:abc123..."
# Aplikácia sťahuje z URL, validuje hashom
Možnosť 4: Vylaď etcd Compaction
# etcd konfigurácia
# Zvýš compaction retention aby mali watcheri viac času
auto-compaction-retention: "8" # hodín (default je 1)
auto-compaction-mode: "revision" # alebo "periodic"
# Ale opatrne: viac retention = viac disk space
# Možno bude treba zvýšiť quota-backend-bytes
quota-backend-bytes: 8589934592 # 8GB
Možnosť 5: Použi Delta Updates
// Namiesto nahradenia celého ConfigMap
// Použi strategic merge patch na update len zmenených polí
patch := []byte(`{"data":{"new-cert.pem":"..."}}`)
_, err := clientset.CoreV1().ConfigMaps(namespace).Patch(
context.TODO(),
"ca-bundle",
types.StrategicMergePatchType,
patch,
metav1.PatchOptions{},
)
// Menšia transakcia = menší dopad na watcherov
Monitoring
Kľúčové Metriky
groups:
- name: etcd-watch
rules:
- alert: EtcdHighCompactionRate
expr: |
rate(etcd_mvcc_db_compaction_total_duration_milliseconds_count[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "Vysoká etcd compaction rate"
- alert: WatchCacheHitRate
expr: |
sum(rate(apiserver_cache_list_fetched_objects_total[5m])) /
sum(rate(apiserver_cache_list_total[5m])) < 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "Nízka watch cache hit rate - možné relist búrky"
- alert: LargeEtcdObject
expr: |
etcd_object_size_bytes > 100000
for: 5m
labels:
severity: info
annotations:
summary: "Veľký objekt v etcd: {{ $labels.key }}"
Checklist
## etcd Watch Replay Búrky
### Symptómy
- [ ] Náhodné spomalenia apiservera
- [ ] Vysoká list request rate
- [ ] Vzor koreluje s config updates
- [ ] Trvá 2-5 minút, potom sa zotaví
### Diagnostika
- [ ] Nájdi veľké objekty v etcd (>100KB)
- [ ] Skontroluj frekvenciu aktualizácie objektov
- [ ] Monitoruj etcd compaction rate
- [ ] Skontroluj watch restart metriky
### Riešenia
- [ ] Rozdeľ veľké ConfigMapy/Secrets
- [ ] Zníž frekvenciu aktualizácie
- [ ] Použi delta updates namiesto full replace
- [ ] Presuň veľké dáta von z etcd
- [ ] Vylaď compaction retention
### Prevencia
- [ ] Nastav object size limity v admission control
- [ ] Monitoruj veľké objekty proaktívne
- [ ] Použi externé úložisko pre veľké dáta
Záver
Tento failure mode je záludný pretože:
- Symptóm je “náhodná pomalosť” - žiadna zjavná príčina
- Štandardné zdroje vyzerajú dobre - CPU/pamäť nie sú problém
- Root cause je interakcia - medzi veľkosťou objektu, frekvenciou update a watch mechanikou
- Fix vyžaduje pochopenie etcd internals - nie len Kubernetes
Kľúčový insight: etcd nie je len key-value store—je to verzovaná, sledovaná databáza. Veľké, často aktualizované objekty môžu destabilizovať celú watch mašinériu.
Súvisiace články
- Kubernetes ConfigMap Anti-Patterns - Viac ConfigMap gotchas
- Control Plane Performance - Optimalizácia control plane
Súvisiace články
etcd Quota Alarm: Keď Váš Kubernetes Cluster Prejde do Read-Only
Cluster prestane prijímať zápisy, pody sa nedajú naplánovať. Príčina: etcd dosiahol storage quota lebo compaction nebežal, história sa nahromadila nad limity.
kube-proxy Mikro-Výpadky: Problém xtables Lock Contencie
Náhodné 1-3 sekundové výpadky spojení počas deploymentov. CPU vyzerá v poriadku, pamäť stabilná. Skrytá príčina: iptables-restore drží xtables lock počas endpoint churnu.
Go cgo DNS Resolution Thread Explózia: Keď net.LookupHost Spawne Tisíce Threadov
Go aplikácia má zrazu 10,000 threadov konzumujúcich všetku pamäť. Príčina: cgo-based DNS resolution blokujúce v pomalých DNS prostrediach, obchádzajúce Go's goroutine scheduler.
Java Profilovanie v Hardened Kubernetes: Keď Security Blokuje Tvoj Debugger
Nemôžeš pripojiť profiler k produkčnej JVM. seccomp blokuje perf_event_open, container dropol CAP_SYS_PTRACE a PodSecurityPolicy bráni privileged mode. Tu je ako profilovať aj tak.
Citujte tento článok
Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.