The OpenTelemetry Java agent has a number of ways to install the agent into a Java application. If you are running your Java applications in Kubernetes pods, there is a separate mechanism (which under the hood uses JAVA_TOOL_OPTIONS and other environment variables) to auto-instrument Java applications. This auto-instrumentation can be achieved with zero configuration of the applications and pods!
The mechanism to achieve zero-config auto-instrumentation of Java applications in Kubernetes is via the OpenTelemetry Operator for Kubernetes. This operator has many capabilities and the full documentation (and of course source) is available in the project itself. In this blog, I'll walk through installing, setting up and running zero-config auto-instrumentation of Java applications in Kubernetes using the OpenTelemetry Operator.
Installing the OpenTelemetry Operator
At the time of writing this blog, the OpenTelemetry Operator needs the certification manager to be installed, after which the operator can be installed. Installing from the web is straightforward. First install the
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml
Then when the cert managers are ready (
NAMESPACE NAME READY
cert-manager cert-manager-67c98b89c8-rnr5s 1/1
cert-manager cert-manager-cainjector-5c5695d979-q9hxz 1/1
cert-manager cert-manager-webhook-7f9f8648b9-8gxgs 1/1
... you can install the OpenTelemetry Operator:
kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
You can, of course, use a specific version of the operator instead of the
An Instrumentation resource
Now you need to add just one further Kubernetes resource to enable auto-instrumentation: an
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: banana-instr
namespace: banana
spec:
exporter:
endpoint: "https://my.endpoint"
propagators:
- tracecontext
- baggage
- b3
sampler:
type: parentbased_traceidratio
argument: "1.0"
java:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:2.5.0
env:
- name: OTEL_EXPORTER_OTLP_HEADERS
value: "Authorization=Bearer MyAuth"
Creating this resource (eg with
metadata:
annotations:
instrumentation.opentelemetry.io/inject-java: "true"
The
apiVersion: v1
kind: Namespace
metadata:
name: banana
annotations:
instrumentation.opentelemetry.io/inject-java: "banana-instr"
...
Now we have a namespace that is going to auto-instrument every Java application deployed in the
Trying it
There is a simple example Java application at docker.elastic.co/demos/apm/k8s-webhook-test which just repeatedly calls the chain
apiVersion: v1
kind: Pod
metadata:
name: banana-app
namespace: banana
labels:
app: banana-app
spec:
containers:
- image: docker.elastic.co/demos/apm/k8s-webhook-test
imagePullPolicy: Always
name: banana-app
env:
- name: OTEL_INSTRUMENTATION_METHODS_INCLUDE
value: "test.Testing[methodB]"
results in the app being auto-instrumented with no configuration changes! The resulting app shows up in any APM UI, such as Elastic APM
As you can see, for this example I also added this env var to the pod yaml,
The technology behind the auto-instrumentation
To use the auto-instrumentation there is no specific need to understand the underlying mechanisms, but for those of you interested, here’s a quick outline.
- The OpenTelemetry Operator for Kubernetes installs a mutating webhook, a standard Kubernetes component.
- When deploying, Kubernetes first sends all definitions to the mutating webhook.
- If the mutating webhook sees that the conditions for auto-instrumentation should be applied (ie
- there is an Instrumentation resource for that namespace and
- the correct annotation for that Instrumentation is applied to the definition in some way, either from the definition itself or from the namespace),
- then the mutating webhook “mutates” the definition to include the environment defined by the Instrumentation resource.
- The environment includes the explicit values defined in the env, as well as some implicit OpenTelemetry values (see the OpenTelemetry Operator for Kubernetes documentation for full details).
- And most importantly, the operator
- pulls the image defined in the Instrumentation resource,
- extracts the file at the path /javaagent.jarfrom that image (using shell commandcp)
- inserts it into the pod at path /otel-auto-instrumentation-java/javaagent.jar
- and adds the environment variable JAVA_TOOL_OPTIONS=-javaagent:/otel-auto-instrumentation-java/javaagent.jar.
- The JVM automatically picks up that JAVA_TOOL_OPTIONS environment variable on startup and applies it to the JVM command-line.
Next steps
This walkthrough can be repeated in any Kubernetes cluster to demonstrate and experiment with auto-instrumentation (you will need to create the banana namespace first). In part 2 of this two part series, Using a custom agent with the OpenTelemetry Operator for Kubernetes, I show how to install any Java agent via the OpenTelemetry operator, using the Elastic Java agents as examples.