Skip to content

Commit aa543db

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 caded11 commit aa543db

File tree

12 files changed

+406
-22
lines changed

12 files changed

+406
-22
lines changed

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+
annotationPythonPlatform = "instrumentation.opentelemetry.io/otel-python-platform"
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{annotationPythonPlatform: annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationPythonPlatform)}
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+
glibcLinuxAutoInstrumentationSrc = "/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+
glibcLinux = "glibc"
39+
muslLinux = "musl"
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, platform string) (corev1.Pod, error) {
3943
volume := instrVolume(pythonSpec.VolumeClaimTemplate, pythonVolumeName, pythonSpec.VolumeSizeLimit)
4044

4145
// caller checks if there is at least one container.
@@ -46,6 +50,16 @@ func injectPythonSDK(pythonSpec v1alpha1.Python, pod corev1.Pod, index int) (cor
4650
return pod, err
4751
}
4852

53+
autoInstrumentationSrc := ""
54+
switch platform {
55+
case "", glibcLinux:
56+
autoInstrumentationSrc = glibcLinuxAutoInstrumentationSrc
57+
case muslLinux:
58+
autoInstrumentationSrc = muslLinuxAutoInstrumentationSrc
59+
default:
60+
return pod, fmt.Errorf("provided instrumentation.opentelemetry.io/otel-python-platform annotation value '%s' is not supported", platform)
61+
}
62+
4963
// inject Python instrumentation spec env vars.
5064
for _, env := range pythonSpec.Env {
5165
idx := getIndexOfEnv(container.Env, env.Name)
@@ -111,7 +125,7 @@ func injectPythonSDK(pythonSpec v1alpha1.Python, pod corev1.Pod, index int) (cor
111125
pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{
112126
Name: pythonInitContainerName,
113127
Image: pythonSpec.Image,
114-
Command: []string{"cp", "-r", "/autoinstrumentation/.", pythonInstrMountPath},
128+
Command: []string{"cp", "-r", autoInstrumentationSrc, pythonInstrMountPath},
115129
Resources: pythonSpec.Resources,
116130
VolumeMounts: []corev1.VolumeMount{{
117131
Name: volume.Name,

pkg/instrumentation/python_test.go

+168-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+
platform string
3233
expected corev1.Pod
3334
err error
3435
}{
@@ -42,6 +43,7 @@ func TestInjectPythonSDK(t *testing.T) {
4243
},
4344
},
4445
},
46+
platform: "glibc",
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+
platform: "glibc",
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+
platform: "glibc",
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+
platform: "glibc",
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+
platform: "glibc",
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+
platform: "glibc",
502509
expected: corev1.Pod{
503510
Spec: corev1.PodSpec{
504511
Containers: []corev1.Container{
@@ -515,11 +522,171 @@ 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: "musl platform 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+
platform: "musl",
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+
Name: "OTEL_LOGS_EXPORTER",
586+
Value: "otlp",
587+
},
588+
},
589+
},
590+
},
591+
},
592+
},
593+
err: nil,
594+
},
595+
{
596+
name: "platform not defined",
597+
Python: v1alpha1.Python{Image: "foo/bar:1"},
598+
pod: corev1.Pod{
599+
Spec: corev1.PodSpec{
600+
Containers: []corev1.Container{
601+
{},
602+
},
603+
},
604+
},
605+
platform: "",
606+
expected: corev1.Pod{
607+
Spec: corev1.PodSpec{
608+
Volumes: []corev1.Volume{
609+
{
610+
Name: pythonVolumeName,
611+
VolumeSource: corev1.VolumeSource{
612+
EmptyDir: &corev1.EmptyDirVolumeSource{
613+
SizeLimit: &defaultVolumeLimitSize,
614+
},
615+
},
616+
},
617+
},
618+
InitContainers: []corev1.Container{
619+
{
620+
Name: "opentelemetry-auto-instrumentation-python",
621+
Image: "foo/bar:1",
622+
Command: []string{"cp", "-r", "/autoinstrumentation/.", "/otel-auto-instrumentation-python"},
623+
VolumeMounts: []corev1.VolumeMount{{
624+
Name: "opentelemetry-auto-instrumentation-python",
625+
MountPath: "/otel-auto-instrumentation-python",
626+
}},
627+
},
628+
},
629+
Containers: []corev1.Container{
630+
{
631+
VolumeMounts: []corev1.VolumeMount{
632+
{
633+
Name: "opentelemetry-auto-instrumentation-python",
634+
MountPath: "/otel-auto-instrumentation-python",
635+
},
636+
},
637+
Env: []corev1.EnvVar{
638+
{
639+
Name: "PYTHONPATH",
640+
Value: fmt.Sprintf("%s:%s", "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation", "/otel-auto-instrumentation-python"),
641+
},
642+
{
643+
Name: "OTEL_EXPORTER_OTLP_PROTOCOL",
644+
Value: "http/protobuf",
645+
},
646+
{
647+
Name: "OTEL_TRACES_EXPORTER",
648+
Value: "otlp",
649+
},
650+
{
651+
Name: "OTEL_METRICS_EXPORTER",
652+
Value: "otlp",
653+
},
654+
{
655+
Name: "OTEL_LOGS_EXPORTER",
656+
Value: "otlp",
657+
},
658+
},
659+
},
660+
},
661+
},
662+
},
663+
err: nil,
664+
},
665+
{
666+
name: "platform not supported",
667+
Python: v1alpha1.Python{Image: "foo/bar:1"},
668+
pod: corev1.Pod{
669+
Spec: corev1.PodSpec{
670+
Containers: []corev1.Container{
671+
{},
672+
},
673+
},
674+
},
675+
platform: "not-supported",
676+
expected: corev1.Pod{
677+
Spec: corev1.PodSpec{
678+
Containers: []corev1.Container{
679+
{},
680+
},
681+
},
682+
},
683+
err: fmt.Errorf("provided instrumentation.opentelemetry.io/otel-python-platform annotation value 'not-supported' is not supported"),
684+
},
518685
}
519686

520687
for _, test := range tests {
521688
t.Run(test.name, func(t *testing.T) {
522-
pod, err := injectPythonSDK(test.Python, test.pod, 0)
689+
pod, err := injectPythonSDK(test.Python, test.pod, 0, test.platform)
523690
assert.Equal(t, test.expected, pod)
524691
assert.Equal(t, test.err, err)
525692
})

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[annotationPythonPlatform])
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)