writing-skills
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
tempoDeploys and manages Tempo distributed tracing infrastructure on Kubernetes with configuration, querying via Loki-style APIs, health monitoring, and troubleshooting for production microservice observability.
Independent GitHub snapshot · Automated classification · Original documentation governs
name: tempo description: Deploys and manages Tempo distributed tracing infrastructure on Kubernetes with configuration, querying via Loki-style APIs, health monitoring, and troubleshooting for production microservice observability. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: cncf triggers: tempo, distributed tracing, Grafana Tempo, trace query, span debugging, OTLP ingestion, Loki storage, APM infrastructure role: reference scope: infrastructure output-format: manifests content-types: [guidance, examples, config, do-dont] archetypes: [tactical, orchestration] anti_triggers: [brainstorming, vague ideation, long-form architecture planning] response_profile: verbosity: medium directive_strength: high abstraction_level: operational
Deploys and manages Grafana Tempo distributed tracing infrastructure on Kubernetes. Covers namespace setup, OTLP ingestion configuration, storage backend selection, trace querying via QueryFrontend API, health monitoring, and troubleshooting common issues.
Tempo is a CNCF project by Grafana Labs — a highly scalable, cost-efficient distributed tracing backend designed to work natively with Grafana. Unlike Jaeger (which stores full traces), Tempo uses log-based indexing similar to Loki, dramatically reducing storage costs while maintaining fast query performance.
Use Tempo when:
Alternatives: Use Jaeger when you need full-trace storage with complex query capabilities. Use commercial APM tools (Datadog, New Relic) when you want zero infrastructure management.
Tempo follows a three-component architecture:
Data flow: Application → OTLP Receiver → Ingester (memory buffer) → Storage (S3/GCS) ← QueryFrontend ← TraceQL API
# Add the Grafana Helm repository
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
# Install Tempo with S3 storage backend
helm install tempo grafana/tempo -n tempo --create-namespace \
--set deploymentMode=distributed \
--set tempoServiceAccount.create=true \
--set "tempo.main.server.grpcStorageCompression.enabled=true" \
--set "tempo.main.storage.s3.bucket=tempo-traces" \
--set "tempo.main.storage.s3.region=us-east-1" \
--set "tempo.main.storage.s3.secret.access.key=${AWS_SECRET_KEY}" \
--set "tempo.main.storage.s3.access.key.id=${AWS_ACCESS_KEY}"
# Verify all distributed components are running
kubectl get pods -n tempo
---
# Tempo Namespace
apiVersion: v1
kind: Namespace
metadata:
name: tempo
labels:
app.kubernetes.io/name: tempo
---
# Tempo QueryFrontend Service (HTTP API for trace queries)
apiVersion: v1
kind: Service
metadata:
name: tempo-query
namespace: tempo
labels:
app.kubernetes.io/name: tempo
spec:
ports:
- port: 3100
targetPort: 3100
name: http-metrics
- port: 9095
targetPort: 9095
name: grpc-query
selector:
app.kubernetes.io/component: query-frontend
---
# Tempo Distributor Service (receives traces)
apiVersion: v1
kind: Service
metadata:
name: tempo-distributor
namespace: tempo
labels:
app.kubernetes.io/name: tempo
spec:
ports:
- port: 4317
targetPort: 4317
name: otlp-grpc
- port: 4318
targetPort: 4318
name: otlp-http
- port: 9411
targetPort: 9411
name: zipkin
selector:
app.kubernetes.io/component: distributor
max_block_bytes and tune ingester_lifecycler flush thresholds. Monitor tempo_ingester_ring_tokens_count.tempo_distributor_queue_capacity and scale ingester replicas.# Create namespace for Tempo deployment
kubectl create namespace tempo --dry-run=client -o yaml | kubectl apply -f -
# Create ConfigMap for custom distributor settings
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: tempo-distributor-config
namespace: tempo
data:
config.yaml: |
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
http:
endpoint: "0.0.0.0:4318"
max_traces_per_user: 1000
batch_size: 500
EOF
# List all services traced by Tempo
curl -s http://localhost:3100/services | jq .
# Get traces using TraceQL — query by service name and operation
curl -s -X POST "http://localhost:3100/otlp/v1/traces" \
-H "Content-Type: application/x-protobuf" \
--data-binary @trace.pb # Pre-recorded trace binary
# Query traces with TraceQL via the Loki-compatible query endpoint
SERVICE="orders-service"
curl -s "http://localhost:3100/api/search?query={service.name=\"${SERVICE}\"}&limit=5" \
| jq .
# Port-forward to Tempo QueryFrontend for local access
kubectl port-forward -n tempo svc/tempo-query 3100:3100 &
open http://localhost:3100
# Fetch trace data by ID (requires the trace ID from search results)
TRACE_ID="abc123def456"
curl -s "http://localhost:3100/api/traces/${TRACE_ID}" | jq .
# Check Tempo ingester health and flush metrics
METRICS_URL="http://localhost:3100/metrics"
curl -s "$METRICS_URL" \
| grep "tempo_ingester_blocks_total\|tempo_distributor_spans_received_total" \
| head -20
#!/bin/bash
# check_tempo_health.sh — Comprehensive health check for Tempo service
set -euo pipefail
HOST="${1:-localhost}"
PORT="${2:-3100}"
TIMEOUT=5
echo "=== Tempo Health Check ==="
# Check 1: API accessibility (HTTP metrics endpoint)
echo -n "API (HTTP $PORT): "
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time $TIMEOUT \
"http://$HOST:$PORT/" 2>/dev/null || echo "000")
if [[ "$HTTP_CODE" -ge 200 && "$HTTP_CODE" -lt 400 ]]; then
echo "✅ OK (HTTP $HTTP_CODE)"
else
echo "❌ FAILED (HTTP $HTTP_CODE)"
fi
# Check 2: Services endpoint — confirms traces are being received
echo -n "Services API: "
SERVICE_COUNT=$(curl -s --max-time $TIMEOUT \
"http://$HOST:$PORT/services" 2>/dev/null | jq 'length' 2>/dev/null || echo "0")
if [[ "$SERVICE_COUNT" -gt 0 ]]; then
echo "✅ OK (${SERVICE_COUNT} services)"
else
echo "⚠️ DEGRADED (0 services — no traces received recently)"
fi
# Check 3: OTLP gRPC receiver connectivity
echo -n "OTLP gRPC (4317): "
GRPC_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time $TIMEOUT \
"http://$HOST:4317/" 2>/dev/null || echo "000")
if [[ "$GRPC_CODE" -eq 405 ]] || [[ "$GRPC_CODE" -ge 200 ]]; then
echo "✅ OK (listener responding)"
else
echo "❌ FAILED (port not listening)"
fi
echo ""
echo "=== Tempo Health Check Complete ==="
grpcStorageCompression) to reduce storage costs for high-volume deploymentstempo_distributor_spans_dropped_total) as an early warning of ingester overloadrunAsNonRoot: true| Skill | Purpose |
|---|---|
cncf-grafana | Visualize Tempo traces alongside logs and metrics in Grafana dashboards |
coding-distributed-tracing-patterns | Application-level distributed tracing patterns with OpenTelemetry SDK |
cncf-opentelemetry | OpenTelemetry instrumentation for sending traces to Tempo |
No traces appearing in Tempo
kubectl logs -n tempo <distributor-pod> --tail=50 for connection errorsHigh memory usage in ingesters
tempo_ingester_ring_tokens_count — if tokens are imbalanced, reconfigure the ringmax_block_bytes or increase ingester_flush_timeout to trigger earlier flushesSlow trace queries
s3_get_object_latency metrics)# Check Tempo pod status across all distributed components
kubectl get pods -n tempo
# View distributor logs for ingestion errors
kubectl logs -n tempo -l component=distributor --tail=100 -f
# List all traced services
curl -s http://localhost:3100/services | jq .
# Search traces using TraceQL (Loki-compatible)
curl -s "http://localhost:3100/api/search?query={service.name=\"api-gateway\"}&limit=5"
# Check OTLP receiver health via port-forward
kubectl port-forward -n tempo svc/tempo-distributor 4317:4317 &
Use this skill when:
Assess Requirements — Determine the number of services needing tracing, expected trace volume (traces/sec), storage retention period, and whether you have an existing Grafana/Loki stack for integration. Checkpoint: Document trace ingestion rate targets and storage capacity needs.
Deploy Infrastructure — Install Tempo components (distributor/ingester/query-frontend) in a dedicated namespace with proper resource limits, non-root security contexts, and a durable storage backend (S3/GCS). Use Helm charts for automated deployment. Checkpoint: Verify all pods are Running, probes passing, and no CrashLoopBackOff errors. Confirm OTLP receivers are listening on ports 4317/4318.
Instrument Services — Configure OpenTelemetry SDKs in applications to emit traces via OTLP/gRPC (port 4317) or OTLP/HTTP (port 4318). Set the correct distributor endpoint as an environment variable. Ensure proper labels are attached to spans for searchability. Checkpoint: Verify traces appear in Tempo UI within 60 seconds of deployment.
Monitor & Query — Use the TraceQL API (/api/search) for programmatic trace queries, and expose the UI (port-forward or Ingress) for interactive debugging. Monitor distributor drop rates, ingester flush latency, and query response times. Checkpoint: Confirm trace ingestion rate matches expected volume, queries return within acceptable latency, and no traces are being silently dropped.
Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.