TL;DR — Resumo Rápido

Configure o Grafana Loki para agregação centralizada de logs com Promtail. LogQL, dashboards, alertas e pipelines multi-tenant em produção com exemplos reais.

O Grafana Loki é um sistema de agregação de logs escalável horizontalmente construído sobre os mesmos princípios do Prometheus — ele indexa apenas labels, não o texto completo do log, tornando-o dramaticamente mais barato que o stack ELK. Este guia cobre a arquitetura do Loki, instalação via Docker Compose, configuração do Promtail, a linguagem de consultas LogQL, criação de dashboards no Grafana, alertas, multi-tenancy e dimensionamento em produção.

Pré-requisitos

  • Docker e Docker Compose (ou um cluster Kubernetes para instalação via Helm).
  • Grafana 10+ (ou Grafana Cloud) para visualização.
  • Familiaridade básica com configuração YAML e ferramentas de linha de comando.

Arquitetura do Loki

O Loki distribui seu trabalho entre quatro componentes principais:

  • Distributor — Recebe fluxos de logs do Promtail/agentes, valida labels e distribui para os ingesters via hashing consistente.
  • Ingester — Mantém logs recentes em memória (tamanho de chunk configurável), descarrega chunks comprimidos para armazenamento de objetos e mantém um WAL para segurança em caso de falha.
  • Querier — Executa consultas LogQL sobre chunks tanto em memória (recentes) quanto em armazenamento (históricos).
  • Compactor — Mescla e deduplica as tabelas de índice do boltdb-shipper e aplica políticas de retenção.

No modo monolítico (padrão para instalações de nó único), os quatro componentes rodam em um único binário. Quando o volume de logs ultrapassa ~50 GB/dia, é recomendado mudar para modo microserviços onde cada componente escala de forma independente.

O insight central do design: o Loki nunca constrói um índice invertido do conteúdo dos logs. Uma consulta com |= "OutOfMemoryError" realiza um grep distribuído nos chunks que correspondem às labels selecionadas.


Por que Loki em vez do ELK?

CaracterísticaLokiElasticsearch
Modelo de indexaçãoApenas labelsÍndice invertido full-text
Custo de armazenamento~10x menosAlto (índice + dados)
Linguagem de consultaLogQL (similar ao PromQL)Lucene / KQL
Nativo no GrafanaSim — primeira classeVia plugin
Escala horizontalSim (sharding por labels)Sim (shards)
Mudanças de esquemaNão necessáriasRequer re-indexação
AlertasLoki ruler + AlertmanagerElastAlert / SIEM
Nativo no KubernetesSim (Helm chart)Sim (operador ECK)

Passo 1: Instalação com Docker Compose

Crie um diretório de projeto e o seguinte docker-compose.yml:

version: "3.8"
services:
  loki:
    image: grafana/loki:3.0.0
    ports:
      - "3100:3100"
    volumes:
      - ./loki-config.yaml:/etc/loki/local-config.yaml
      - loki-data:/loki
    command: -config.file=/etc/loki/local-config.yaml

  promtail:
    image: grafana/promtail:3.0.0
    volumes:
      - ./promtail-config.yaml:/etc/promtail/config.yaml
      - /var/log:/var/log:ro
      - /run/log/journal:/run/log/journal:ro
    command: -config.file=/etc/promtail/config.yaml

  grafana:
    image: grafana/grafana:10.4.0
    ports:
      - "3000:3000"
    environment:
      GF_AUTH_ANONYMOUS_ENABLED: "true"
      GF_AUTH_ANONYMOUS_ORG_ROLE: Admin

volumes:
  loki-data:

Passo 2: Configurar o Loki

Crie loki-config.yaml:

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

ingester:
  lifecycler:
    ring:
      kvstore:
        store: inmemory
      replication_factor: 1
  chunk_idle_period: 5m
  chunk_retain_period: 30s
  wal:
    enabled: true
    dir: /loki/wal

schema_config:
  configs:
    - from: 2024-01-01
      store: boltdb-shipper
      object_store: filesystem
      schema: v12
      index:
        prefix: index_
        period: 24h

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

compactor:
  working_directory: /loki/compactor
  shared_store: filesystem
  compaction_interval: 10m
  retention_enabled: true
  retention_delete_delay: 2h

limits_config:
  retention_period: 30d
  ingestion_rate_mb: 16
  ingestion_burst_size_mb: 32
  max_query_length: 721h

Passo 3: Configurar o Promtail

Crie promtail-config.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: journal
    journal:
      max_age: 12h
      labels:
        job: systemd-journal
        host: __HOSTNAME__
    relabel_configs:
      - source_labels: [__journal__systemd_unit]
        target_label: unit

  - job_name: nginx
    static_configs:
      - targets: [localhost]
        labels:
          job: nginx
          env: prod
          __path__: /var/log/nginx/access.log

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

Inicie o stack:

docker compose up -d
docker compose logs -f loki

Passo 4: Linguagem de Consultas LogQL

LogQL tem duas formas: consultas de log e consultas de métricas.

Seletores de stream (obrigatórios, sempre primeiro):

{job="nginx"}
{job="nginx", env="prod"}
{job=~"nginx|apache"}

Expressões de filtro (pipe após o seletor):

{job="nginx"} |= "error"
{job="nginx"} != "health_check"
{job="nginx"} |~ "5[0-9]{2}"

Expressões de parser — extraia campos de logs estruturados:

{job="app"} | json
{job="app"} | logfmt
{job="app"} | json | level="error"

Consultas de métricas — converta logs em séries temporais:

rate({job="nginx"} |= "error" [5m])
count_over_time({job="app"} | json | level="error" [1h])
sum by (status) (rate({job="nginx"} | logfmt | status=~"5.." [5m]))
topk(5, sum by (uri) (rate({job="nginx"}[10m])))

Passo 5: Fonte de Dados e Dashboards no Grafana

  1. No Grafana, vá a Connections → Data sources → Add data source → Loki.
  2. Configure a URL para http://loki:3100 (ou seu endereço do Loki).
  3. Clique em Save & test — você verá “Data source connected.”

Dicas do painel Explore:

  • Use Live tail para transmitir logs em tempo real — ideal para depurar deploys.
  • Clique em qualquer linha de log para ver o contexto do log — linhas antes e depois do match.
  • Mude para a aba Metrics para visualizar consultas rate() como painéis de séries temporais.

Passo 6: Alertas com o Loki Ruler

Adicione um bloco ruler ao loki-config.yaml:

ruler:
  storage:
    type: local
    local:
      directory: /loki/rules
  rule_path: /loki/rules-temp
  alertmanager_url: http://alertmanager:9093
  ring:
    kvstore:
      store: inmemory
  enable_api: true

Crie /loki/rules/fake/rules.yaml:

groups:
  - name: alertas-app
    interval: 1m
    rules:
      - alert: TaxaErroAlta
        expr: |
          sum(rate({job="app"} | json | level="error" [5m])) > 0.1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Taxa de erros alta nos logs da app"

Loki vs. Alternativas

FerramentaModelo de armazenamentoAuto-hospedadoCustoNativo GrafanaMelhor para
LokiÍndice de labels + chunks comprimidosSimBaixoSimEquipes com Grafana/Prometheus
ElasticsearchÍndice invertido full-textSimAltoVia pluginBusca full-text, SIEM
FluentdApenas agente (sem armazenamento)SimGrátisNãoRoteamento e transformação
Datadog LogsSaaS, proprietárioNãoAltoNãoEmpresa, observabilidade completa
CloudWatch LogsSaaS (AWS)NãoMédioVia pluginCargas de trabalho nativas AWS

Multi-Tenancy

Ative multi-tenancy com auth_enabled: true no Loki. Cada requisição deve incluir X-Scope-OrgID: <tenant-id>. Configure o Promtail para enviar o cabeçalho:

clients:
  - url: http://loki:3100/loki/api/v1/push
    tenant_id: equipe-backend

Recomendações de Dimensionamento em Produção

Volume de logsArquiteturaArmazenamentoRAM
< 5 GB/diaMonolítico, nó únicoFilesystem local2 GB
5–50 GB/diaMonolítico + S3/GCSArmazenamento de objetos4–8 GB
50–500 GB/diaMicroserviços, 3 ingestersObjetos + TSDB16–32 GB
> 500 GB/diaMicroserviços + Thanos rulerObjetos multi-AZ64 GB+

Sempre ative o WAL (ingester.wal.enabled: true) em produção para evitar perda de dados durante reinicializações do ingester.


Resolução de Problemas

ProblemaSolução
connection refused ao enviarVerifique se o contêiner Loki está rodando e a porta 3100 é acessível do Promtail
Timeout nas consultasAumente querier.query_timeout e divida consultas em intervalos de tempo menores
Logs do Docker ausentesVerifique positions.yaml do Promtail para o caminho do log do contêiner
Alta memória no ingesterReduza chunk_idle_period para descarregar chunks ao armazenamento mais cedo
Retenção não funcionaCertifique-se de que compactor.retention_enabled: true e limits_config.retention_period estão definidos

Resumo

  • A indexação apenas de labels torna o Loki 10x mais barato que o Elasticsearch para o mesmo volume.
  • O Promtail envia logs de arquivos, journal do systemd e Docker com enriquecimento de labels.
  • O LogQL suporta filtragem de streams, parse JSON/logfmt, cálculos de taxa e agregação.
  • O Loki ruler avalia alertas LogQL em um calendário e os roteia para o Alertmanager.
  • Para produção, substitua o armazenamento em filesystem por S3/GCS e ative WAL e cache de chunks.

Artigos Relacionados