In the last part, we set up the OpenTelemetry Collector in agent mode to receive telemetry data from our ML app. But telemetry isn't useful if it's just sitting in logs, right? We want end-to-end traces that we can visualize, search, and troubleshoot.
And that’s exactly where Jaeger enters the scene.
What is Jaeger?
Jaeger is an open-source distributed tracing system, originally built by Uber, and now part of the CNCF. It helps you:
- Monitor distributed transactions
- Understand application latency
- Perform root cause analysis
- Visualize request flow across services
In short, if your app is a mystery novel, Jaeger is Sherlock Holmes.
What are Traces and Spans?
- A trace is a complete journey of a request through your app — from start to finish.
- A span is a single step in that journey, like one function call or one external API hit.
Think of a trace as the delivery of a pizza. Every span is a milestone in that process — order placed, pizza prepared, baked, out for delivery, delivered. Jaeger shows you the whole pizza journey.
Jaeger Deployment in Kubernetes
Let’s deploy Jaeger in our Kubernetes cluster.
This YAML configuration sets up a single-instance Jaeger deployment in all-in-one mode within a Kubernetes cluster, suitable for development environments. The deployment uses the jaegertracing/all-in-one
image and exposes key ports for telemetry (OTLP gRPC on 4317) and visualization (UI on 16686).
The associated ClusterIP service allows internal communication within the cluster, enabling the OpenTelemetry Collector to send trace data to Jaeger and providing access to the Jaeger UI via port forwarding for trace analysis and visualization.
apiVersion: apps/v1 kind: Deployment metadata: name: jaeger labels: app: jaeger spec: replicas: 1 selector: matchLabels: app: jaeger template: metadata: labels: app: jaeger spec: containers: - name: jaeger image: jaegertracing/all-in-one:latest resources: requests: cpu: "10m" memory: "128Mi" limits: cpu: "20m" memory: "256Mi" ports: - containerPort: 4317 - containerPort: 6831 - containerPort: 16686 - containerPort: 14250 --- apiVersion: v1 kind: Service metadata: name: jaeger spec: selector: app: jaeger type: ClusterIP ports: - name: ui port: 16686 targetPort: 16686 - name: grpc port: 4317 targetPort: 4317
Save this configuration in a yaml file jaeger.yaml
and deploy the jaeger using the following command:
kubectl -n observability apply -f jaeger.yaml
You can verify it's up using:
kubectl -n observability get all -l app=jaeger
The Jaeger UI will be available at the service's ClusterIP
. Use kubectl port-forward
to access the Jaeger UI locally:
kubectl -n observability port-forward svc/jaeger 16686:16686
Now open http://localhost:16686 in your browser.
Update the OpenTelemetry Collector Pipeline
Now that Jaeger is live, we need to update the OpenTelemetry Collector config to export spans to Jaeger.
# otel-collector-agent-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: otel-collector-agent-config namespace: observability data: otel-collector-config.yaml: | receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 # receive traces and metrics from instrumented application processors: memory_limiter: check_interval: 1s limit_percentage: 80 spike_limit_percentage: 15 batch: send_batch_size: 1000 timeout: 5s exporters: otlp/jaeger: endpoint: "http://jaeger.observability.svc.cluster.local:4317" # export traces to jaeger tls: insecure: true service: pipelines: # collect trace data using otlp receiver and send it to jaeger traces: receivers: [otlp] processors: [memory_limiter, batch] exporters: [otlp/jaeger]
Apply the updated ConfigMap:
kubectl -n observability apply -f otel-collector-agent-configmap.yaml
Rollout the collector to pick up the new config:
kubectl -n observability rollout restart deployment otel-collector-agent
Test the Setup
Get the Endpoint IP from the K8s service:
API_ENDPOINT_IP=$(kubectl -n mlapp get svc -l app=house-price-service -o json | jq -r '.items[].spec.clusterIP')
Test it locally using curl
or Postman:
curl -X POST "http://${API_ENDPOINT_IP}:80/predict/" \ -H "Content-Type: application/json" \ -d '{"features": [1200]}'
View Traces in Jaeger UI
Open Jaeger UI in your browser.
- Select the
house-price-service
- Hit Find Traces
- Voilà! You can now trace requests, view span timings, and debug latency in style.
Up Next: From Spans to Stats — Let’s Talk Metrics
Now that Jaeger is live and humming—collecting traces and giving us deep insights into our application’s behavior—it's time to turn our attention to the second pillar of observability: metrics.
Stay tuned as we wire up Prometheus and bring metrics into the mix, completing another piece of our observability blueprint.
{ "author" : "Kartik Dudeja", "email" : "kartikdudeja21@gmail.com", "linkedin" : "https://linkedin.com/in/kartik-dudeja", "github" : "https://github.com/Kartikdudeja" }
Top comments (0)