Observabilidade com Grafana Stack: Loki, Tempo e Mimir na prática

1. Introdução à Grafana Stack para Observabilidade

Em um cenário de sistemas distribuídos cada vez mais complexos, a capacidade de correlacionar logs, traces e métricas tornou-se requisito fundamental para equipes que buscam confiabilidade. A Grafana Stack — composta por Loki, Tempo e Mimir — oferece uma plataforma unificada e de código aberto que resolve esse desafio sem a necessidade de múltiplas ferramentas desconectadas.

Diferente de soluções como Prometheus + Jaeger + Elastic, que exigem integrações manuais e custos elevados de armazenamento, a stack Grafana foi projetada para trabalhar de forma integrada desde a concepção. Loki gerencia logs com eficiência usando índices invertidos baseados em labels, Tempo armazena traces distribuídos com baixo custo de armazenamento, e Mimir oferece métricas de longa retenção com escalabilidade horizontal nativa.

2. Loki: Agregação de Logs em Escala

Loki difere de soluções tradicionais como Elasticsearch por não indexar o conteúdo completo dos logs. Em vez disso, utiliza labels para indexação e armazena os logs em chunks comprimidos em object stores (S3, GCS, MinIO). Isso reduz drasticamente o custo de armazenamento e acelera consultas.

Para coletar logs, utilizamos o Promtail (ou o novo Alloy). Um exemplo de configuração básica do Promtail:

# promtail-config.yml
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: system
    static_configs:
      - targets:
          - localhost
        labels:
          job: varlogs
          __path__: /var/log/*.log

Com o LogQL, podemos realizar consultas poderosas. Para filtrar erros de um serviço específico:

{job="varlogs"} |= "ERROR" | json | line_format "{{.timestamp}} {{.message}}"

Para correlação com traces, extraímos o trace_id dos logs:

{job="api-service"} | regexp "trace_id=(?P<trace_id>[a-f0-9]+)" | line_format "{{.trace_id}}"

3. Tempo: Rastreamento Distribuído (Distributed Tracing)

Tempo é um backend de tracing distribuído que aceita spans no formato OpenTelemetry. Sua arquitetura armazena os dados brutos em object stores, indexando apenas os trace IDs e metadados essenciais, o que permite retenção de longo prazo com custo reduzido.

Para enviar spans de uma aplicação Go:

// main.go - configuração OpenTelemetry
import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
    "go.opentelemetry.io/otel/sdk/trace"
)

func initTracer() {
    exporter, _ := otlptrace.New(
        context.Background(),
        otlptracegrpc.NewClient(
            otlptracegrpc.WithEndpoint("tempo:4317"),
            otlptracegrpc.WithInsecure(),
        ),
    )
    tp := trace.NewTracerProvider(trace.WithBatcher(exporter))
    otel.SetTracerProvider(tp)
}

Para identificar gargalos, buscamos traces com duração elevada:

{ resource.service.name = "payment-service" } && { duration > 1s }

O Tempo permite visualizar a árvore de spans e identificar onde o tempo está sendo consumido, seja em chamadas HTTP, consultas a banco ou filas de mensageria.

4. Mimir: Métricas de Longa Retenção e Escalabilidade

Mimir é o sucessor do Cortex, projetado para armazenar métricas no formato Prometheus com alta disponibilidade e escalabilidade horizontal. Ele oferece compressão eficiente, downsampling automático e retenção configurável por períodos de meses ou anos.

Configuração básica de um tenant no Mimir:

# mimir-config.yml
multitenancy_enabled: true

blocks_storage:
  backend: s3
  s3:
    bucket_name: mimir-metrics
    endpoint: minio:9000
    access_key_id: minio
    secret_access_key: minio123
    insecure: true

compactor:
  retention: 90d
  downsample:
    enabled: true
    retention: 30d

Uma consulta PromQL para monitorar SLOs:

# Taxa de erros 5xx nos últimos 5 minutos
rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.01

Alertas baseados em SLOs podem ser configurados no Grafana ou no Alertmanager:

groups:
  - name: slo-alerts
    rules:
      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
        for: 10m
        labels:
          severity: critical
        annotations:
          summary: "Erro rate acima de 5% no serviço {{ $labels.service }}"

5. Correlação entre Logs, Traces e Métricas na Prática

A verdadeira potência da Grafana Stack está na correlação entre os três pilares. No Grafana, configuramos Derived Fields para extrair trace_ids dos logs e criar links diretos para o Tempo.

Passos para configurar:

  1. No datasource Loki, adicione Derived Fields apontando para o campo trace_id
  2. No datasource Tempo, configure o link de volta para logs com base no trace ID
  3. No datasource Mimir, adicione links para traces usando labels como service_name

Estudo de caso: debug de um serviço de alto throughput

  1. Um alerta dispara no Grafana: rate(http_requests_total{service="checkout"}[5m]) > 1000
  2. Clicamos no alerta e vemos o pico de métricas no Mimir
  3. No painel de métricas, clicamos em um trace específico que mostra latência alta
  4. No Tempo, visualizamos a árvore de spans e identificamos uma chamada lenta ao banco Redis
  5. Clicamos no span problemático e somos levados aos logs do Loki para aquele trace_id
  6. Nos logs, encontramos erros de timeout na conexão Redis

Todo o processo leva segundos, sem precisar alternar entre ferramentas diferentes.

6. Otimizações e Boas Práticas para Times Pequenos

Para times com recursos limitados, algumas estratégias ajudam a reduzir custos:

Retenção e compressão no Loki:

# Configuração de retenção no Loki
table_manager:
  retention_deletes_enabled: true
  retention_period: 30d

chunk_store_config:
  max_look_back_period: 30d

storage_config:
  boltdb_shipper:
    active_index_directory: /data/loki/index
    cache_location: /data/loki/cache
    shared_store: s3

Multi-tenancy e RBAC:

# Configuração de tenants no Mimir
overrides:
  tenant-a:
    ingestion_rate: 10000
    max_series_per_user: 50000
  tenant-b:
    ingestion_rate: 5000
    max_series_per_user: 20000

Monitoramento da stack: Configure health checks para cada componente e alertas para alta cardinalidade de labels, que pode impactar performance e custos.

7. Conclusão e Próximos Passos

A Grafana Stack com Loki, Tempo e Mimir representa a evolução natural da observabilidade para sistemas modernos. A correlação nativa entre logs, traces e métricas elimina silos de informação e reduz drasticamente o tempo de diagnóstico de incidentes.

Para migração gradual, comece com Loki substituindo o Elasticsearch para logs, depois adicione Tempo para tracing e por último migre métricas do Prometheus standalone para Mimir. A integração com o Grafana existente facilita a transição.

A stack é particularmente valiosa para organizações que lidam com alto throughput e precisam de retenção de longo prazo sem explodir o orçamento de armazenamento. Com a comunidade ativa e documentação robusta, é uma escolha sólida para a lista final de temas em observabilidade.

Referências