5.3.1 · блок 5
Distributed tracing и OpenTelemetry
Distributed tracing и OpenTelemetry
Зачем это нужно
Inference request проходит: Ingress → Istio → churn-serving → Feature API → (cache) → model forward. Latency выросла на 200 ms. Metrics показывают p95; trace показывает, какой hop съел время. Без tracing вы оптимизируете model, а виноват сетевой вызов фич.
OpenTelemetry (OTel) — открытый стандарт traces (и metrics/logs). В курсе — propagation через Istio и instrumentation в Python-сервисе.
Основные идеи
Trace, span, context.
- Trace — один end-to-end request (trace_id).
- Span — один участок работы (HTTP call,
model.predict). - Parent-child spans формируют дерево.
W3C Trace Context. Header traceparent передаётся между сервисами. Istio sidecar генерирует/продолжает trace.
OpenTelemetry components.
| Piece | Role |
|-------|------|
| **OTel SDK** | Instrument app, create spans |
| **OTel Collector** | Receive, process, export to backend |
| **Backend** | Jaeger, Tempo, Zipkin |
Auto vs manual instrumentation.
- Auto: some frameworks HTTP clients.
- Manual:
@tracer.start_as_current_span("predict")around model inference — видеть GPU forward отдельно от HTTP handler.
Sampling. 100% traces дорого. Head-based sampling 1–10% in prod; always sample errors (tail sampling in Collector — advanced).
Istio + tracing. Mesh config enables tracing provider; spans for ingress and outbound calls without app changes — good baseline.
ML-specific spans.
validate_inputfetch_featurespreprocessmodel_inferencepostprocess
Compare p95 model_inference vs fetch_features — guides optimization.
Correlation.
trace_idin structured logs (5.2.1).- Grafana: jump from trace to logs with same trace_id.
Tempo vs Jaeger. Grafana Tempo integrates natively with Loki/Prometheus; Jaeger classic UI. Both accept OTLP export.
Как это выглядит на практике
Python (conceptual):
from opentelemetry import trace
tracer = trace.get_tracer("churn-serving")
@app.post("/predict")
async def predict(data: Features):
with tracer.start_as_current_span("predict") as span:
with tracer.start_as_current_span("fetch_features"):
feats = await feature_client.get(data.user_id)
with tracer.start_as_current_span("model_inference"):
score = model.predict(feats)
span.set_attribute("model.version", MODEL_VERSION)
return {"score": score}
Incident: p95 800 ms, GPU idle.
1. Open Grafana Tempo → filter service.name=churn-serving.
2. Trace waterfall: fetch_features 750 ms.
3. Drill Feature API traces — DB query slow.
Fix index upstream; inference team not retraining model.
Istio telemetry: enable tracing sample 5%; compare app span vs sidecar span for overhead audit.
Что сделать после занятия
- [ ] Нарисуйте span tree для
/predictс минимум 4 spans. - [ ] Объясните, зачем
trace_idв JSON logs. - [ ] Прочитайте OTel docs: что такое OTLP exporter.