Skip to content
All projects

OpenTelemetry & LGTM Platform

Cloud-native observability stack using OpenTelemetry Collectors in agent and gateway patterns, feeding the LGTM stack for unified metrics, logs and distributed tracing.

Source code
  • OpenTelemetry
  • LGTM Stack
  • Prometheus
  • Grafana
  • Loki
  • Tempo

The problem

Telemetry ends up coupled to whichever vendor was chosen first — instrumentation is rewritten every time the backend changes, and metrics, logs and traces stay in three unconnected tools.

Constraints

Architecture

Source

Microservices on EKS
OTel instrumentation

Collect

OTel Collector
Receivers
Processors / Exporters

Store

Prometheus — metrics
Loki — logs
Tempo — traces
Grafana — unified query and alerting

Interactive Pipeline Simulator

Ingest & Tail-Sampling Flow

1. Node DaemonSet

Microservices on EKSOTLP / gRPC Egress
Node Metadata Enrichmentpod_name, namespace, host

2. Collector Gateway

Tail Sampling: 5% UniformShedding 95% of healthy 200 OK spans to reduce storage cost
Batching & Retries5s / 1024 spans queue buffer

3. LGTM Destination

PrometheusMetrics
LokiLogs
TempoTraces (5%)

Key decisions

What was chosen, what it was chosen over, and why.

Also decided

  • A dedicated observability clusternotCo-locating the backend with the workloads

    The platform team owns routing, sampling, dashboards and cost controls on isolated node groups, so a workload cluster incident does not take down the tooling you need to debug it.

  • Specialised backends behind one GrafananotA single general-purpose store

    Metrics, logs and traces have genuinely different retention and query shapes. Grafana unifies them at the point of use, so the split costs nothing where it would be felt.

Production resilience & failure modes

Failure mode this architecture has to account for

A traffic spike upstream sends the gateway's tail-sampling buffer (`num_traces: 10000`, 10s decision wait) more concurrent traces than it can hold in memory before deciding what to keep.

How it's handled

The `filter/budgeting` and `tail_sampling` processors run before export, not after — a misbehaving tenant gets dropped at the gateway instead of paying to ship its spans to Tempo first. The five sampling policies (errors, 5xx, slow requests, then 5% of everything healthy) are evaluated in that order, so the traces worth keeping are decided before the traces worth dropping ever get sized for export.

Source: otel-gateway-tail-sampling.yaml

Deep dive writing