How to Enable and Analyze Istio Distributed Tracing with Jaeger on Kubernetes
This guide explains why distributed tracing is needed, how Istio uses Jaeger as the tracing backend, the required request‑header propagation, step‑by‑step deployment of Jaeger and the Bookinfo demo on Kubernetes, and how to inspect and interpret the generated spans.
Background & Introduction
Distributed tracing records and correlates details of each stage in a distributed call, helping operators quickly locate failures or performance bottlenecks. Istio uses Jaeger, an open‑source tracing system from Uber, as its tracing backend for storage, visualization, and querying.
Special Notes & Constraints
Although Istio claims to be non‑intrusive, tracing still requires intercepting and forwarding request headers. The official documentation explains that the Istio sidecar must add appropriate HTTP headers so that spans can be linked into a single trace.
Despite the Istio proxy automatically sending spans, additional information is needed to attach these spans to the same trace. Each application must collect request headers from incoming requests and forward them to all outbound requests. The specific headers to forward depend on the configured tracing backend.Key request headers include:
x-request-id – Envoy‑specific unique identifier for logs and tracing.
x-b3-traceid – Trace ID generated by the first span and propagated downstream.
x-b3-spanid – Span ID assigned when a span is created.
x-b3-parentspanid – Parent span ID for the current call.
x-b3-sampled – Sampling flag (1 to report the span, 0 to ignore).
x-b3-flags – Additional flags.
These headers allow the server to reconstruct the trace after all span data is pushed.
How Istio‑Proxy Generates Span Information
For inbound traffic, if no tracing headers are present, Envoy creates a root span and injects its context into the request. If tracing headers exist, Envoy extracts the context and forwards it to the application. For outbound traffic, Envoy creates a root span when no headers are present, or creates a child span based on the received context and adds the new span information to the outbound request headers.
Note: This mechanism requires HTTP communication between services so that headers can be propagated.Deploy Jaeger & Test Demo
Prepare a Kubernetes cluster with Istio installed (e.g., Kubernetes 1.25 and Istio 1.19). Deploy Jaeger manually because Istio does not enable it by default.
apiVersion: apps/v1
kind: Deployment
metadata:
name: jaeger
namespace: istio-system
labels:
app: jaeger
spec:
selector:
matchLabels:
app: jaeger
template:
metadata:
labels:
app: jaeger
sidecar.istio.io/inject: "false"
spec:
containers:
- name: jaeger
image: "docker.io/jaegertracing/all-in-one:1.46"
env:
- name: BADGER_EPHEMERAL
value: "false"
- name: SPAN_STORAGE_TYPE
value: "badger"
- name: BADGER_DIRECTORY_VALUE
value: "/badger/data"
- name: BADGER_DIRECTORY_KEY
value: "/badger/key"
- name: COLLECTOR_ZIPKIN_HOST_PORT
value: ":9411"
- name: MEMORY_MAX_TRACES
value: "50000"
- name: QUERY_BASE_PATH
value: /jaeger
livenessProbe:
httpGet:
path: /
port: 14269
readinessProbe:
httpGet:
path: /
port: 14269
volumeMounts:
- name: data
mountPath: /badger
resources:
requests:
cpu: 10m
volumes:
- name: data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: tracing
namespace: istio-system
labels:
app: jaeger
spec:
type: ClusterIP
ports:
- name: http-query
port: 80
protocol: TCP
targetPort: 16686
- name: grpc-query
port: 16685
protocol: TCP
targetPort: 16685
selector:
app: jaeger
---
# Additional services for Zipkin compatibility and collector ports omitted for brevityDeployment succeeded:
Adjust Sampling Rate
Istio’s default sampling rate is 1 %. Increase it to 100 % for full visibility.
kubectl edit iop installed-state -n istio-system spec:
values:
pilot:
traceSampling: 100 # default 1, change to 100Deploy Bookinfo Demo
Deploy the official Bookinfo microservices (details, ratings, reviews, productpage) using the provided Kubernetes manifests.
# Sample excerpt of the Bookinfo manifests (full YAML omitted for brevity)
apiVersion: v1
kind: Service
metadata:
name: details
labels:
app: details
spec:
ports:
- port: 9080
name: http
selector:
app: details
---
# Additional services and deployments for ratings, reviews, and productpage follow the same patternDeployment succeeded:
Testing & Observations
Access the productpage service via its ClusterIP and trigger a request. The request succeeds, and Jaeger’s UI shows generated spans: 7 spans across 4 services with a depth of 5 layers.
Analyzing the Trace
Set the Bookinfo pods’ log level to trace to capture detailed span information.
spec:
template:
metadata:
annotations:
sidecar.istio.io/logLevel: traceLog analysis shows that productpage generates three spans (one inbound, two outbound), reviews generates two spans, and each of ratings and details generates one inbound span, matching the seven spans displayed in Jaeger.
'x-b3-traceid': '235aa67f7b07cc91bbdd05f23bcd7bab', 'x-b3-spanid': 'bbdd05f23bcd7bab'The trace ID remains consistent across the entire call chain.
Business Code Logic
Tracing is performed by the sidecar; application code only needs to forward the received trace headers.
@app.route('/productpage')
@trace()
def front():
product_id = 0 # TODO: replace default value
headers = getForwardHeaders(request)
... def getForwardHeaders(request):
headers = {}
span = get_current_span()
carrier = {}
tracer.inject(span_context=span.context, format=Format.HTTP_HEADERS, carrier=carrier)
headers.update(carrier)
if 'user' in session:
headers['end-user'] = session['user']
return headers def getProductDetails(product_id, headers):
try:
url = details['name'] + "/" + details['endpoint'] + "/" + str(product_id)
res = requests.get(url, headers=headers, timeout=3.0)
...In the Details service, incoming headers are collected and forwarded downstream.
def get_forward_headers(request):
headers = {}
incoming_headers = [
'x-request-id',
'x-ot-span-context',
'x-datadog-trace-id',
'x-datadog-parent-id',
'x-datadog-sampling-priority',
'x-b3-traceid',
'x-b3-spanid',
'x-b3-parentspanid',
'x-b3-sampled',
'x-b3-flags',
'end-user',
'user-agent',
'cookie',
'authorization',
'jwt'
]
request.each do |header, value|
if incoming_headers.include? header then
headers[header] = value
end
end
return headers
endSigned-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
