Opentracing Istio Troubleshooting

Tackle the challenge of observability in a Kubernetes application that consists of multiple microservices running in the Open Liberty application server.
Alternatives To Opentracing Istio Troubleshooting
Project NameStarsDownloadsRepos Using ThisPackages Using ThisMost Recent CommitTotal ReleasesLatest ReleaseOpen IssuesLicenseLanguage
13 hours ago311gpl-3.0C
Real-time performance monitoring, done right!
Cilium14,8181513 hours ago547September 14, 20221,112apache-2.0Go
eBPF-based Networking, Security, and Observability
18 hours ago127April 23, 2021379apache-2.0Go
The container platform tailored for Kubernetes multi-cloud, datacenter, and edge management ⎈ 🖥 ☁️
4 days ago67apache-2.0Go
The API traffic analyzer for Kubernetes providing real-time K8s protocol-level visibility, capturing and monitoring all traffic and payloads going in, out and across containers, pods, nodes and clusters.. Think TCPDump and Wireshark re-invented for Kubernetes
Victoriametrics8,227414 hours ago174September 08, 2022636apache-2.0Go
VictoriaMetrics: fast, cost-effective monitoring solution and time series database
a day ago88April 24, 2021236apache-2.0C++
Instant Kubernetes-Native Application Observability
Kube State Metrics4,4334814 hours ago93June 03, 202254apache-2.0Go
Add-on agent to generate and expose cluster-level metrics.
13 hours ago5August 10, 2022164apache-2.0Go
Open source cloud native security observability platform. Linux, K8s, AWS Fargate and more.
Parca2,985120 hours ago83July 29, 2022147apache-2.0TypeScript
Continuous profiling for analysis of CPU and memory usage, down to the line number and throughout time. Saving infrastructure cost, improving performance, and increasing reliability.
Hubble2,600421 hours ago42June 22, 202229apache-2.0Go
Hubble - Network, Service & Security Observability for Kubernetes using eBPF
Alternatives To Opentracing Istio Troubleshooting
Select To Compare

Alternative Project Comparisons

Build Status

Use MicroProfile OpenTracing and distributed tracing with Istio to enhance system observability.


The shift toward distributed, container-based microservice architectures brings with it a number of benefits but also drawbacks. While Kubernetes makes it easier than ever to split up monoliths into multiple, smaller services that use multiple network requests to complete every transaction, engineers and operations teams face challenges in observability, problem determination and root cause analysis.

Istio, a joint effort between IBM, Google and Lyft creates a service mesh that can be integrated into a container orchestration platform like Kubernetes. While the set of technologies provided by Istio promises to enhance system observability, developers should be aware of new requirements to take advantage of these capabilities.

In this pattern we'll look at how Open Liberty, MicroProfile and Open Tracing work alongside Istio and introduce the concept of distributed tracing, a way to capture, visualize and tell the story of what happens to an individual request. An application inside the mesh does need to do some extra work to propagate request context through the mesh. Open Liberty reduces the effort needed to instrument code to allow services to use distributed tracing.

Tracing with Istio

One of the perhaps confusing areas of Istio is whether and how much an application has to be changed to take advantage of the distributed tracing support. If you install the standard "Bookinfo" application, you will find that requests are traced and visible in Zipkin or Jaeger. However, each service has code to capture and propagate tracing headers. (See documentation here). Manually handling context propagation is error prone, tedious and runs the risk creating gaps in the trace record. This is where Open Liberty and MicroProfile can help. When using the JAX-RS REST client alongside MicroProfile OpenTracing, spans and request errors are sent to the trace collector, and header-based trace ID propagation is handled automatically.

Along with deploying Open Liberty based containers, we also add a Node microservice not using any tracing library. This will demonstrate how to take advantage of Envoy and its understanding of tracing headers to connect its function to the broader request context.

In this pattern, we'll go through these steps:

  1. Installing Istio, enabling tracing and providing access to a dashboard.
  2. Add MicroProfile and OpenTracing to Open Liberty project.
  3. Run a simple microservice based application and explore a distributed tracing UI.
  4. Investigate application failures using information from distributed tracing.


The sample app deployed here, a modified version of the instrument-craft-shop demo app from Sebastian Daschner, simulates an interface to a manufacturing facility. The details of the internals are less important than simply showing an application whose function is distributed across several microservices.

The sample app is made up of six services, shown below. The app is intended to be as simple as possible to demonstrate some features of distributed tracing without getting bogged down by details of its functionality. Some of the services will throw exceptions at random times to simulate failures that give us a chance to explore distributed tracing features.


After a user sends a POST call into the ingress gateway public IP address, that request flows through the sample app as follows:

  1. The Istio ingress gateway forwards the request to the service registered under the instrument-craft-shop name.
  2. The instrument-craft-shop service calls to the maker-bot service, which then:
  3. kicks off the "processing pipeline", which consists of four steps, each running in a separate pod.
  4. the maker-bot service waits for the entire pipeline to complete.
  5. If the pipeline completes, the final step in the sequence is a call from the maker-bot to the dbwrapper service which, in a real service could persist the object to a database, but in our case sleeps for a short period of time before returning a response.

Included Components


Create a Kubernetes cluster with either Minikube for local testing or with IBM Cloud Kubernetes Service to deploy in cloud. Once your cluster is up, install Helm, which is required for Istio installation.


Let's install the Istio service mesh installed on top of your Kubernetes cluster.

$ curl -L | sh -
$ mv istio-<version> istio # replace with version downloaded
$ kubectl create namespace istio-system
$ kubectl apply -f install/kubernetes/helm/helm-service-account.yaml
$ helm init --service-account tiller

When installing Istio, you are able install either a Jaeger or Zipkin tracing back-end. Most of the steps are the same with either service:

To install with a Jaeger back-end, run:

helm install kubernetes/helm/istio --name istio --namespace istio-system  --set grafana.enabled=true --set servicegraph.enabled=true --set tracing.enabled=true

for Zipkin:

helm install kubernetes/helm/istio --name istio --namespace istio-system  --set grafana.enabled=true --set servicegraph.enabled=true --set tracing.enabled=true --set tracing.provider=zipkin
  • Enable automatic sidecar injection and increase trace sampling frequency.

Verify that the envoy side car is added to each deployed pod:

$ kubectl label namespace default istio-injection=enabled --overwrite
$ kubectl get namespace -L istio-injection
default          Active   1d    enabled

By default the sampling frequency tracing is 1%. This is suitable for services where very high request volume would create too much noise in the tracing data. For our purposes, we can set it to 100% to allow all requests to be traced.

Run this command to edit the Istio pilot settings: $ kubectl -n istio-system edit deploy istio-pilot

Find the parameter PILOT_TRACE_SAMPLING, set its value to 100 and save the changes.

          value: "100"

Finally, set up port forwarding to access the tracing UI on your system:

  • If Istio is installed with Jaeger:
kubectl port-forward -n istio-system $(kubectl get pod -n istio-system -l app=jaeger -o jsonpath={.items[0]}) 16686:16686 &

and go to http://localhost:16686

  • For Zipkin (if tracing.provider=zipkin was used) run:
 kubectl port-forward -n istio-system $(kubectl get pod -n istio-system -l app=zipkin -o jsonpath={.items[0]}) 9411:9411 &

and access the dashboard at http://localhost:9411

  • Clone the sample application.

Create a working directory to clone this repo and to download Istio into: (note: will be moved to this repository after review)

$ mkdir ibm
$ cd ibm
$ git clone 

Run the deployment scripts. The Docker images for this pattern are already uploaded to Docker hub.

$ ./
service/instrument-craft-shop unchanged
deployment.apps/instrument-craft-shop created
deployment.apps/maker-bot created
service/maker-bot unchanged
deployment.apps/dbwrapper created
service/dbwrapper unchanged
deployment.apps/pipeline-n1 created
service/pipeline-n1 unchanged
deployment.apps/pipeline-n2 created
service/pipeline-n2 unchanged
deployment.apps/pipeline-n3 created
service/pipeline-n3 unchanged
deployment.apps/pipeline-js created
service/pipeline-js unchanged created created

Verify pods are running (each pod should have 2 containers)

$   kubectl get pods
NAME                                     READY   STATUS    RESTARTS   AGE
dbwrapper-7cbffb64fd-s9djp               2/2     Running   0          2m
instrument-craft-shop-86f8bbcb97-jn7xh   2/2     Running   0          2m
maker-bot-64ffbc7c65-pp9sr               2/2     Running   0          2m
pipeline-js-57c4dcd475-9bbcp             2/2     Running   0          31s
pipeline-n1-694c45bb8b-mkdgz             2/2     Running   0          2m
pipeline-n2-77b8d95b49-d7b5m             2/2     Running   0          2m
pipeline-n3-7dd8976ddb-rnwk8             2/2     Running   0          2m

Once services are running, find the istio-ingressgateway service external IP and port:

$  kubectl get svc -n istio-system istio-ingressgateway
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                                                                                                                   AGE
istio-ingressgateway   LoadBalancer   80:31380/TCP,443:31390/TCP,31400:31400/TCP,15011:31315/TCP,8060:31977/TCP,853:31762/TCP,15030:30709/TCP,15031:31912/TCP   26d

In this cluster, the external address is A request to the application can be sent with curl:

curl -i -XPOST -H 'count: 1'  -H 'Content-Type: application/json' -d '{"type":"GUITAR", "price":200}'

Without application instrumentation

Before we look at what we get with distributed tracing enabled, here's a screenshot from the tracing UI with Istio installed but without any application-level context propagation. (Open Tracing/zipkin configuration removed from Open Liberty and no manual header duplication.) Envoy reports each HTTP call to the trace collector but it creates new trace IDs for every span, so there's no way for the trace subsystem to link them into one request.


MicroProfile Open Tracing vs. manual context propagation.

First, let's take a look at how context propagation is done manually. Without a tracing library, headers in the Node service need to be copied from input to output to maintain the unified trace context:

    var b3headers = ["x-b3-traceid",
					 "x-b3-parentspanid"]; = {};
    b3headers.forEach(function(hdr) {
        var value = req.headers[hdr];
        if (value) {
  [hdr] = value;

When viewed in Jaeger or Zipkin, the Node service is still visible among the Java applications, since the traceid is consistent with trace IDs propagated by JAX-RS/mpOpenTracing, allowing it to be placed in context of other services. Every time a network call is made, these headers must be propagated, either manually or through a library that wraps network calls.

Open Liberty changes to build Open Liberty Docker containers that enable trace propagation.

On the Open Liberty side, we're configuring trace reporting by modifying configuration in server.xml. Note that there are no changes in the Java code needed. Since all REST calls use the JAX-RS client library - and associated classes - new trace spans are sent to the trace collector during each call. The x-b3-traceid header is also preserved across calls, allowing non-Liberty services to be part of a unified trace.

  1. Changes in server.xml

We add the microProfile-2.1, which brings in the entire suite of MicroProfile features, including JAX-RS and mpOpenTracing. The Zipkin feature provides support for Zipkin compatible trace collector. With the opentracingZipkin parameter, we point Liberty to the tracing collector in Istio. This address is valid for either a Zipkin or Jaeger based Istio installation, as Jaeger natively supports the Zipkin tracing format, and Istio maps its collector address to zipkin.istio-system:9411. This address can be found by checking the startup arguments in any envoy proxy deployment:

$ kubectl describe pod maker-bot-64ffbc7c65-pp9sr | grep -A1 zipkinAddress
  • server.xml:

    <opentracingZipkin host="zipkin.istio-system" port="9411" />

  1. Changes in the Dockerfile

In the Dockerfile, we are copying the lib directory that includes the zipkin user feature downloaded by Maven:

COPY target/liberty/wlp/usr/extension /opt/ol/wlp/usr/extension/
  1. Changes in pom.xml

These sections in the pom.xml are necessary to download the zipkin user feature.


And the plugin to download the user feature:


Test Scenario: Normal request processing

Let's take a look at one successful request.

Then, we'll make a single REST call to our service via the ingress IP/port found earlier:

curl -i -XPOST -H 'count: 1' -H 'Content-Type: application/json' -d '{"type":"GUITAR", "price":200}'

Heading over to the dashboard, we'll see a trace that looks like this. Notice that we see the total time for the request (3.5s) and have separate spans for work done in each service. Because both MicroProfile OpenTracing and the Envoy proxy (sidecar container) are sending traces to the collector, spans for both show up and are nearly identical in length, as the proxy adds very little latency to each call. (Since the JAX-RS integration with MicroProfile OpenTracing propagates the x-b3-traceid header value across network calls, the trace collector is able to combine information from both services. This is also the reason our Node service (pipeline-js in the diagram here) is made part of this collection: even though it's not using an OpenTracing (or any tracing library) for that matter, we're able to see the work in the Node service in the context of the larger request.


Error Scenario: Load testing and network timeout

We'll use Artillery (, a service load testing tool to drive many requests to our cluster. Once the run is complete, we can examine the distributed tracing dashboard to understand how the service behaves and examine any errors.

Note: the target line in the load-test.yml file should be updated to the IP/port found earlier.

$ npm install -g artillery
$ artillery run load-test.yml 

All virtual users finished
Summary report @ 14:59:53(-0400) 2019-04-04
  Scenarios launched:  28
  Scenarios completed: 28
  Requests completed:  28
  RPS sent: 0.82
  Request latency:
    min: 747.1
    max: 25311.3
    median: 8253.1
    p95: 24145.1
    p99: 25311.3
  Scenario counts:
    build instrument: 28 (100%)
    204: 20
    500: 8

This launched 28 requests over the course of 10 seconds; based on HTTP return codes, we can infer that the application successfully completed only 20 of the requests. If we load the service in the Jaeger UI and sort by most recent traces, Jager builds a scatter plot showing at a glance how the service performed; we can examine the outliers by clicking on them (red arrow).


The result shows us that the pipeline-n1 process had an extremely long runtime, ultimately resulting in a network timeout upstream:


Upstream timeout. Note the Logs is available due to JAX-RS instrumentation through Open Liberty tracing instrumentation, which is not available directly through Envoy.


If we follow the x-request-id value from the envoy trace entry for that process, we can use that to locate the specific invocation of this request.


Normally, you would want to aggregate your logs in a central location (See LogDNA in IBM Cloud) but for the purpose of our demo, we can check the pod with kubectl logs and filter by request-id. We find it ran for 19370ms, causing a connection error in the top level maker-bot service, which has client connections set to timeout after 20 seconds.

$ kubectl logs pipeline-n1-694c45bb8b-8nlns pipeline-node | grep -C6 aefd6fa9-635b-9a83-895a-c5f3e3d14225
Accept: */*
User-Agent: Apache-CXF/3.3.0
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 42
X-Forwarded-Proto: http
x-request-id: aefd6fa9-635b-9a83-895a-c5f3e3d14225
x-envoy-decorator-operation: pipeline-n1.default.svc.cluster.local:9080/*
x-istio-attributes: Cj8KCnNvdXJjZS51aWQSMRIva3ViZXJuZXRlczovL21ha2VyLWJvdC02NGZmYmM3YzY1LXp6ZzVqLmRlZmF1bHQKPgoTZGVzdGluYXRpb24uc2VydmljZRInEiVwaXBlbGluZS1uMS5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsCkEKF2Rlc3RpbmF0aW9uLnNlcnZpY2UudWlkEiYSJGlzdGlvOi8vZGVmYXVsdC9zZXJ2aWNlcy9waXBlbGluZS1uMQpDChhkZXN0aW5hdGlvbi5zZXJ2aWNlLmhvc3QSJxIlcGlwZWxpbmUtbjEuZGVmYXVsdC5zdmMuY2x1c3Rlci5sb2NhbAoqCh1kZXN0aW5hdGlvbi5zZXJ2aWNlLm5hbWVzcGFjZRIJEgdkZWZhdWx0CikKGGRlc3RpbmF0aW9uLnNlcnZpY2UubmFtZRINEgtwaXBlbGluZS1uMQ==
doWork process time = 19370 ms
[err]   at javax.servlet.http.HttpServlet.service(
[err]   at
[err]   at [internal classes]

With the help of distributed tracing, the entire process of finding the longest running request and locating its logs took seconds.

The x-request-id value is injected by Istio header can be forwarded as well, but if left alone can still be useful by as a way to locate individual service requests, as tracing does not record detailed error messages or stack traces.

More details about Envoy's use of HTTP header fields can be found here.

Note: Because the traces don't retain information about path parameters or payload bodies, separating REST operations by URL helps to identify what was going on by quickly glancing at the trace UI.

Error Scenario: misconfiguration

In this section, we'll look at a relatively simple situation: an internal service can't be reached. In a Kubernetes cluster it can happen that a service isn't ready (possibly still deploying) or is listening at an URL that doesn't match a client's configuration. Another service attempting to call this service will get an error. This jumps out immediately in the distributed tracing system as some spans just don't appear. In this case, a pipeline node did not complete its startup and the web application was not ready to receive requests. We can see it's missing from the trace entirely and if we dig deeper we find a "404" message in the maker-bot:

4041 4041

The specific error message is available due to MicroProfile OpenTracing reporting the error to the trace collector.

Error Scenario: run-time application failure

Lastly, let's take a look at a typical application error.


We can see how an error in one pipeline process ripples up through the call chain so you can follow it to the initial request that triggered the sequence.


If we check the Envoy proxy's span one level above the JAX-RS span, we find the request-id that was injected into the HTTP header, making it possible to locate the specific Java stack trace (and more error details) in your centralized logging system.



In this pattern, we set up a Kubernetes cluster, installed the Istio service mesh, and added tracing instrumentation to our microservice application.

As we saw, Istio mesh adds observability, but it's not completely "free", as applications do need to instrument their code. We saw how Open Liberty's MicroProfile can simplify implementing end-to-end tracing across a microservice application. A toolkit like Open Liberty's MicroProfile and JAX-RS that allows automatic span creation reduces the chance that important information is lost due to gaps in recorded traces.


More about Open Liberty and mpOpenTracing on the official blog


This code pattern is licensed under the Apache License, Version 2. Separate third-party code objects invoked within this code pattern are licensed by their respective providers pursuant to their own separate licenses. Contributions are subject to the Developer Certificate of Origin, Version 1.1 and the Apache License, Version 2.

Apache License FAQ

Popular Kubernetes Projects
Popular Observability Projects
Popular Virtualization Categories
Related Searches

Get A Weekly Email With Trending Projects For These Categories
No Spam. Unsubscribe easily at any time.
Distributed Tracing