Ship and visualize your Istio virtual service traces with AWS X-Ray
Distributed tracing is a mechanism to derive runtime performance insights of a distributed system by tracking how requests flow through the system components and capturing key performance indicators at call sites. It surfaces dynamic service dependency information along with traffic health information and helps service owners and cluster operators quickly visualize the application landscape, identify performance bottlenecks, and proactively address service issues.
However, in spite of the benefits that a tracing platform brings to the table, standing up a self-managed production ready stack for distributed tracing involves a good amount of engineering effort. At a minimum, a distributed tracing setup commonly involves the following steps:
- Instrument application code, either manually or with the help of an instrumentation library, to propagate trace related headers across inter service calls.
- Package and configure a tracing library to export traces from the application to a collector endpoint.
- Deploy a collector that can receive the traces and forward to a persistence layer.
- Deploy a data store for trace persistence.
- Deploy API and UI visualization components to allow operators to query the persisted traces and access visualizations to extract actionable insights.
While an application developer must always instrument the code to propagate the tracing context through headers, a service mesh like Istio makes life easier for the application developer by embedding the exporters for popular tracing backends in the service proxies, which reside alongside the application process intercepting all network communications. The proxies can be configured to automatically publish spans for all incoming and outgoing requests. The benefits of this approach are:
- the application is now responsible for only the propagation of the right context, and the service mesh proxies take care of publishing the traces to the configured collector endpoint
- the service mesh administrator can apply the trace agent related configurations in a single place as part of the service mesh control plane setup.
Even then, setting up persistence and UI visualization layer for a popular open source tracing backend like Open Zipkin requires setting up and fine-tuning a data store and spark dependencies, which consume a lot of DevOps bandwidth.
Customers migrating from self-managed deployments to AWS often look for ways to offload a lot of the undifferentiated engineering heavy lifting by moving to a fully managed solution. For distributed tracing, AWS X-Ray provides customers a fully managed experience freeing up DevOps teams to focus more on innovation rather than busy work.
In addition to distributed tracing, any non-trivial production system also has to deal with other telemetry concerns like metrics and logs collection. Often these solutions will have their own agent/collector implementations leading to a proliferation of agents and collectors being deployed in the operating environment each with its own specifications and protocol requirements that may not be easily ported to support other backends. Think of a production environment where metrics are ingested by Prometheus, logs are shipped to Elasticsearch, and traces are sent to Open Zipkin. Now to introduce CloudWatch Logs based EMF custom metrics ingestion, another agent needs to be added in to the already complex mix. This is the core problem that the OpenTelemetry project, an open source initiative under the Cloud Native Computing Foundation (CNCF), attempts to solve by providing a standardized specification and reference implementations for telemetry data generation, processing, and ingestion. The OpenTelemetry Collector subproject provides a single unified, vendor-agnostic collector architecture that can receive standardized metrics, logs, and traces from multiple sources, convert/enrich the received data, and export to multiple backends simplifying deployment complexity.
This post shows how customers can migrate their existing Open Zipkin instrumented applications running on Istio to AWS and enjoy the managed experience of AWS X-Ray. The solution leverages the flexibility offered by the OpenTelemetry Collector project to convert Open Zipkin formatted spans to AWS X-Ray segments.
|Time to read||12 minutes|
|Time to complete||45 minutes|
|Cost to complete (estimated)||$ 0.686|
|Learning level||Expert (400)|
Overview of solution
The collector architecture comprises pluggable receivers, processors, and exporters that can be combined to form pipelines. A metric/trace event enters the pipeline through a receiver, goes through zero or more processors, and finally exits the pipeline through one or more exporters. The receivers implement the wire protocol of the source data format. For example,
zipkin receiver in the
opentelemetry-collector project exposes an endpoint to decode JSON formatted Zipkin v1 or v2 spans into internal OpenTelemetry representation. The exporters, on the other hand, implement the wire transport protocol for the target backend. The collector repository hosts exporters for common open source backends like Zipkin, Jaeger, Prometheus, Kafka and others. It also provides exporters for open formats like OpenCensus and OTLP. The
opentelemetry-collector-contrib repository is a companion repository for commercial vendor implementations. For example, the
awsxrayexporter module in the Contrib repo translates the internal trace spans from the pipeline to AWS X-Ray segment documents and posts the documents to a configured AWS X-Ray endpoint.
The architecture allows new components to be linked in to the collector binary by referencing vendor implementations from the Contrib project. A collector image can be deployed as a standalone Kubernetes deployment with one or more pipeline configurations connecting any number of receivers with any number of backends.
The solution involves deploying a custom collector binary in an EKS cluster where Istio will be set up to publish Zipkin spans to the collector endpoint. The collector will be configured with a single pipeline definition to ingest the spans and export to AWS X-Ray. A logging exporter will also be configured to easily view the internal representation of the spans in the collector log.
The Zipkin tracer built into Istio proxy as of this writing (Istio version 1.7.4) does not conform to the OpenTelemetry semantic conventions and thus requires translation of some of the span attributes to OpenTelemetry resource attributes so that AWS X-Ray can correctly identify the attributes. The pipeline used in the solution sets up the
zipkin receiver module from the
opentelemetry-collector repository and the
awsxrayexporter module from the
opentelemetry-collector-contrib repository. The
opentelemetry-collector-contrib repository was forked and a new
istio-build branch was created in the forked repository to add and test the translations. The collector binary build project maintained in the
otelcol-custom-istio-awsxray repository pulls in these custom translations from the forked repository.
Under the hood
Collector Contrib customizations
The trace id mapping logic in the
awsxrayexporter module has been updated by introducing an in-memory cache for the Trace ID field. AWS X-Ray requires
trace_id in segment documents to embed the epoch time in seconds when the trace had started. Zipkin tracer in Istio, on the other hand, expects the
traceId field to be a randomly generated, 128-bit, unique identifier. The default handling currently checks if the high order 32 bits are within the allowed epoch window and maps to the Trace ID epoch section. If the high order 32 bits are outside the allowed window then the exporter drops the span. The enhancement will take the trace id with epoch outside the valid window and update it with epoch calculated from the start time of the segment. This epoch is also cached for later segments in the same trace. If the segment start time is missing then the current epoch is taken and cached for later lookup. The TTL of the cache is defaulted to 60 seconds but can be configured through the exporter configuration as done in the demo deployment.
The configuration of the exporter has been updated to allow setting a cache provider. The current implementation uses a simple TTL based in-process thread safe cache, which mandates a single instance of the collector to avoid cache sync issues. To allow multiple collector instances to load balance telemetry ingestion in a large cluster, the implementation can be extended to introduce a clustered cache either in-process or external like Amazon ElastiCache for Redis.
The next set of changes refers to the
upstream_cluster attribute of a span. The Istio proxy generated spans always have the span
kind set to
CLIENT irrespective of the direction of traffic. This confuses the exporter and X-Ray service since they expect spans for incoming traffic to have span kind set to
SERVER. (This span kind behavior can potentially confuse other backends also and the custom collector allows working around such bugs until they can be released upstream.) In our case this is resolved by introspecting the
upstream_cluster attribute. This attribute captures whether the span is for incoming or outgoing traffic and other details like service port and remote or local service FQDN. For example, the
upstream_cluster attribute value for egress traffic from a client to an upstream service will look like
outbound|9080||productpage.default.svc.cluster.local. On the server side, the corresponding
upstream_cluster attribute value for the ingress traffic will look like
For more details about the changes please refer to the
istio-build branch of the forked repository.
The custom collector binary is built through the
otelcol-custom-istio-awsxray project. This project is modeled based on the instructions available here. The project pulls in the
awsxrayexporter module from the collector contrib repo but instead of directly referencing the upstream version of the module, it replaces the module link with a locally checked out version from
Once the image is built, tagged, and pushed to a remote registry of choice, deploy the collector, install Istio, and deploy a sample application to see the traces flowing in to AWS X-Ray.
For this tutorial, you should have the following prerequisites:
- An AWS account
- An IAM user
- A workstation with
- AWS CLI v2 to interact with AWS services like ECR and EKS
gitto clone the solution repository
Dockerengine to build the collector image
kubectlcompatible with the server version to interact with the EKS control plane and deploy the collector and istio service mesh
- The tutorial assumes basic familiarity with Istio service mesh, distributed tracing concepts, and AWS X-Ray
Create an EKS cluster
In this section, we are going to setup IAM and create an EKS cluster.
- Log in to IAM console.
- Create a role that will be used to launch the CloudFormation template. The role will be granted admin access to the EKS cluster that will be created by the CloudFormation template. For this demo, a role named
AdministratorAccesspermissions is created. Note that
Require MFAoption is enabled for the new role.
- Once the role is created update your CLI profile (
~/.aws/credentials) to refer to the new role. If MFA is required also add the MFA device serial of the IAM user that will be used to assume the role.
- Assume the new role from the console.
- Clone the
otelcol-custom-istio-awsxrayrepository in a local workspace.
cd ~ mkdir -p github.com/iamsouravin cd github.com/iamsouravin git clone https://github.com/iamsouravin/otelcol-custom-istio-awsxray.git
- Navigate to the CloudFormation console.
- On the
Create stackpage choose
Upload a template filein the
Specify templatesection and click on
docs/examples/base-cfn.yamltemplate file from the cloned workspace.
- Enter a name for the stack. For this demo I have used
otelcol-custom-istio-awsxray-demo. Review and/or update the parameters.
- On the
Configure stack optionspage leave the default values.
- On the
Reviewpage review the selections and acknowledge that the stack may create IAM resources.
Create stackbutton to kick off the stack creation.
- This will be a good time to take a break and grab a warm cup of your favorite beverage.
- Once the stack is created, switch to the terminal and update your local
kubeconfigfile to connect to the cluster.
aws eks update-kubeconfig --name tracing-cluster --region us-east-1 --profile admin-role
- Verify that you are able to connect to the cluster and the nodes are ready.
kubectl cluster-info kubectl get nodes
Build the custom collector binary
In this step, we are going to build the custom collector binary.
- The build script (
bin/build.sh) clones the forked collector contrib repo locally and copies the
awsxrayexportermodule in the project directory before kicking off the docker build. Launch the build with the tag (
-t) argument. The time taken for the build depends on multiple factors like internet bandwidth, build machine cores, volume type (SSD or HDD), etc.
Install and test
In this step, we are going to install the custom collector binary, Istio, and a sample app to test the end-to-end integration.
- Open the generated manifest file (
docs/examples/otelcol-custom-istio-awsxray-manifest.yaml) to verify that the image tag refers to the newly built image.
- Login to the remote Docker registry and push the image.
- The sampling rate for traces generated by the proxies is defaulted to
100for demo purposes. The configuration can be found in
docs/examples/tracing.yaml. This is not meant for production deployments. Typically, operators will set the sampling rate to a low value like 1% in production deployments.
- Run the installer script (
- install the collector in the cluster
- download and install
1.7.4release version of Istio
- label the
defaultnamespace to enable automatic proxy injection
- install and expose the book info app from the Istio samples directory.
- Get the gateway URL of
/productpagefrom the script output.
- Open the product page URL in a browser and refresh a number of times. You should notice that the review section switches across three different versions. Also, try logging in and out with dummy user credentials and repeat for a while.
- If the page does not load in the browser, then the ELB for the ingress gateway may still be registering the target EC2 instances. Either wait for a few minutes or track the registration progress in the Load Balancers view in the EC2 console.
- Navigate to the AWS X-Ray console to review the service map visualization.
- Open the traces pane to drill down into the individual segments.
- You can also exercise the API endpoints exposed by the Bookinfo app using a client like Postman or plain
curland see the trace links update in the service map.
To avoid incurring future charges, delete all the resources.
- Run the uninstallation script (
bin/uninstall-components.sh) to uninstall the components installed by the installer script.
- ECR will not allow deletion of a non-empty repository. Delete the image that was pushed to the
aws ecr batch-delete-image --repository-name otelcol-custom-istio-awsxray --image-ids imageTag=0.1.0 --region us-east-1
- Finally, clean up the remaining resources by triggering Delete stack on the CloudFormation console.
This post gives customers an additional option to leverage AWS X-Ray as a managed backend for traces generated from Istio service mesh on AWS. The solution demonstrates how the OpenTelemetry collector project opens up new integration possibilities and gives DevOps teams full flexibility to choose and customize the parts based on their specific org’s requirements. As the community moves towards a standards-based approach to collecting telemetry data, we hope to see better alignment among different products to support more seamless integration obviating the need for much of the customizations demonstrated in this blog. We are excited to showcase this integration and can’t wait to see how customers will leverage and extend this solution.