Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Python auto-instrumentation injection #962

Closed
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 173 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,56 +166,209 @@ When using sidecar mode the OpenTelemetry collector container will have the envi

### OpenTelemetry auto-instrumentation injection

The operator can inject and configure OpenTelemetry auto-instrumentation libraries. Currently Java, NodeJS and Python are supported.
The operator can inject and configure OpenTelemetry auto-instrumentation libraries.
Currently Java, NodeJS and Python are supported. Each language auto-instrumentation supports different exporters.
Auto-instrumentation is configured by [Instrumentation](./apis/v1alpha1/instrumentation_types.go) resource.

To use auto-instrumentation, configure an `Instrumentation` resource with the configuration for the SDK and instrumentation.
#### Instrumentation resource

OpenTelemetry-Operator uses Instrumentation resource to define auto-instrumentation configuration.
A single `Instrumentation` resource can host configuration to all available auto-instrumentations.
It is mandatory to create an `Instrumentation` resource.
mat-rumian marked this conversation as resolved.
Show resolved Hide resolved

```yaml
kubectl apply -f - <<EOF
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to keep this as an reference example people can apply to the cluster

apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
## name - defines name of the instrumentation resource
mat-rumian marked this conversation as resolved.
Show resolved Hide resolved
name: my-instrumentation
spec:
## env - a list of Env objects which will be added to the instrumented pod
env:
- name: MY_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
mat-rumian marked this conversation as resolved.
Show resolved Hide resolved
- name: CUSTOM_ATTR
value: "custom_value"

## exporter.endpoint - defines endpoint to the collector
mat-rumian marked this conversation as resolved.
Show resolved Hide resolved
## and sets OTEL_EXPORTER_OTLP_ENDPOINT environment variable
mat-rumian marked this conversation as resolved.
Show resolved Hide resolved
exporter:
endpoint: http://otel-collector:4317


## propagators defines which format is used for distributed context
## values are applied to OTEL_PROPAGATORS environment variable
propagators:
- tracecontext
- baggage
- b3

## resource defines the configuration for the resource attributes
resource:
## resourceAttributes - defines attributes that are added to the resource.
mat-rumian marked this conversation as resolved.
Show resolved Hide resolved
resourceAttributes:
attribKey: "attribVal"

## addK8sUIDAttributes defines whether K8s UID attributes should be collected
## (e.g. k8s.deployment.uid)
addK8sUIDAttributes: true


## sampler - defines sampling on the instrumentation SDK level
mat-rumian marked this conversation as resolved.
Show resolved Hide resolved
## sets OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG environment variable
mat-rumian marked this conversation as resolved.
Show resolved Hide resolved
sampler:
type: parentbased_traceidratio
argument: "0.25"
EOF

## java - defines environment variables specific only for java instrumentation
## and auto-instrumentation libraries image
java:
## env - smiliar to spec.env but used only for java auto-instrumentation injection
env:
- name: EXAMPLE_JAVA_ENV
value: "value"
## img - if not set by default points to opentelemetry-operator auto-instrumentation images,
## a place for customized auto-instrumentation libraries
img: my-custom.java-img.repository:latest


## nodejs - defines environment variables specific only for nodejs instrumentation
## and auto-instrumentation libraries image
nodejs:
## env - smiliar to spec.env but used only for nodejs auto-instrumentation injection
env:
- name: EXAMPLE_NODEJS_ENV
value: "value"
## img - if not set by default points to opentelemetry-operator auto-instrumentation images,
## a place for customized auto-instrumentation libraries
img: my-custom.nodejs-img.repository:latest

## python - defines environment variables specific only for python instrumentation
## and auto-instrumentation libraries image
python:
## env - smiliar to spec.env but used only for python auto-instrumentation injection
env:
- name: EXAMPLE_PYTHON_ENV
value: "value"
## img - if not set by default points to opentelemetry-operator auto-instrumentation images,
## a place for customized auto-instrumentation libraries
img: my-custom.python-img.repository:latest
```

The above CR can be queried by `kubectl get otelinst`.
The above CR can be queried by `kubectl get otelinst`.

Then add an annotation to a pod to enable injection. The annotation can be added to a namespace, so that all pods within
that namespace wil get instrumentation, or by adding the annotation to individual PodSpec objects, available as part of
Deployment, Statefulset, and other resources.
#### Auto-instrumentation injection

Java:
```bash
instrumentation.opentelemetry.io/inject-java: "true"
```
To enable auto-instrumentation it is required to add an annotation to a pod. The annotation can be added to a namespace,
so that all pods within that namespace wil get instrumentation, or by adding the annotation to individual PodSpec
objects, available as part of Deployment, Statefulset, and other resources.

NodeJS:
```bash
instrumentation.opentelemetry.io/inject-nodejs: "true"
```
Each language auto-instrumentation has specific annotation to be set:

Python:
```bash
instrumentation.opentelemetry.io/inject-python: "true"
```
* Java:
```yaml
instrumentation.opentelemetry.io/inject-java: "true"
```

* NodeJS:
```yaml
instrumentation.opentelemetry.io/inject-nodejs: "true"
```

* Python:
```yaml
instrumentation.opentelemetry.io/inject-python: "true"
```

The possible values for the annotation can be
* `"true"` - inject and `Instrumentation` resource from the namespace.
* `"my-instrumentation"` - name of `Instrumentation` CR instance in the current namespace.
* `"my-other-namespace/my-instrumentation"` - name and namespace of `Instrumentation` CR instance in another namespace.
* `"false"` - do not inject

#### Java auto-instrumentation

Auto-instrumentation is provided by [OpenTelemetry Java Instrumentaion](https://github.com/open-telemetry/opentelemetry-java-instrumentation).
By default `OTLP gRPC exporter` is used and default port is `4317`.
To customize configuration please see [OT Java Auto-Instrumentation configuration](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#exporters).

Example `Instrumentation` resource:

```yaml
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: my-java-instrumentation
spec:
exporter:
endpoint: http://otel-collector:4317
propagators:
- tracecontext
- baggage
- b3
sampler:
type: always_on
```

#### NodeJS auto-instrumentation

NodeJS auto-instrumentation comes from [OpenTelemetry JS](https://github.com/open-telemetry/opentelemetry-js) for SDK
and [OpenTelemetry JS Contrib](https://github.com/open-telemetry/opentelemetry-js-contrib) for instrumentation.
Auto-instrumentation is supporting **only** [OTLP gRPC exporter](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-grpc)
and by default OpenTelemetry Collector accepts telemetry data on port `4317` for this protocol.

Example `Instrumentation` resource:

```yaml
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: my-nodejs-instrumentation
spec:
exporter:
endpoint: http://otel-collector:4317
propagators:
- tracecontext
- baggage
sampler:
type: always_on
nodejs:
env:
- name: OTEL_RESOURCE_ATTRIBUTES
value: "application=my-js-app"
```

#### Python auto-instrumentation

[OpenTelemetry Python](https://github.com/open-telemetry/opentelemetry-python) and [OpenTelemetry Python Contrib](https://github.com/open-telemetry/opentelemetry-python-contrib)
are the sources of auto-instrumentation libraries. Python instrumentation supports [OTLP Proto HTTP exporter](https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-otlp-proto-http)
and default port on the collector side is `4318`. OTLP gRPC exporter is not appropriate for injected auto-instrumentation,
where it has a strict dependency on the OS / Python version the artifact is built for.

Example `Instrumentation` resource:

```yaml
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: my-python-instrumentation
spec:
exporter:
endpoint: http://otel-collector:4318
propagators:
- tracecontext
- baggage
- b3
sampler:
type: always_on
python:
env:
- name: OTEL_RESOURCE_ATTRIBUTES
value: "application=my-python-app"
```

#### Multi-container pods

If nothing else is specified, instrumentation is performed on the first container available in the pod spec.
Expand Down
4 changes: 4 additions & 0 deletions pkg/instrumentation/podmutator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,10 @@ func TestMutatePod(t *testing.T) {
Name: "PYTHONPATH",
Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix),
},
{
Name: "OTEL_METRICS_EXPORTER",
Value: "otlp_proto_http",
},
{
Name: "OTEL_EXPORTER_OTLP_TIMEOUT",
Value: "20",
Expand Down
21 changes: 16 additions & 5 deletions pkg/instrumentation/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ import (
)

const (
envPythonPath = "PYTHONPATH"
envOtelTracesExporter = "OTEL_TRACES_EXPORTER"
pythonPathPrefix = "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation"
pythonPathSuffix = "/otel-auto-instrumentation"
envPythonPath = "PYTHONPATH"
envOtelTracesExporter = "OTEL_TRACES_EXPORTER"
envOtelMetricsExporter = "OTEL_METRICS_EXPORTER"
envValOtelHttpExporter = "otlp_proto_http"
pythonPathPrefix = "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation"
pythonPathSuffix = "/otel-auto-instrumentation"
)

func injectPythonSDK(logger logr.Logger, pythonSpec v1alpha1.Python, pod corev1.Pod, index int) corev1.Pod {
Expand Down Expand Up @@ -64,7 +66,16 @@ func injectPythonSDK(logger logr.Logger, pythonSpec v1alpha1.Python, pod corev1.
if idx == -1 {
container.Env = append(container.Env, corev1.EnvVar{
Name: envOtelTracesExporter,
Value: "otlp_proto_http",
Value: envValOtelHttpExporter,
})
}

// Set OTEL_METRICS_EXPORTER to HTTP exporter if not set by user because it is what our autoinstrumentation supports.
idx = getIndexOfEnv(container.Env, envOtelMetricsExporter)
if idx == -1 {
container.Env = append(container.Env, corev1.EnvVar{
Name: envOtelMetricsExporter,
Value: envValOtelHttpExporter,
})
}

Expand Down
Loading