-
-
Notifications
You must be signed in to change notification settings - Fork 240
Description
Bug: OpenLIT Operator does not support ABI-matched auto-instrumentation for multiple Python versions
Component
OpenLIT Operator
What happened?
When the OpenLIT Operator injects the default auto-instrumentation image (built for Python 3.11) into workloads running other Python versions (e.g., 3.12, 3.13), any application using C-extensions (such as Pydantic, numpy, or other AI libraries) frequently fails with:
ModuleNotFoundError: No module named 'pydantic_core._pydantic_core'
The cause is that the instrumentation image installs .so files (binary extensions) built for Python 3.11, but the workload’s interpreter expects files for a different version (ABI mismatch). This is especially problematic in clusters with mixed Python versions, where some apps use Python 3.11 and others use newer versions.
Where the Default Instrumentation Image Uses Python 3.11
The official OpenLIT auto-instrumentation image is built using Python 3.11 as specified in its Dockerfile:
######### OpenLIT Instrumentation Image #########
FROM python:3.11-slim AS openlit-builder
...
# Create final OpenLIT image
FROM python:3.11-slim
...All compiled packages inside this image are built for the Python 3.11 ABI. If the application workload in your pod runs a different Python version, binary imports will fail due to ABI incompatibility.
Steps to Reproduce
- Deploy a workload using Python 3.12 or 3.13 in a cluster with the OpenLIT Operator and the default OpenLIT instrumentation provider.
- Trigger any request or flow that uses C-extension libraries (such as Pydantic).
- Observe errors like:
ModuleNotFoundError: No module named 'pydantic_core._pydantic_core'
Expected Behavior
- The OpenLIT Operator should inject an ABI-compatible instrumentation image matching the Python version of each workload, avoiding these errors.
- Users should not need to manually build or maintain custom instrumentation images for each Python release.
Current Workarounds
Manual: Build and publish a custom OpenLIT instrumentation image per Python version, then reference it in the CR with customInitImage:
apiVersion: openlit.io/v1alpha1
kind: AutoInstrumentation
spec:
python:
instrumentation:
provider: openlit
customInitImage: myrepo/openlit-auto-instrumentation:py312However, this workaround does not scale for large clusters or organizations with mixed Python dependencies.
Proposed Solution
Option 1: Official ABI-Matched Images (User-Selected)
- OpenLIT publishes
openlit/auto-instrumentation:py311,:py312,:py313,:py314, etc. - Users select the appropriate image in their AutoInstrumentation CR:
apiVersion: openlit.io/v1alpha1
kind: AutoInstrumentation
spec:
python:
instrumentation:
provider: openlit
customInitImage: openlit/auto-instrumentation:py312Option 2: Operator Autodetection (Recommended, Zero-Config for User)
- Operator is enhanced to detect the Python version from:
- The
PYTHON_VERSIONenvironment variable in the workload container, e.g.:containers: - name: my-app env: - name: PYTHON_VERSION value: "3.13"
- Or, parse the Python version from the image tag, e.g.:
containers: - name: my-app image: python:3.12-slim
- The
- It then automatically injects the matching ABI image:
openlit/auto-instrumentation:py312orpy313as appropriate.
Example (Go) Operator Logic
// detectPythonVersion returns the python version string ("3.12") based on env or image tag
func (i *OpenLitInjector) detectPythonVersion(container *corev1.Container) string {
for _, env := range container.Env {
if env.Name == "PYTHON_VERSION" && env.Value != "" {
return env.Value
}
}
if strings.HasPrefix(container.Image, "python:") {
v := strings.Split(strings.TrimPrefix(container.Image, "python:"), "-")[0]
if v != "" {
return v
}
}
return i.config.DefaultPythonVersion // fallback (e.g., "3.11")
}
// getPythonInitImage constructs the correct image tag for injection
func (i *OpenLitInjector) getPythonInitImage(container *corev1.Container) string {
version := i.detectPythonVersion(container)
tag := "py" + strings.ReplaceAll(version, ".", "")
return "openlit/auto-instrumentation:" + tag
}Example Unit Test
func TestDetectPythonVersion(t *testing.T) {
injector := &OpenLitInjector{config: &InjectorConfig{DefaultPythonVersion: "3.11"}}
// Using PYTHON_VERSION env
cont := &corev1.Container{
Env: []corev1.EnvVar{{Name: "PYTHON_VERSION", Value: "3.13"}},
}
assert.Equal(t, "3.13", injector.detectPythonVersion(cont))
assert.Equal(t, "openlit/auto-instrumentation:py313", injector.getPythonInitImage(cont))
// Using image tag
cont = &corev1.Container{Image: "python:3.12-slim"}
assert.Equal(t, "3.12", injector.detectPythonVersion(cont))
assert.Equal(t, "openlit/auto-instrumentation:py312", injector.getPythonInitImage(cont))
// Fallback default
cont = &corev1.Container{}
assert.Equal(t, "3.11", injector.detectPythonVersion(cont))
assert.Equal(t, "openlit/auto-instrumentation:py311", injector.getPythonInitImage(cont))
}Key Code Integration Points
operator/internal/injector/injector.gooperator/internal/injector/types.gooperator/internal/webhook/handler.gooperator/internal/injector/injector_test.go
Environment
- OpenLIT Operator: latest
- Python: 3.12, 3.13 (anything above 3.11)
- Kubernetes: Any supported version
Additional context
- This prevents "zero-config" auto-instrumentation for modern Python environments.
- Blocks organizations with mixed Python stacks from simple OpenLIT adoption.
- Many modern auto-instrumentation solutions (OpenTelemetry, Datadog, etc.) match the runtime ABI for this exact reason.
Pre-submission checklist
- I searched existing issues and did not find a duplicate.
- Environment details included.
- Willing to provide more technical context or example manifests if requested.
Are you willing to submit a PR?
No, but I am happy to clarify requirements further and provide code/design feedback.