Python Instrumentation in Containers

Instrumentation of Python applications in containers with the OpenTelemetry agent

This documentation explains how to instrument Python applications in containers using the zero-code model, with the official OpenTelemetry agent, adapted for use with Elven Observability. The idea is to ensure the collection of traces (and optionally metrics) without code changes, only by adjusting the Dockerfile and environment variables.

Agent installation in the Dockerfile

Add only the necessary packages to the Dockerfile according to your framework. See below for the common packages and those specific to each framework:

Common packages (always install):

RUN pip install \
    opentelemetry-distro \
    opentelemetry-exporter-otlp \
    opentelemetry-instrumentation-requests

FastAPI:

RUN pip install opentelemetry-instrumentation-fastapi

Flask:

RUN pip install opentelemetry-instrumentation-flask

Django:

RUN pip install opentelemetry-instrumentation-django

Important: Do not install unnecessary instrumentations. Ex: apps using Django do not need opentelemetry-instrumentation-fastapi.

Example Dockerfile:

FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV DJANGO_SETTINGS_MODULE=config.settings.base

WORKDIR /app

RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    netcat-openbsd \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Install OpenTelemetry   
RUN pip install opentelemetry-distro \
    opentelemetry-exporter-otlp \
    opentelemetry-instrumentation \
    opentelemetry-instrumentation-django \
    opentelemetry-instrumentation-requests

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["opentelemetry-instrument", "--", "gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]

Environment Variables Configuration

Set the following variables to enable automatic instrumentation and send data to the Elven collector:

- name: OTEL_SERVICE_NAME
  value: "python-app"

- name: OTEL_RESOURCE_ATTRIBUTES
  value: "service=python-app,environment=production"

- name: OTEL_TRACES_EXPORTER
  value: "otlp"

- name: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
  value: "http://your_collector_endpoint.com:4317/v1/traces"

- name: OTEL_METRICS_EXPORTER
  value: "otlp"

- name: OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
  value: "http://your_collector_endpoint.com:4317/v1/metrics"

Explanation:

  • OTEL_SERVICE_NAME: name of the service displayed in Grafana/Tempo.

  • OTEL_RESOURCE_ATTRIBUTES: extra tags to enrich the traces.

  • OTEL_EXPORTER_OTLP_*: endpoints for sending the data.

How to start the app with the agent (CMD)

Use the opentelemetry-instrument command as a wrapper for the application. See below for examples by framework:

FastAPI (with Uvicorn)

CMD ["opentelemetry-instrument", "--", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Flask

CMD ["opentelemetry-instrument", "--", "python", "app.py"]

Django

CMD ["opentelemetry-instrument", "--", "python", "manage.py", "runserver", "0.0.0.0:8000"]

Info: You can adapt the command according to your entrypoint: gunicorn, uvicorn, waitress, etc.

Examples by Environment

Docker Compose

services:
  api:
    build: .
    environment:
      - OTEL_SERVICE_NAME=python-app
      - OTEL_RESOURCE_ATTRIBUTES=service=python-app,environment=production
      - OTEL_TRACES_EXPORTER=otlp
      - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://your_collector_endpoint.com:4317/v1/traces
      - OTEL_METRICS_EXPORTER=otlp
      - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://your_collector_endpoint.com:4317/v1/metrics

Kubernetes

spec:
  containers:
    - name: python-app
      image: python-app:latest
      env:
        - name: OTEL_SERVICE_NAME
          value: "python-app"
        - name: OTEL_RESOURCE_ATTRIBUTES
          value: "service=python-app,environment=production"
        - name: OTEL_TRACES_EXPORTER
          value: "otlp"
        - name: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
          value: "http://your_collector_endpoint.com:4317/v1/traces"
        - name: OTEL_METRICS_EXPORTER
          value: "otlp"
        - name: OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
          value: "http://your_collector_endpoint.com:4317/v1/metrics" 

ECS (Fargate ou EC2)

"environment": [
  { "name": "OTEL_SERVICE_NAME", "value": "python-app" },
  { "name": "OTEL_RESOURCE_ATTRIBUTES", "value": "service=python-app,environment=production" },
  { "name": "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "value": "http://your_collector_endpoint.com:4317/v1/traces" },
  { "name": "OTEL_TRACES_EXPORTER", "value": "otlp" },
  { "name": "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", "value": "http://your_collector_endpoint.com:4317/v1/metrics" },
  { "name": "OTEL_METRICS_EXPORTER", "value": "otlp" }
]

Troubleshooting

  • Traces not showing: check if the endpoint is accessible and if the CMD uses opentelemetry-instrument correctly.

  • Application timeout: verify if the collector is online and responding on port 4318.

  • Duplicate instrumentation: avoid mixing manual instrumentation with the automatic mode.

  • Dependency error: each framework requires its specific instrumentation, review the pip install.

Best Practices

  • Give meaningful names to services (OTEL_SERVICE_NAME=projeto-env-modulo).

  • Separate environments (environment=dev, staging, production).

  • Do not expose credentials in environment variables.

  • Do not use OTEL_LOG_LEVEL=debug in production.

  • Test locally with otel-cli ou curl to validate the collector.

Example of .env

OTEL_SERVICE_NAME=python-app
OTEL_RESOURCE_ATTRIBUTES=service=python-app,environment=production
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://your_collector_endpoint.com:4317/v1/traces
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://your_collector_endpoint.com:4317/v1/metrics
OTEL_METRICS_EXPORTER=otlp

Your Python app is now instrumented with Elven Observability, using auto-instrumentation based on OpenTelemetry.

Last updated

Was this helpful?