Skip to content

Commit d9dab88

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 open-telemetry#2264
1 parent e9936df commit d9dab88

File tree

11 files changed

+404
-18
lines changed

11 files changed

+404
-18
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
@@ -321,6 +321,7 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c
321321
}
322322
if pm.config.EnablePythonAutoInstrumentation() || inst == nil {
323323
insts.Python.Instrumentation = inst
324+
insts.Python.AdditionalAnnotations = map[string]string{annotationPythonWheelKind: annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationPythonWheelKind)}
324325
} else {
325326
logger.Error(nil, "support for Python auto instrumentation is not enabled")
326327
pm.Recorder.Event(pod.DeepCopy(), "Warning", "InstrumentationRequestRejected", "support for Python auto instrumentation is not enabled")

pkg/instrumentation/python.go

+26-12
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,23 @@ import (
2323
)
2424

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

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

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

51+
autoInstrumentationSrc := ""
52+
switch wheelKind {
53+
case "", wheelKindManyLinux:
54+
autoInstrumentationSrc = manyLinuxAutoInstrumentationSrc
55+
case wheelKindMuslLinux:
56+
autoInstrumentationSrc = muslLinuxAutoInstrumentationSrc
57+
default:
58+
return pod, fmt.Errorf("provided instrumentation.opentelemetry.io/python-wheel-kind annotation value '%s' is not supported", wheelKind)
59+
}
60+
4761
// inject Python instrumentation spec env vars.
4862
for _, env := range pythonSpec.Env {
4963
idx := getIndexOfEnv(container.Env, env.Name)
@@ -116,7 +130,7 @@ func injectPythonSDK(pythonSpec v1alpha1.Python, pod corev1.Pod, index int) (cor
116130
pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{
117131
Name: pythonInitContainerName,
118132
Image: pythonSpec.Image,
119-
Command: []string{"cp", "-r", "/autoinstrumentation/.", pythonInstrMountPath},
133+
Command: []string{"cp", "-r", autoInstrumentationSrc, pythonInstrMountPath},
120134
Resources: pythonSpec.Resources,
121135
VolumeMounts: []corev1.VolumeMount{{
122136
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{
@@ -118,6 +120,7 @@ func TestInjectPythonSDK(t *testing.T) {
118120
},
119121
},
120122
},
123+
wheelKind: "manylinux",
121124
expected: corev1.Pod{
122125
Spec: corev1.PodSpec{
123126
Volumes: []corev1.Volume{
@@ -195,6 +198,7 @@ func TestInjectPythonSDK(t *testing.T) {
195198
},
196199
},
197200
},
201+
wheelKind: "manylinux",
198202
expected: corev1.Pod{
199203
Spec: corev1.PodSpec{
200204
Volumes: []corev1.Volume{
@@ -271,6 +275,7 @@ func TestInjectPythonSDK(t *testing.T) {
271275
},
272276
},
273277
},
278+
wheelKind: "manylinux",
274279
expected: corev1.Pod{
275280
Spec: corev1.PodSpec{
276281
Volumes: []corev1.Volume{
@@ -423,6 +428,7 @@ func TestInjectPythonSDK(t *testing.T) {
423428
},
424429
},
425430
},
431+
wheelKind: "manylinux",
426432
expected: corev1.Pod{
427433
Spec: corev1.PodSpec{
428434
Volumes: []corev1.Volume{
@@ -499,6 +505,7 @@ func TestInjectPythonSDK(t *testing.T) {
499505
},
500506
},
501507
},
508+
wheelKind: "manylinux",
502509
expected: corev1.Pod{
503510
Spec: corev1.PodSpec{
504511
Containers: []corev1.Container{
@@ -515,11 +522,163 @@ func TestInjectPythonSDK(t *testing.T) {
515522
},
516523
err: fmt.Errorf("the container defines env var value via ValueFrom, envVar: %s", envPythonPath),
517524
},
525+
{
526+
name: "musllinux wheelKind defined",
527+
Python: v1alpha1.Python{Image: "foo/bar:1"},
528+
pod: corev1.Pod{
529+
Spec: corev1.PodSpec{
530+
Containers: []corev1.Container{
531+
{},
532+
},
533+
},
534+
},
535+
wheelKind: "musllinux",
536+
expected: corev1.Pod{
537+
Spec: corev1.PodSpec{
538+
Volumes: []corev1.Volume{
539+
{
540+
Name: pythonVolumeName,
541+
VolumeSource: corev1.VolumeSource{
542+
EmptyDir: &corev1.EmptyDirVolumeSource{
543+
SizeLimit: &defaultVolumeLimitSize,
544+
},
545+
},
546+
},
547+
},
548+
InitContainers: []corev1.Container{
549+
{
550+
Name: "opentelemetry-auto-instrumentation-python",
551+
Image: "foo/bar:1",
552+
Command: []string{"cp", "-r", "/autoinstrumentation-musl/.", "/otel-auto-instrumentation-python"},
553+
VolumeMounts: []corev1.VolumeMount{{
554+
Name: "opentelemetry-auto-instrumentation-python",
555+
MountPath: "/otel-auto-instrumentation-python",
556+
}},
557+
},
558+
},
559+
Containers: []corev1.Container{
560+
{
561+
VolumeMounts: []corev1.VolumeMount{
562+
{
563+
Name: "opentelemetry-auto-instrumentation-python",
564+
MountPath: "/otel-auto-instrumentation-python",
565+
},
566+
},
567+
Env: []corev1.EnvVar{
568+
{
569+
Name: "PYTHONPATH",
570+
Value: fmt.Sprintf("%s:%s", "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation", "/otel-auto-instrumentation-python"),
571+
},
572+
{
573+
Name: "OTEL_EXPORTER_OTLP_PROTOCOL",
574+
Value: "http/protobuf",
575+
},
576+
{
577+
Name: "OTEL_TRACES_EXPORTER",
578+
Value: "otlp",
579+
},
580+
{
581+
Name: "OTEL_METRICS_EXPORTER",
582+
Value: "otlp",
583+
},
584+
},
585+
},
586+
},
587+
},
588+
},
589+
err: nil,
590+
},
591+
{
592+
name: "wheelKind not defined",
593+
Python: v1alpha1.Python{Image: "foo/bar:1"},
594+
pod: corev1.Pod{
595+
Spec: corev1.PodSpec{
596+
Containers: []corev1.Container{
597+
{},
598+
},
599+
},
600+
},
601+
wheelKind: "",
602+
expected: corev1.Pod{
603+
Spec: corev1.PodSpec{
604+
Volumes: []corev1.Volume{
605+
{
606+
Name: pythonVolumeName,
607+
VolumeSource: corev1.VolumeSource{
608+
EmptyDir: &corev1.EmptyDirVolumeSource{
609+
SizeLimit: &defaultVolumeLimitSize,
610+
},
611+
},
612+
},
613+
},
614+
InitContainers: []corev1.Container{
615+
{
616+
Name: "opentelemetry-auto-instrumentation-python",
617+
Image: "foo/bar:1",
618+
Command: []string{"cp", "-r", "/autoinstrumentation/.", "/otel-auto-instrumentation-python"},
619+
VolumeMounts: []corev1.VolumeMount{{
620+
Name: "opentelemetry-auto-instrumentation-python",
621+
MountPath: "/otel-auto-instrumentation-python",
622+
}},
623+
},
624+
},
625+
Containers: []corev1.Container{
626+
{
627+
VolumeMounts: []corev1.VolumeMount{
628+
{
629+
Name: "opentelemetry-auto-instrumentation-python",
630+
MountPath: "/otel-auto-instrumentation-python",
631+
},
632+
},
633+
Env: []corev1.EnvVar{
634+
{
635+
Name: "PYTHONPATH",
636+
Value: fmt.Sprintf("%s:%s", "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation", "/otel-auto-instrumentation-python"),
637+
},
638+
{
639+
Name: "OTEL_EXPORTER_OTLP_PROTOCOL",
640+
Value: "http/protobuf",
641+
},
642+
{
643+
Name: "OTEL_TRACES_EXPORTER",
644+
Value: "otlp",
645+
},
646+
{
647+
Name: "OTEL_METRICS_EXPORTER",
648+
Value: "otlp",
649+
},
650+
},
651+
},
652+
},
653+
},
654+
},
655+
err: nil,
656+
},
657+
{
658+
name: "wheelKind not supported",
659+
Python: v1alpha1.Python{Image: "foo/bar:1"},
660+
pod: corev1.Pod{
661+
Spec: corev1.PodSpec{
662+
Containers: []corev1.Container{
663+
{},
664+
},
665+
},
666+
},
667+
wheelKind: "not supported",
668+
expected: corev1.Pod{
669+
Spec: corev1.PodSpec{
670+
Containers: []corev1.Container{
671+
{},
672+
},
673+
},
674+
},
675+
err: fmt.Errorf("provided instrumentation.opentelemetry.io/python-wheel-kind annotation value 'not supported' is not supported"),
676+
},
518677
}
519678

520679
for _, test := range tests {
521680
t.Run(test.name, func(t *testing.T) {
522-
pod, err := injectPythonSDK(test.Python, test.pod, 0)
681+
pod, err := injectPythonSDK(test.Python, test.pod, 0, test.wheelKind)
523682
assert.Equal(t, test.expected, pod)
524683
assert.Equal(t, test.err, err)
525684
})

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)