Architecture Overview
Components
| Component | Role | Port | Access |
|---|---|---|---|
| Grafana | Unified UI and dashboarding | 30300 | http://localhost:30300 (admin/admin) |
| Prometheus | Metrics storage and alerting | 30090 | http://localhost:30090 |
| Alertmanager | Alert routing and deduplication | 30093 | http://localhost:30093 |
| Loki | Log aggregation and querying | 3100 | Internal only |
| Tempo | Distributed trace storage | 3200 | Internal only |
| OTel Collector | Telemetry pipeline and routing | 4317/4318 | Internal only |
| Garage | S3-compatible object storage | 3900 | Internal only |
OpenTelemetry Collector
The platform uses a custom-built OpenTelemetry Collector defined inflake.nix:
Configuration
The collector is deployed with this pipeline configuration:Pipelines
Traces Pipeline:- Receives traces via OTLP gRPC/HTTP
- Batches spans for efficiency
- Exports to Tempo via OTLP gRPC
- Receives metrics via OTLP gRPC/HTTP
- Batches datapoints
- Converts to Prometheus format and writes via remote write API
- Receives logs via OTLP gRPC/HTTP
- Batches log records
- Exports to Loki via OTLP HTTP
Why Custom Build?
Benefits:- Minimal size: Only 3 receivers, 1 processor, 3 exporters (vs. 100+ in full distribution)
- Security: No unnecessary components that could have vulnerabilities
- Performance: Smaller binary, faster startup, lower memory usage
- Reproducibility: Exact versions pinned in flake.lock
Prometheus
Prometheus is deployed via the kube-prometheus-stack Helm chart.Configuration
Key Features
Remote Write Receiver:- Accepts metrics from OTel Collector via Prometheus remote write protocol
- Converts OTLP metrics to Prometheus format
- No need for separate Prometheus exporters
- Automatically discovers ServiceMonitors and PodMonitors in any namespace
- No manual scrape config required
serviceMonitorSelectorNilUsesHelmValues = falseenables cluster-wide discovery
- 5 GB persistent volume for local storage
- 24-hour retention (configurable)
- Uses TSDB format for efficient time-series storage
Metrics Available
Kubernetes Metrics:- Node metrics (CPU, memory, disk, network) from node-exporter
- Pod metrics (resource usage, restarts, status) from kubelet
- Container metrics (CPU, memory limits/requests) from cAdvisor
- Cluster state (deployments, services, endpoints) from kube-state-metrics
- Request rate, duration, size by service
- Error rates and HTTP status codes
- TCP connection metrics
- mTLS usage statistics
- Custom business metrics
- HTTP server metrics
- Database client metrics
- Queue depth and processing time
Loki
Loki provides scalable log aggregation with S3 backend storage.Configuration
Storage Architecture
TSDB + S3:- Index: TSDB (Time Series Database) for fast label queries
- Chunks: Compressed log data in S3-compatible storage (Garage)
- Schema v13: Latest Loki schema with improved query performance
- Unlimited retention (limited only by S3 capacity)
- Cost-effective storage (object storage is cheap)
- Separation of index and chunks for scalability
- Fast queries on recent data, slower but complete on historical data
OTLP Ingestion
Loki accepts logs via OTLP HTTP endpoint:- OTLP log records → Loki log lines
- OTLP attributes → Loki labels
- Timestamp preserved
- Resource attributes become static labels
Query Language (LogQL)
Loki uses LogQL for querying:Tempo
Tempo stores distributed traces with S3 backend.Configuration
Trace Storage
S3 Backend:- Traces stored as compressed Parquet files in Garage S3
- Efficient columnar format for fast queries
- Unlimited retention with low storage costs
- Recent traces buffered in local disk
- Fast writes with eventual consistency
- Flushed to S3 periodically
Metrics Generator
Tempo can derive metrics from traces:traces_spanmetrics_calls_total: Request count by servicetraces_spanmetrics_latency_bucket: Latency histogramstraces_spanmetrics_size_total: Request/response size
- RED metrics (Rate, Errors, Duration) automatically from traces
- No need to instrument both tracing and metrics
- Consistent labels between traces and metrics
- Exemplars link metrics back to example traces
Istio Integration
Istio sends traces to OTel Collector, which forwards to Tempo:scripts/istio-install.sh:
istio/telemetry.yaml:
Grafana
Grafana provides the unified query interface for all observability data.Data Sources
All data sources are pre-configured innixidy/env/local/kube-prometheus-stack.nix:
Correlation Features
Trace → Logs:- Click “Logs” button on a span
- Automatically queries Loki with trace ID filter
- Time range adjusted to span duration ±1 hour
- Shows logs from services involved in the trace
- Click “Metrics” button on a span
- Queries Prometheus for service metrics in the trace
- Shows request rate, latency, errors for the service
- Exemplars allow jumping from metric back to trace
- Loki results include “Tempo” button when trace ID detected
- Automatically extracts trace ID from log fields
- Opens full trace view in Tempo
- Prometheus queries return exemplars (example trace IDs)
- Click exemplar point on graph to view trace
- Links specific metric spike to actual request
Dashboards
The platform includes pre-built dashboards indashboards/:
Garage S3 Storage
Garage provides S3-compatible object storage for Loki and Tempo.Why Garage?
- Lightweight: Designed for self-hosted deployments
- S3 compatible: Works with any S3-compatible client
- Kubernetes-native: Runs as a StatefulSet
- No external dependencies: No need for MinIO, AWS S3, etc.
Setup
Garage is bootstrapped viascripts/garage-setup.sh:
Data Flow Examples
Application Request Trace
Log Aggregation Flow
Performance Tuning
OTel Collector Batching
- Larger batches → Higher throughput, more latency
- Smaller batches → Lower latency, more overhead
Prometheus Retention
- Dev: 24h retention, 5 GB storage
- Staging: 7d retention, 20 GB storage
- Production: 30d retention, 100+ GB storage (or use Thanos for long-term storage)
Loki Query Performance
Optimize queries:- Always include namespace label
- Limit time range to minimum needed
- Use streaming for large result sets
- Pre-filter with label matchers before line filters
Troubleshooting
OTel Collector Not Receiving Data
Prometheus Remote Write Errors
Loki/Tempo S3 Connection Issues
Next Steps
GitOps
Learn how observability configs are managed via ArgoCD and Nixidy
Kubernetes Setup
Understand the underlying cluster architecture