# 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**.
