Logs centralizados com Loki e Promtail

1. Introdução ao Loki e Promtail no ecossistema DevOps

Em ambientes distribuídos baseados em Docker e Kubernetes, a gestão de logs torna-se um desafio significativo. Cada container gera logs em formatos variados, e com dezenas ou centenas de pods rodando simultaneamente, rastrear erros e anomalias manualmente é impraticável. É aqui que entra o Loki, um sistema de agregação de logs inspirado no Prometheus, desenvolvido pela Grafana Labs.

Diferente de soluções tradicionais como a ELK Stack (Elasticsearch, Logstash, Kibana) ou Splunk, o Loki não indexa o conteúdo dos logs, mas apenas os metadados (labels). Isso resulta em armazenamento mais eficiente e consultas mais rápidas, especialmente em ambientes com alto volume de dados. O Promtail atua como agente de coleta, responsável por descobrir fontes de logs, aplicar transformações e enviar os dados ao Loki.

A combinação Loki + Promtail + Grafana forma um stack leve, nativo da nuvem e perfeitamente adaptado ao ecossistema Kubernetes, onde cada pod pode ser automaticamente identificado por labels como namespace, pod e container.

2. Arquitetura do stack de logs centralizados

A arquitetura segue um fluxo linear e simples:

  1. Aplicação gera logs em stdout/stderr (ou arquivos)
  2. Promtail coleta, processa e envia os logs ao Loki
  3. Loki armazena os logs em chunks compactados, indexados por labels
  4. Grafana consulta o Loki via LogQL e exibe os logs em dashboards

O modelo de armazenamento do Loki é baseado em chunks (blocos de dados) e índices. Cada chunk contém um intervalo de tempo e um conjunto de labels. Isso permite consultas eficientes por intervalo temporal e filtros por labels, sem necessidade de indexação do conteúdo textual.

Em clusters Kubernetes, o Promtail é implantado como DaemonSet, garantindo que cada node tenha um agente coletando logs de todos os pods daquele node. O Loki pode ser implantado em modo monolítico (para desenvolvimento) ou em modo escalável (para produção), com componentes separados: distribuidor, ingestor, querier e compactor.

3. Instalação e configuração do Loki

Modo monolítico (desenvolvimento)

Para testes locais, o modo single-binary é o mais simples. Crie um arquivo loki.yaml:

auth_enabled: false

server:
  http_listen_port: 3100

ingester:
  lifecycler:
    address: 127.0.0.1
    ring:
      kvstore:
        store: inmemory
      replication_factor: 1
  chunk_idle_period: 5m
  chunk_retain_period: 30s

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

storage_config:
  boltdb_shipper:
    active_index_directory: /tmp/loki/index
    cache_location: /tmp/loki/cache
    shared_store: filesystem
  filesystem:
    directory: /tmp/loki/chunks

compactor:
  working_directory: /tmp/loki/compactor

Execute o Loki:

docker run -d --name loki -p 3100:3100 -v $(pwd)/loki.yaml:/etc/loki/loki.yaml grafana/loki:2.9.0 -config.file=/etc/loki/loki.yaml

Implantação no Kubernetes com Helm

O Helm Chart oficial grafana/loki-stack simplifica a implantação completa do stack:

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

helm upgrade --install loki grafana/loki-stack \
  --set grafana.enabled=true \
  --set promtail.enabled=true \
  --set loki.persistence.enabled=true \
  --set loki.persistence.size=10Gi \
  --namespace monitoring --create-namespace

4. Configuração do Promtail para coleta de logs

Promtail como DaemonSet

O Promtail é configurado via YAML e deve ser implantado como DaemonSet para coletar logs de todos os pods. Exemplo de promtail.yaml:

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: kubernetes-pods
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_label_app]
        target_label: app
      - source_labels: [__meta_kubernetes_namespace]
        target_label: namespace
      - source_labels: [__meta_kubernetes_pod_container_name]
        target_label: container
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: pod
    pipeline_stages:
      - multiline:
          firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'
      - regex:
          expression: '^(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}) (?P<level>\w+) (?P<message>.*)'
      - labels:
          level:
      - timestamp:
          source: timestamp
          format: RFC3339

Coleta de logs do Docker local

Para ambientes Docker simples, configure o Promtail para ler logs do diretório do Docker:

scrape_configs:
  - job_name: docker-logs
    static_configs:
      - targets: [localhost]
        labels:
          job: docker
          __path__: /var/lib/docker/containers/*/*-log.json
    pipeline_stages:
      - json:
          expressions:
            log: log
            stream: stream
            time: time

5. Consultas e visualização de logs no Grafana

Após adicionar o Loki como fonte de dados no Grafana (URL: http://loki:3100), utilize a linguagem LogQL para consultas poderosas.

Sintaxe básica LogQL

  • Selector de streams: {app="nginx"}
  • Filtro de conteúdo: {app="nginx"} |= "error"
  • Filtro negativo: {app="nginx"} != "health"
  • Regex: {app="nginx"} |~ "error|critical"
  • Agregação temporal: rate({app="nginx"} |= "error" [5m])

Exemplos práticos

# Logs de erro do namespace production
{namespace="production"} |= "ERROR"

# Contagem de erros por aplicação nos últimos 15 minutos
sum by (app) (rate({job="kubernetes-pods"} |= "error" [15m]))

# Logs de um pod específico com timestamp
{pod="nginx-7d8b9c5d6f-abc12"} | logfmt | line_format "{{.timestamp}} {{.message}}"

Dashboard em tempo real

Crie um dashboard com painéis de logs usando consultas como:

# Top 5 aplicações com mais erros
topk(5, sum by (app) (count_over_time({job="kubernetes-pods"} |= "error" [1h])))

6. Integração com Docker e Kubernetes

Docker Compose com Loki e Promtail

Exemplo de docker-compose.yml:

version: '3.8'

services:
  loki:
    image: grafana/loki:2.9.0
    ports:
      - "3100:3100"
    volumes:
      - ./loki.yaml:/etc/loki/loki.yaml
    command: -config.file=/etc/loki/loki.yaml

  promtail:
    image: grafana/promtail:2.9.0
    volumes:
      - ./promtail.yaml:/etc/promtail/promtail.yaml
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/log:/var/log:ro
    command: -config.file=/etc/promtail/promtail.yaml

  nginx:
    image: nginx:alpine
    logging:
      driver: loki
      options:
        loki-url: "http://loki:3100/loki/api/v1/push"
        loki-retries: "2"
        loki-max-backoff: "1s"
        loki-timeout: "5s"

Coleta de logs de pods Kubernetes

No Kubernetes, o Promtail descobre automaticamente novos pods através do kubernetes_sd_configs. Labels como app, namespace e container são extraídos automaticamente. Para incluir labels personalizados, adicione anotações ao pod:

metadata:
  annotations:
    promtail.io/scrape: "true"
    promtail.io/format: "json"

Para logs de múltiplos namespaces, configure o Promtail com permissões RBAC adequadas e utilize seletores de labels no scrape_configs.

7. Boas práticas, alertas e manutenção

Retenção e compactação

Configure a retenção no Loki para evitar acúmulo excessivo:

table_manager:
  retention_deletes_enabled: true
  retention_period: 720h  # 30 dias

compactor:
  retention_enabled: true
  retention_rules:
    - selector:
        match: '{namespace="production"}'
      period: 720h
    - selector:
        match: '{namespace="staging"}'
      period: 168h

Alertas baseados em logs

Utilize o Alertmanager com regras baseadas em LogQL:

groups:
  - name: log-alerts
    rules:
      - alert: HighErrorRate
        expr: rate({app="api"} |= "error" [5m]) > 10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Alta taxa de erros na API"

Monitoramento do stack

Monitore as métricas do próprio Loki e Promtail no Grafana:

# Métricas do Loki
sum(rate(loki_request_duration_seconds_count{route="push"}[5m]))
# Métricas do Promtail
sum(rate(promtail_files_read_total[5m]))

Segurança

  • Ative autenticação básica ou OAuth no Loki
  • Configure TLS entre Promtail e Loki
  • No Kubernetes, utilize RBAC para restringir acesso ao Promtail
  • Isole namespaces de produção e desenvolvimento

Referências