kira-otel-collector 是 Kira 所有服务统一的 tail-sampling OTLP 网关。每个服务把 100% OTLP(traces / metrics / logs)经 Fly 6PN 内网发到它,由它决定保留什么,再导出到 Dash0。
观测平台只有 Dash0。 没有 BetterStack / Sentry / NATS。Dash0 的 auth token 只存在这台 collector 上(DASH0_AUTH_TOKEN fly secret),各服务在 6PN 内网无需鉴权即可发到 collector。
为什么要 collector
Dash0 按发送的 span 计费,且没有 ingest 端采样(它的 “Adaptive Sampling” 只在查询期生效)。所以「保留重要的、丢弃普通的」必须发生在到 Dash0 之前 —— 这就是这台 collector 做的 tail sampling。
采样策略
| 信号 | 策略 |
|---|
| traces | tail-sampled:保留 100% error trace + 100% slow(>1s)trace + 10% baseline |
| metrics | 100% 透传(聚合量,永不采样) |
| logs | 100% 透传(error 走 ERROR log,永不采样) |
tail sampling 缓冲整条 trace decision_wait 时长后再裁决;各策略是 OR 关系 —— 任一策略投票保留即保留。
tail_sampling:
decision_wait: 10s
num_traces: 50000
expected_new_traces_per_sec: 1000
policies:
- name: errors # 100% 含 ERROR span 的 trace
type: status_code
status_code: { status_codes: [ERROR] }
- name: slow # 100% >1s 的 trace
type: latency
latency: { threshold_ms: 1000 }
- name: baseline # 其余 10%
type: probabilistic
probabilistic: { sampling_percentage: 10 }
部署形态
| 项 | 值 |
|---|
| 镜像 | otel/opentelemetry-collector-contrib:0.152.1 |
| 平台 | Fly.io(sjc,internal-only,单实例) |
| 资源 | 2 CPU / 2 GB RAM |
| 配置 | otel-config.yaml,baked 进镜像 |
| 入站 OTLP | gRPC [::]:4317 / HTTP [::]:4318(IPv6,Fly 6PN 是 IPv6) |
| 健康检查 | TCP :13133(health_check extension) |
无 [http_service]:不暴露公网,仅经 6PN 以 kira-otel-collector.internal:4318(OTLP/HTTP)、:4317(OTLP/gRPC)被同 org 内网服务访问。
服务如何接入
把每个服务的 OTLP endpoint 指向 collector(而非 Dash0),各服务保持 AlwaysOnSampler(发 100%),由 collector 决定保留:
OTEL_EXPORTER_OTLP_ENDPOINT = http://kira-otel-collector.internal:4318
这一行在每个服务的 fly.toml [env] 里(kira-be / kira-agent / kira-cdn / kira-video-worker / kira-queue / kira-sg-billing / kira-notify / kira-auth …)。
Black-box 基础设施(Prometheus scrape)
Centrifugo 和 imgproxy 不主动推 OTLP —— collector 从它们的原生 Prometheus /metrics 端点拉取,无需 per-service sidecar:
| receiver | target | 说明 |
|---|
prometheus/centrifugo | kira-centrifugo.internal:8000/metrics | config.json prometheus.enabled = true |
prometheus/imgproxy | kira-imgproxy.internal:8081/metrics | IMGPROXY_PROMETHEUS_BIND=":8081",独占 RED 三件套 |
prometheus/collector | 127.0.0.1:8888/metrics | collector 自身 otelcol_* 内部指标(loopback only) |
拉取后用 resource/* processor 给指标打上 service.name / service.namespace=kira / deployment.environment.name=production,让它们在 Dash0 里归到正确服务下。
Pipeline
processors: memory_limiter → tail_sampling → batch (traces)
processors: memory_limiter → batch (metrics / logs)
exporter: otlphttp/dash0 (ingress.europe-west4.gcp.dash0.com, Dash0-Dataset: default)
memory_limiter(80% / spike 25%)必须最先跑,OOM 前先丢遥测。batch(8192 / 5s)聚批后导出。
环境变量(Fly secrets)
DASH0_AUTH_TOKEN # Dash0 ingest bearer token —— 全平台仅此一份
fly.toml 不含任何 endpoint secret:Dash0 endpoint / dataset 直接写死在 otel-config.yaml,token 走 ${env:DASH0_AUTH_TOKEN}。
Caveat
- tail sampling 需要整条 trace 落到同一个 collector 实例。单实例简单但是 SPOF。要 HA 得做两层:
loadbalancing exporter 按 traceID 路由 → 一池 tail-sampling collector。
decision_wait(10s)导致 trace 在 Dash0 里有约 10s 延迟(它要缓冲整条 trace 才能裁决)。
tail_sampling 在内存里缓冲在途 trace(num_traces=50000);量级增长时调大 VM 内存。