DEV Community

Cover image for Stack completa de observabilidade com Docker Compose - Python, PHP, Prometheus, Loki, OpenTelemetry e Grafana
Rafael Dutra for apsis-cc

Posted on

Stack completa de observabilidade com Docker Compose - Python, PHP, Prometheus, Loki, OpenTelemetry e Grafana

1. Retomando: as peças soltas até aqui

Cada artigo desta série subiu uma peça isolada: Prometheus + Loki + Grafana no artigo 1, a mini app Python no artigo 2, a mini app PHP no artigo 3, envio de logs para o Loki no artigo 4, e instrumentação de traces (por enquanto só impressos no console) no artigo 5. Este artigo une tudo em um único docker-compose.yml, com uma peça nova: um coletor OpenTelemetry, responsável por receber os traces das mini apps via OTLP e encaminhá-los adiante.

2. Por que um coletor no meio do caminho

No artigo 5, as mini apps exportavam spans diretamente para o console (ConsoleSpanExporter). Isso prova que a instrumentação funciona, mas não é assim que uma aplicação real se comunica com uma stack de observabilidade — cada aplicação teria que saber exportar diretamente para cada backend final, duplicando configuração de endpoint, autenticação e formato em todas elas.

O OpenTelemetry Collector resolve isso ficando no meio: as aplicações exportam sempre para o mesmo lugar (o coletor, via OTLP), e é o coletor — configurado uma vez, centralmente — quem decide para onde os dados seguem depois: um backend de traces, processamento adicional (amostragem, enriquecimento de atributos), ou múltiplos destinos ao mesmo tempo. Trocar de backend passa a ser uma mudança de configuração do coletor, não um redeploy de cada aplicação instrumentada.

3. Trocando o exportador de console por OTLP

Nas duas mini apps, o único ponto que muda em relação ao artigo 5 é o exportador — a forma como os spans saem do processo — não a instrumentação em si (os tracer.start_as_current_span e spanBuilder(...)->startSpan() continuam idênticos).

# tracing.py (Python) — troca do ConsoleSpanExporter por OTLP
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
    OTLPSpanExporter,
)

provider = TracerProvider()
exporter = OTLPSpanExporter(endpoint="http://otel-collector:4318/v1/traces")
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

tracer = trace.get_tracer("python-app")
Enter fullscreen mode Exit fullscreen mode
pip install opentelemetry-exporter-otlp-proto-http
Enter fullscreen mode Exit fullscreen mode
<?php
// tracing.php (PHP) — troca do ConsoleSpanExporter por OTLP
use OpenTelemetry\Contrib\Otlp\SpanExporter;
use OpenTelemetry\SDK\Common\Transport\Http\PsrTransportFactory;

$transport = (new PsrTransportFactory())->create(
    'http://otel-collector:4318/v1/traces',
    'application/x-protobuf'
);
$exporter = new SpanExporter($transport);
$tracerProvider = new TracerProvider(new SimpleSpanProcessor($exporter));
$tracer = $tracerProvider->getTracer('php-app');
Enter fullscreen mode Exit fullscreen mode

O hostname otel-collector usado nos dois casos é o nome do serviço do Compose que aparece na seção 5.

4. Configurando o coletor

O coletor precisa de um arquivo de configuração declarando três coisas: receivers (como os dados entram), processors (transformações opcionais no meio do caminho) e exporters (para onde os dados saem):

# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:

exporters:
  debug:
    verbosity: detailed

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [debug]
Enter fullscreen mode Exit fullscreen mode

Este exemplo usa o exportador debug (o sucessor do antigo logging exporter), que imprime os traces recebidos no log do próprio coletor — suficiente para confirmar que o pipeline completo funciona de ponta a ponta: mini app → coletor → saída. Visualizar traces de forma gráfica, com uma árvore de spans navegável, exigiria um backend de tracing dedicado (como Grafana Tempo ou Jaeger) recebendo esse mesmo exportador — fora do escopo desta stack mínima, mas um próximo passo natural para quem quiser aprofundar depois desta série.

5. O docker-compose.yml unificado

Juntando tudo o que a série construiu até aqui em um único arquivo:

# docker-compose.yml
services:
  prometheus:
    image: prom/prometheus:v2.55.0
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro

  loki:
    image: grafana/loki:3.2.0
    ports:
      - "3100:3100"

  otel-collector:
    image: otel/opentelemetry-collector:0.112.0
    command: ["--config=/etc/otel-collector-config.yaml"]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro
    ports:
      - "4318:4318"

  grafana:
    image: grafana/grafana:11.3.0
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    depends_on:
      - prometheus
      - loki

  python-app:
    image: python:3.12-slim
    working_dir: /app
    volumes:
      - ./python-app:/app:ro
    command: ["sh", "-c", "pip install --no-cache-dir opentelemetry-sdk opentelemetry-exporter-otlp-proto-http && python3 app.py"]
    ports:
      - "8000:8000"
    depends_on:
      - otel-collector
      - loki

  php-app:
    image: php:8.3-cli
    working_dir: /app
    volumes:
      - ./php-app:/app
    command: ["php", "-S", "0.0.0.0:8001", "app.php"]
    ports:
      - "8001:8001"
    depends_on:
      - otel-collector
      - loki
Enter fullscreen mode Exit fullscreen mode

E o prometheus.yml reunindo os alvos dos artigos 2 e 3:

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "python-app"
    static_configs:
      - targets: ["python-app:8000"]
  - job_name: "php-app"
    static_configs:
      - targets: ["php-app:8001"]
Enter fullscreen mode Exit fullscreen mode

Subindo tudo de uma vez:

docker compose up -d
docker compose ps
Enter fullscreen mode Exit fullscreen mode

Seis serviços devem aparecer no ar: prometheus, loki, otel-collector, grafana, python-app e php-app.

6. Configurando os datasources no Grafana

Com todos os serviços na mesma rede do Compose, em Connections → Data sources no Grafana, dois datasources apontam para os serviços pelo nome:

  • Prometheus — http://prometheus:9090
  • Loki — http://loki:3100

Esses são os dois datasources que o artigo 7 desta série vai efetivamente usar em dashboards. O coletor OpenTelemetry não aparece como datasource do Grafana neste setup — ele só encaminha traces para o exportador debug configurado na seção 4; ligar um datasource de traces é o passo seguinte caso um backend de tracing seja adicionado à stack.

7. Confirmando que o pipeline completo funciona

Gerando tráfego nas duas aplicações e conferindo cada ponta:

curl http://localhost:8000/
curl http://localhost:8001/

# metricas
curl http://localhost:8000/metrics
curl http://localhost:8001/metrics

# logs do coletor, onde os traces devem aparecer impressos
docker compose logs otel-collector --tail 50
Enter fullscreen mode Exit fullscreen mode

Se o docker compose logs otel-collector mostrar blocos com TraceID, SpanID e Name para as requisições feitas, o pipeline de ponta a ponta está funcionando: as mini apps geram spans, o coletor os recebe via OTLP e os processa.

8. Conclusão e próximos passos

Unimos as seis peças construídas ao longo da série em uma stack única e reproduzível: duas mini aplicações instrumentadas para métricas, logs e traces, Prometheus fazendo scraping, Loki recebendo logs, um coletor OpenTelemetry recebendo traces via OTLP, e Grafana com os datasources de Prometheus e Loki configurados. No próximo artigo, o foco volta para o Grafana: construir dashboards reais que combinam PromQL e LogQL para acompanhar as mini apps de ponta a ponta em um único painel.


Imagem de capa: Logo oficial do Prometheus — repositório prometheus/prometheus, licença Apache 2.0 (convertido para PNG via wsrv.nl)

Referências:

  1. OpenTelemetry Collector — Documentation
  2. OpenTelemetry Collector — Configuration
  3. OpenTelemetry — OTLP Exporter Configuration
  4. Docker Compose — Networking

Top comments (0)