OpenTelemetry Collector backpressure: dropy, memory_limiter a queue ako guardrails
Failure mode vyzerá ako “tracing je flaky”:
- developeri tvrdia, že spany posielajú
- backend (Tempo/Jaeger/OTLP endpoint) vyzerá zdravo
- a napriek tomu traces miznú presne pri peaku
- collector pody restartujú (OOMKilled) alebo ticho dropujú dáta
Mne sa to stalo presne vtedy, keď sme trace potrebovali najviac — počas incidentu.
Pointa je jednoduchá: telemetria musí byť best-effort a collector má byť nakonfigurovaný tak, aby vedel úmyselne zhadzovať dáta namiesto toho, aby umrel pod backpressure.
Testované na: OpenTelemetry Collector (contrib) 0.9x–0.10x, Kubernetes 1.29–1.31, OTLP cez gRPC, exportéry s retry + queue.
Incident (anonymizovaný)
Bežíme node-local collector ako DaemonSet (OTLP receiver), export do centrálneho gateway a potom do trace backendu. Pri traffic spiku:
- ingest na backende sa spomalil (vyššia export latencia)
- collector začal bufferovať viac
- pamäť rástla až do OOMKill
- po restarte sa to opakovalo
- traces chýbali presne v intervale, ktorý sme debugovali
Blast radius: observability degradovalo pre celý cluster; metriky/logy ostali, ale trace-driven debugging bol slepý.
Constraint: v danom momente sme nevedeli “len doskalovať backend”. Potrebovali sme, aby collector ostal stabilný aj pri čiastočnej pomalosti backendu.
Timeline
- T-0: on-call chce traces; coverage zrazu padne.
- T+10m: collector pody majú rast pamäte; niektoré dostanú OOMKilled.
- T+20m: v logoch je “dropping data” / “queue full”.
- T+30m: potvrdíme exporter backpressure (slow sends, retry).
- T+45m: mitigácia: zapnúť
memory_limiter, doladiť queue, upraviť batch. - T+90m: pamäť sa stabilizuje; drop rate je bounded a viditeľný, nie katastrofický.
Mechanizmus: prečo collectory padajú (alebo ticho dropujú)
Backpressure je normálny — vy sa rozhodujete, ako prídete o dáta
Pipeline je v princípe:
receivers → processors → exporters
Keď exportéry spomalia:
- queue sa plní
- retry sa rozbehne
- pamäť rastie (bufferované spany/logy/metriky)
- nakoniec buď:
- OOMKill (najhoršie; často stratíte ešte viac)
- alebo controlled drop (lepšie)
Tail sampling zhoršuje pamäťový tlak
Tail sampling musí držať dáta, kým sa rozhodne. Kombinácia tail sampling + exporter backpressure + bez memory limiteru vie spraviť pamäťový cliff veľmi rýchlo.
“Nemáme chyby” ≠ “nemáme dropy”
Veľa tímov sleduje iba ingest na backende. Dropy ale môžu vzniknúť:
- v exporter queue
- v processoroch (memory limiter)
- v exporter retry logike
Ak negrafujete “collector o collectore”, ste slepí.
Runbook: dokážte backpressure a dropy
Čo skontrolovať ako prvé
- Restarty a pamäť collectora
kubectl -n observability get pods -o wide
kubectl -n observability describe pod <otelcol-pod> | grep -n "OOMKilled\\|Restart" -n
kubectl -n observability top pod <otelcol-pod>
- Logy collectora v čase incidentu
kubectl -n observability logs <otelcol-pod> --since=30m | tail -n 200
Hľadám:
- “dropping”
- “queue is full”
- “exporting failed”
- “retrying”
- Pozrieť reálne metriky na
/metrics(a nehádať názvy) Metriky sa medzi verziami menia; ja vždy najprv grepnem, čo collector vystavuje.
kubectl -n observability port-forward pod/<otelcol-pod> 8888:8888
curl -s http://127.0.0.1:8888/metrics | grep -E "^otelcol_" | head -n 50
curl -s http://127.0.0.1:8888/metrics | grep -E "exporter|queue|dropped|refused|failed" | head -n 50
Ako potvrdiť hypotézu
Chcete konzistentný príbeh:
- export latencia rastie
- queue utilization ide k limitu
- accepted spany sú vysoké, ale “sent” zaostáva
- “failed/dropped/refused” rastie
- pamäť rastie (bez limiteru) alebo drop rastie (s limiterom)
Ak máte Prometheus, typicky grafujem:
- accepted vs sent (by pipeline/exporter)
- queue size/capacity
- dropped/refused countery
- RSS collectora
Použite presné názvy metrík, ktoré vidíte v /metrics.
Bezpečné mitigácie
-
Zapnúť
memory_limitera dať ho skoro do pipeline Z “collector umrie” spravíte “collector dropuje kontrolovane”. -
Zapnúť sending queue na exportéri Aby krátke špičky absorboval bez retry búrky.
-
Doladiť
batchPríliš veľký batch vie spraviť latency spike aj memory spike. Príliš malý zas overhead. -
Dočasne znížiť load
- znížiť sampling pre low-value telemetry
- znížiť cardinalitu atribútov
- vypnúť debug “log-to-span” mosty
Rizikové mitigácie
- “Zväčšíme queue a bude to OK”
- len zväčšíte blast radius, keď backend bude pomalý dlhšie než queue horizon
- “Vypneme retries”
- môžete zvýšiť drop (niekedy treba, ale vedome)
- “Dáme file_storage všade”
- viete presunúť incident z RAM na disk (a potom na evictions)
Čo sme zmenili (konkrétne)
Po zmene: memory limiter + batch tuning + exporter queue.
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 75
spike_limit_percentage: 15
batch:
timeout: 1s
send_batch_size: 2048
send_batch_max_size: 4096
exporters:
otlp:
endpoint: otel-gateway.observability.svc.cluster.local:4317
sending_queue:
enabled: true
num_consumers: 4
queue_size: 10000
retry_on_failure:
enabled: true
initial_interval: 200ms
max_interval: 5s
max_elapsed_time: 30s
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp]
Dôležitá vec: memory_limiter musí byť pred batch (a aj pred tail samplingom, ak ho používate).
Ako verifikovať (merateľné)
- Pamäť je stabilná
- RSS je flat aj pri peaku
- žiadne OOMKills
- Dropy sú bounded a viditeľné
- pri extrémnej pomalosti backendu môžete dropovať, ale kontrolovane
- Queue funguje ako tlmič
- krátke špičky → queue sa naplní → potom sa vyprázdni
- nemá byť pegged na 100% dlhé minúty
- Backend sa zotaví bez retry búrky
- po zlepšení latency klesne retry rate
Prevencia / guardrails
- Telemetry loss budget
- akceptovateľná drop rate (napr. ≤ 0.1% v steady state; vyššia počas incidentu)
- Collector SLO
- bez restartov pri peaku
- bounded memory pod backpressure
- Sampling kontrakt
- tail sampling vyžaduje sizing a pamäťový budget
- Dashboardy a alerty
- dropped/refused countery
- queue utilization
- exporter latency a retry rate
Súvisiace čítanie
- Tail-based sampling v OpenTelemetry: Sizing, pamäťové pády a cost model
- Span Contracts: Trace-driven API contract testing z OpenTelemetry
- Prometheus remote_write backpressure: keď monitoring zaplní disk a ešte aj stratí dáta
- Prometheus Kardinalita Explózia: Detekcia, Prevencia a Obnova
- Dash Contracts v Go: CI kompilator pre Grafana dashboardy a Prometheus alerty
- Structured Logging Performance: Keď Sa Logger Stane Bottleneckom
- Adaptive Concurrency Limits: Prestaňte Hádať Veľkosti Thread Poolov
Súvisiace články
Tail-based sampling v OpenTelemetry: Sizing, pamäťové pády a cost model
Praktický sizing guide pre tail sampling v OpenTelemetry Collector. Od decision_wait cez memory limity až po cost-benefit analýzu.
RSS Contracts: Ako prestat zabijat Java pody v Kubernetes (OOMKilled) testovanim RSS ako API
Cgroup RSS budgety, CI sampling a runtime headroom ti chytia JVM memory regresie skor, nez trafia produkciu.
Pod zaseknutý v Terminating: produkčný rozhodovací strom pre finalizery, volume a mŕtve nody
Konzervatívny runbook na bezpečné odblokovanie Terminating Podov: finalizery, CSI/volume cleanup, mŕtve nody a kedy (a ako) použiť force delete.
Kubernetes graceful shutdown ako kontrakt: nula 502 počas rolloutov (HTTP + gRPC)
Reprodukovateľný postup ako odstrániť 502/ECONNRESET pri rolloute: readiness-driven draining, preStop, SIGTERM a merateľný drain budget.
Citujte tento článok
Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.