How to Implement Distributed Tracing with Jaeger
Deploy Jaeger for distributed tracing in Kubernetes. Learn to instrument applications, trace requests across services, and identify performance bottlenecks.
π‘ Quick Answer: Deploy Jaeger with the operator:
kubectl apply -f jaeger-operator.yaml, then create aJaegerCR. Instrument apps with OpenTelemetry SDK to send traces via OTLP or Jaeger protocol. Access UI atkubectl port-forward svc/jaeger-query 16686:16686.Key setup: Configure
OTEL_EXPORTER_JAEGER_ENDPOINT=http://jaeger-collector:14268/api/tracesin your app.Gotcha: Ensure context propagation headers (traceparent, b3) are passed between services for end-to-end traces.
How to Implement Distributed Tracing with Jaeger
Distributed tracing helps you understand request flow across microservices. Jaeger provides end-to-end tracing for identifying latency issues and debugging distributed systems.
Deploy Jaeger Operator
# Install cert-manager (required)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# Install Jaeger Operator
kubectl create namespace observability
kubectl apply -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.51.0/jaeger-operator.yaml -n observabilitySimple All-in-One Deployment
# jaeger-allinone.yaml
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
name: jaeger
namespace: observability
spec:
strategy: allInOne
allInOne:
image: jaegertracing/all-in-one:1.51
options:
log-level: info
storage:
type: memory
options:
memory:
max-traces: 100000
ingress:
enabled: true
agent:
strategy: DaemonSetProduction Setup with Elasticsearch
# jaeger-production.yaml
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
name: jaeger-production
namespace: observability
spec:
strategy: production
collector:
replicas: 2
maxReplicas: 5
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
query:
replicas: 2
resources:
limits:
cpu: 500m
memory: 512Mi
storage:
type: elasticsearch
options:
es:
server-urls: http://elasticsearch:9200
index-prefix: jaeger
num-shards: 3
num-replicas: 1
esIndexCleaner:
enabled: true
numberOfDays: 7
schedule: "55 23 * * *"
agent:
strategy: DaemonSetOpenTelemetry Collector Integration
# otel-collector.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: otel-collector-config
namespace: observability
data:
config.yaml: |
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
jaeger:
protocols:
thrift_http:
endpoint: 0.0.0.0:14268
grpc:
endpoint: 0.0.0.0:14250
processors:
batch:
timeout: 10s
memory_limiter:
check_interval: 1s
limit_mib: 400
spike_limit_mib: 100
exporters:
jaeger:
endpoint: jaeger-collector.observability.svc:14250
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp, jaeger]
processors: [memory_limiter, batch]
exporters: [jaeger]
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: otel-collector
namespace: observability
spec:
replicas: 2
selector:
matchLabels:
app: otel-collector
template:
metadata:
labels:
app: otel-collector
spec:
containers:
- name: collector
image: otel/opentelemetry-collector-contrib:0.91.0
args: ["--config=/etc/otel/config.yaml"]
ports:
- containerPort: 4317 # OTLP gRPC
- containerPort: 4318 # OTLP HTTP
- containerPort: 14268 # Jaeger thrift
volumeMounts:
- name: config
mountPath: /etc/otel
resources:
limits:
memory: 512Mi
cpu: 500m
volumes:
- name: config
configMap:
name: otel-collector-config
---
apiVersion: v1
kind: Service
metadata:
name: otel-collector
namespace: observability
spec:
ports:
- name: otlp-grpc
port: 4317
- name: otlp-http
port: 4318
- name: jaeger-thrift
port: 14268
selector:
app: otel-collectorPython Application Instrumentation
# app.py
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
import os
# Configure tracing
trace.set_tracer_provider(TracerProvider())
otlp_exporter = OTLPSpanExporter(
endpoint=os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", "otel-collector:4317"),
insecure=True
)
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(otlp_exporter)
)
# Auto-instrument Flask and requests
from flask import Flask
app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()
tracer = trace.get_tracer(__name__)
@app.route("/api/orders")
def get_orders():
with tracer.start_as_current_span("fetch-orders") as span:
span.set_attribute("order.count", 10)
# Your business logic
return {"orders": []}Node.js Application Instrumentation
// tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'grpc://otel-collector:4317',
}),
instrumentations: [getNodeAutoInstrumentations()],
serviceName: process.env.OTEL_SERVICE_NAME || 'my-service',
});
sdk.start();
// app.js
const express = require('express');
const { trace } = require('@opentelemetry/api');
const app = express();
const tracer = trace.getTracer('my-service');
app.get('/api/users', async (req, res) => {
const span = tracer.startSpan('fetch-users');
try {
span.setAttribute('user.count', 100);
// Your logic here
res.json({ users: [] });
} finally {
span.end();
}
});Kubernetes Deployment with Tracing
# traced-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
annotations:
sidecar.jaegertracing.io/inject: "true"
spec:
containers:
- name: app
image: order-service:v1
env:
- name: OTEL_SERVICE_NAME
value: "order-service"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://otel-collector.observability:4317"
- name: OTEL_TRACES_SAMPLER
value: "parentbased_traceidratio"
- name: OTEL_TRACES_SAMPLER_ARG
value: "0.1" # Sample 10% of traces
ports:
- containerPort: 8080
resources:
limits:
memory: 256Mi
cpu: 200mContext Propagation Between Services
# Ensure trace context is propagated via headers
# Required headers: traceparent, tracestate (W3C), or uber-trace-id (Jaeger)
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
OTEL_PROPAGATORS: "tracecontext,baggage,jaeger"Sampling Strategies
# jaeger-sampling.yaml
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
name: jaeger
spec:
strategy: production
sampling:
options:
default_strategy:
type: probabilistic
param: 0.1 # Sample 10%
service_strategies:
- service: payment-service
type: probabilistic
param: 1.0 # Sample 100% for critical service
- service: health-check
type: probabilistic
param: 0.001 # Sample 0.1% for noisy serviceAccess Jaeger UI
# Port forward to access UI
kubectl port-forward svc/jaeger-query -n observability 16686:16686
# Or via IngressapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jaeger
namespace: observability
spec:
rules:
- host: jaeger.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jaeger-query
port:
number: 16686Summary
Jaeger provides powerful distributed tracing capabilities for Kubernetes. Use OpenTelemetry for instrumentation, configure appropriate sampling for production, and leverage the Jaeger UI to debug latency issues across your microservices.
π Go Further with Kubernetes Recipes
Love this recipe? Thereβs so much more! This is just one of 100+ hands-on recipes in our comprehensive Kubernetes Recipes book.
Inside the book, youβll master:
- β Production-ready deployment strategies
- β Advanced networking and security patterns
- β Observability, monitoring, and troubleshooting
- β Real-world best practices from industry experts
βThe practical, recipe-based approach made complex Kubernetes concepts finally click for me.β
π Get Your Copy Now β Start building production-grade Kubernetes skills today!
π Get All 100+ Recipes in One Book
Stop searching β get every production-ready pattern with detailed explanations, best practices, and copy-paste YAML.
Want More Kubernetes Recipes?
This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.