Skip to content

Commit c9d17f7

Browse files
committed
Python auto-instrumentation: handle musl based containers
Build and and inject musl based python auto-instrumentation if proper annotation is configured: instrumentation.opentelemetry.io/otel-python-wheel-kind: "musllinux" Refs #2264
1 parent e84193d commit c9d17f7

File tree

11 files changed

+403
-17
lines changed

11 files changed

+403
-17
lines changed

autoinstrumentation/python/Dockerfile

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# To build one auto-instrumentation image for Python, please:
2-
# - Ensure the packages are installed in the `/autoinstrumentation` directory. This is required as when instrumenting the pod,
3-
# one init container will be created to copy all the content in `/autoinstrumentation` directory to your app's container. Then
2+
# - Ensure the packages are installed in the `/autoinstrumentation,{musl}` directory. This is required as when instrumenting the pod,
3+
# one init container will be created to copy all the content in `/autoinstrumentation{,-musl}` directory to your app's container. Then
44
# update the `PYTHONPATH` environment variable accordingly. To achieve this, you can mimic the one in `autoinstrumentation/python/Dockerfile`
55
# by using multi-stage builds. In the first stage, install all the required packages in one custom directory with `pip install --target`.
6-
# Then in the second stage, copy the directory to `/autoinstrumentation`.
6+
# Then in the second stage, copy the directory to `/autoinstrumentation{,-musl}`.
77
# - Ensure you have `opentelemetry-distro` and `opentelemetry-instrumentation` or your customized alternatives installed.
88
# Those two packages are essential to Python auto-instrumentation.
9-
# - Grant the necessary access to `/autoinstrumentation` directory. `chmod -R go+r /autoinstrumentation`
9+
# - Grant the necessary access to `/autoinstrumentation{,-musl}` directory. `chmod -R go+r /autoinstrumentation`
1010
# - For auto-instrumentation by container injection, the Linux command cp is
1111
# used and must be availabe in the image.
1212
FROM python:3.11 AS build
@@ -17,8 +17,19 @@ ADD requirements.txt .
1717

1818
RUN mkdir workspace && pip install --target workspace -r requirements.txt
1919

20+
FROM python:3.11-alpine AS build-musl
21+
22+
WORKDIR /operator-build
23+
24+
ADD requirements.txt .
25+
26+
RUN apk add gcc python3-dev musl-dev linux-headers
27+
RUN mkdir workspace && pip install --target workspace -r requirements.txt
28+
2029
FROM busybox
2130

2231
COPY --from=build /operator-build/workspace /autoinstrumentation
32+
COPY --from=build-musl /operator-build/workspace /autoinstrumentation-musl
2333

2434
RUN chmod -R go+r /autoinstrumentation
35+
RUN chmod -R go+r /autoinstrumentation-musl

pkg/instrumentation/annotation.go

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
annotationInjectNodeJSContainersName = "instrumentation.opentelemetry.io/nodejs-container-names"
3131
annotationInjectPython = "instrumentation.opentelemetry.io/inject-python"
3232
annotationInjectPythonContainersName = "instrumentation.opentelemetry.io/python-container-names"
33+
annotationPythonWheelKind = "instrumentation.opentelemetry.io/python-wheel-kind"
3334
annotationInjectDotNet = "instrumentation.opentelemetry.io/inject-dotnet"
3435
annotationDotNetRuntime = "instrumentation.opentelemetry.io/otel-dotnet-auto-runtime"
3536
annotationInjectDotnetContainersName = "instrumentation.opentelemetry.io/dotnet-container-names"

pkg/instrumentation/podmutator.go

+1
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c
320320
}
321321
if pm.config.EnablePythonAutoInstrumentation() || inst == nil {
322322
insts.Python.Instrumentation = inst
323+
insts.Python.AdditionalAnnotations = map[string]string{annotationPythonWheelKind: annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationPythonWheelKind)}
323324
} else {
324325
logger.Error(nil, "support for Python auto instrumentation is not enabled")
325326
pm.Recorder.Event(pod.DeepCopy(), "Warning", "InstrumentationRequestRejected", "support for Python auto instrumentation is not enabled")

pkg/instrumentation/python.go

+25-11
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,22 @@ import (
2323
)
2424

2525
const (
26-
envPythonPath = "PYTHONPATH"
27-
envOtelTracesExporter = "OTEL_TRACES_EXPORTER"
28-
envOtelMetricsExporter = "OTEL_METRICS_EXPORTER"
29-
envOtelExporterOTLPProtocol = "OTEL_EXPORTER_OTLP_PROTOCOL"
30-
pythonPathPrefix = "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation"
31-
pythonPathSuffix = "/otel-auto-instrumentation-python"
32-
pythonInstrMountPath = "/otel-auto-instrumentation-python"
33-
pythonVolumeName = volumeName + "-python"
34-
pythonInitContainerName = initContainerName + "-python"
26+
envPythonPath = "PYTHONPATH"
27+
envOtelTracesExporter = "OTEL_TRACES_EXPORTER"
28+
envOtelMetricsExporter = "OTEL_METRICS_EXPORTER"
29+
envOtelExporterOTLPProtocol = "OTEL_EXPORTER_OTLP_PROTOCOL"
30+
manyLinuxAutoInstrumentationSrc = "/autoinstrumentation/."
31+
muslLinuxAutoInstrumentationSrc = "/autoinstrumentation-musl/."
32+
pythonPathPrefix = "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation"
33+
pythonPathSuffix = "/otel-auto-instrumentation-python"
34+
pythonInstrMountPath = "/otel-auto-instrumentation-python"
35+
pythonVolumeName = volumeName + "-python"
36+
pythonInitContainerName = initContainerName + "-python"
37+
wheelKindManyLinux = "manylinux"
38+
wheelKindMuslLinux = "musllinux"
3539
)
3640

37-
func injectPythonSDK(pythonSpec v1alpha1.Python, pod corev1.Pod, index int) (corev1.Pod, error) {
41+
func injectPythonSDK(pythonSpec v1alpha1.Python, pod corev1.Pod, index int, wheelKind string) (corev1.Pod, error) {
3842
// caller checks if there is at least one container.
3943
container := &pod.Spec.Containers[index]
4044

@@ -43,6 +47,16 @@ func injectPythonSDK(pythonSpec v1alpha1.Python, pod corev1.Pod, index int) (cor
4347
return pod, err
4448
}
4549

50+
autoInstrumentationSrc := ""
51+
switch wheelKind {
52+
case "", wheelKindManyLinux:
53+
autoInstrumentationSrc = manyLinuxAutoInstrumentationSrc
54+
case wheelKindMuslLinux:
55+
autoInstrumentationSrc = muslLinuxAutoInstrumentationSrc
56+
default:
57+
return pod, fmt.Errorf("provided instrumentation.opentelemetry.io/python-wheel-kind annotation value '%s' is not supported", wheelKind)
58+
}
59+
4660
// inject Python instrumentation spec env vars.
4761
for _, env := range pythonSpec.Env {
4862
idx := getIndexOfEnv(container.Env, env.Name)
@@ -106,7 +120,7 @@ func injectPythonSDK(pythonSpec v1alpha1.Python, pod corev1.Pod, index int) (cor
106120
pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{
107121
Name: pythonInitContainerName,
108122
Image: pythonSpec.Image,
109-
Command: []string{"cp", "-r", "/autoinstrumentation/.", pythonInstrMountPath},
123+
Command: []string{"cp", "-r", autoInstrumentationSrc, pythonInstrMountPath},
110124
Resources: pythonSpec.Resources,
111125
VolumeMounts: []corev1.VolumeMount{{
112126
Name: pythonVolumeName,

pkg/instrumentation/python_test.go

+160-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestInjectPythonSDK(t *testing.T) {
2929
name string
3030
v1alpha1.Python
3131
pod corev1.Pod
32+
wheelKind string
3233
expected corev1.Pod
3334
err error
3435
}{
@@ -42,6 +43,7 @@ func TestInjectPythonSDK(t *testing.T) {
4243
},
4344
},
4445
},
46+
wheelKind: "manylinux",
4547
expected: corev1.Pod{
4648
Spec: corev1.PodSpec{
4749
Volumes: []corev1.Volume{
@@ -114,6 +116,7 @@ func TestInjectPythonSDK(t *testing.T) {
114116
},
115117
},
116118
},
119+
wheelKind: "manylinux",
117120
expected: corev1.Pod{
118121
Spec: corev1.PodSpec{
119122
Volumes: []corev1.Volume{
@@ -187,6 +190,7 @@ func TestInjectPythonSDK(t *testing.T) {
187190
},
188191
},
189192
},
193+
wheelKind: "manylinux",
190194
expected: corev1.Pod{
191195
Spec: corev1.PodSpec{
192196
Volumes: []corev1.Volume{
@@ -259,6 +263,7 @@ func TestInjectPythonSDK(t *testing.T) {
259263
},
260264
},
261265
},
266+
wheelKind: "manylinux",
262267
expected: corev1.Pod{
263268
Spec: corev1.PodSpec{
264269
Volumes: []corev1.Volume{
@@ -331,6 +336,7 @@ func TestInjectPythonSDK(t *testing.T) {
331336
},
332337
},
333338
},
339+
wheelKind: "manylinux",
334340
expected: corev1.Pod{
335341
Spec: corev1.PodSpec{
336342
Volumes: []corev1.Volume{
@@ -403,6 +409,7 @@ func TestInjectPythonSDK(t *testing.T) {
403409
},
404410
},
405411
},
412+
wheelKind: "manylinux",
406413
expected: corev1.Pod{
407414
Spec: corev1.PodSpec{
408415
Containers: []corev1.Container{
@@ -419,11 +426,163 @@ func TestInjectPythonSDK(t *testing.T) {
419426
},
420427
err: fmt.Errorf("the container defines env var value via ValueFrom, envVar: %s", envPythonPath),
421428
},
429+
{
430+
name: "musllinux wheelKind defined",
431+
Python: v1alpha1.Python{Image: "foo/bar:1"},
432+
pod: corev1.Pod{
433+
Spec: corev1.PodSpec{
434+
Containers: []corev1.Container{
435+
{},
436+
},
437+
},
438+
},
439+
wheelKind: "musllinux",
440+
expected: corev1.Pod{
441+
Spec: corev1.PodSpec{
442+
Volumes: []corev1.Volume{
443+
{
444+
Name: pythonVolumeName,
445+
VolumeSource: corev1.VolumeSource{
446+
EmptyDir: &corev1.EmptyDirVolumeSource{
447+
SizeLimit: &defaultVolumeLimitSize,
448+
},
449+
},
450+
},
451+
},
452+
InitContainers: []corev1.Container{
453+
{
454+
Name: "opentelemetry-auto-instrumentation-python",
455+
Image: "foo/bar:1",
456+
Command: []string{"cp", "-r", "/autoinstrumentation-musl/.", "/otel-auto-instrumentation-python"},
457+
VolumeMounts: []corev1.VolumeMount{{
458+
Name: "opentelemetry-auto-instrumentation-python",
459+
MountPath: "/otel-auto-instrumentation-python",
460+
}},
461+
},
462+
},
463+
Containers: []corev1.Container{
464+
{
465+
VolumeMounts: []corev1.VolumeMount{
466+
{
467+
Name: "opentelemetry-auto-instrumentation-python",
468+
MountPath: "/otel-auto-instrumentation-python",
469+
},
470+
},
471+
Env: []corev1.EnvVar{
472+
{
473+
Name: "PYTHONPATH",
474+
Value: fmt.Sprintf("%s:%s", "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation", "/otel-auto-instrumentation-python"),
475+
},
476+
{
477+
Name: "OTEL_EXPORTER_OTLP_PROTOCOL",
478+
Value: "http/protobuf",
479+
},
480+
{
481+
Name: "OTEL_TRACES_EXPORTER",
482+
Value: "otlp",
483+
},
484+
{
485+
Name: "OTEL_METRICS_EXPORTER",
486+
Value: "otlp",
487+
},
488+
},
489+
},
490+
},
491+
},
492+
},
493+
err: nil,
494+
},
495+
{
496+
name: "wheelKind not defined",
497+
Python: v1alpha1.Python{Image: "foo/bar:1"},
498+
pod: corev1.Pod{
499+
Spec: corev1.PodSpec{
500+
Containers: []corev1.Container{
501+
{},
502+
},
503+
},
504+
},
505+
wheelKind: "",
506+
expected: corev1.Pod{
507+
Spec: corev1.PodSpec{
508+
Volumes: []corev1.Volume{
509+
{
510+
Name: pythonVolumeName,
511+
VolumeSource: corev1.VolumeSource{
512+
EmptyDir: &corev1.EmptyDirVolumeSource{
513+
SizeLimit: &defaultVolumeLimitSize,
514+
},
515+
},
516+
},
517+
},
518+
InitContainers: []corev1.Container{
519+
{
520+
Name: "opentelemetry-auto-instrumentation-python",
521+
Image: "foo/bar:1",
522+
Command: []string{"cp", "-r", "/autoinstrumentation/.", "/otel-auto-instrumentation-python"},
523+
VolumeMounts: []corev1.VolumeMount{{
524+
Name: "opentelemetry-auto-instrumentation-python",
525+
MountPath: "/otel-auto-instrumentation-python",
526+
}},
527+
},
528+
},
529+
Containers: []corev1.Container{
530+
{
531+
VolumeMounts: []corev1.VolumeMount{
532+
{
533+
Name: "opentelemetry-auto-instrumentation-python",
534+
MountPath: "/otel-auto-instrumentation-python",
535+
},
536+
},
537+
Env: []corev1.EnvVar{
538+
{
539+
Name: "PYTHONPATH",
540+
Value: fmt.Sprintf("%s:%s", "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation", "/otel-auto-instrumentation-python"),
541+
},
542+
{
543+
Name: "OTEL_EXPORTER_OTLP_PROTOCOL",
544+
Value: "http/protobuf",
545+
},
546+
{
547+
Name: "OTEL_TRACES_EXPORTER",
548+
Value: "otlp",
549+
},
550+
{
551+
Name: "OTEL_METRICS_EXPORTER",
552+
Value: "otlp",
553+
},
554+
},
555+
},
556+
},
557+
},
558+
},
559+
err: nil,
560+
},
561+
{
562+
name: "wheelKind not supported",
563+
Python: v1alpha1.Python{Image: "foo/bar:1"},
564+
pod: corev1.Pod{
565+
Spec: corev1.PodSpec{
566+
Containers: []corev1.Container{
567+
{},
568+
},
569+
},
570+
},
571+
wheelKind: "not supported",
572+
expected: corev1.Pod{
573+
Spec: corev1.PodSpec{
574+
Containers: []corev1.Container{
575+
{},
576+
},
577+
},
578+
},
579+
err: fmt.Errorf("provided instrumentation.opentelemetry.io/python-wheel-kind annotation value 'not supported' is not supported"),
580+
},
422581
}
423582

424583
for _, test := range tests {
425584
t.Run(test.name, func(t *testing.T) {
426-
pod, err := injectPythonSDK(test.Python, test.pod, 0)
585+
pod, err := injectPythonSDK(test.Python, test.pod, 0, test.wheelKind)
427586
assert.Equal(t, test.expected, pod)
428587
assert.Equal(t, test.err, err)
429588
})

pkg/instrumentation/sdk.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations
110110

111111
for _, container := range insts.Python.Containers {
112112
index := getContainerIndex(container, pod)
113-
pod, err = injectPythonSDK(otelinst.Spec.Python, pod, index)
113+
pod, err = injectPythonSDK(otelinst.Spec.Python, pod, index, insts.Python.AdditionalAnnotations[annotationPythonWheelKind])
114114
if err != nil {
115115
i.logger.Info("Skipping Python SDK injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name)
116116
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
apiVersion: opentelemetry.io/v1alpha1
2+
kind: OpenTelemetryCollector
3+
metadata:
4+
name: sidecar
5+
spec:
6+
config: |
7+
receivers:
8+
otlp:
9+
protocols:
10+
grpc:
11+
http:
12+
processors:
13+
14+
exporters:
15+
debug:
16+
17+
service:
18+
pipelines:
19+
traces:
20+
receivers: [otlp]
21+
exporters: [debug]
22+
mode: sidecar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
apiVersion: opentelemetry.io/v1alpha1
2+
kind: Instrumentation
3+
metadata:
4+
name: python-musl
5+
spec:
6+
env:
7+
- name: OTEL_EXPORTER_OTLP_TIMEOUT
8+
value: "20"
9+
- name: OTEL_TRACES_SAMPLER
10+
value: parentbased_traceidratio
11+
- name: OTEL_TRACES_SAMPLER_ARG
12+
value: "0.85"
13+
- name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED
14+
value: "true"
15+
exporter:
16+
endpoint: http://localhost:4317
17+
propagators:
18+
- jaeger
19+
- b3
20+
sampler:
21+
type: parentbased_traceidratio
22+
argument: "0.25"
23+
python:
24+
env:
25+
- name: OTEL_LOG_LEVEL
26+
value: "debug"
27+
- name: OTEL_TRACES_EXPORTER
28+
value: otlp
29+
- name: OTEL_EXPORTER_OTLP_ENDPOINT
30+
value: http://localhost:4318

0 commit comments

Comments
 (0)