TL;DR — Resumen Rápido

Configura Grafana Loki para centralizar logs con Promtail. Cubre LogQL, dashboards en Grafana, alertas con ruler y configuración multi-tenant en producción.

Grafana Loki es un sistema de agregación de logs escalable horizontalmente construido sobre los mismos principios que Prometheus: indexa solo etiquetas, no el texto completo del log, lo que lo hace dramáticamente más económico que el stack ELK. Esta guía cubre la arquitectura de Loki, instalación con Docker Compose, configuración de Promtail, el lenguaje de consultas LogQL, construcción de dashboards en Grafana, alertas, multi-tenancy y dimensionamiento en producción.

Requisitos Previos

  • Docker y Docker Compose (o un clúster de Kubernetes para instalación con Helm).
  • Grafana 10+ (o Grafana Cloud) para visualización.
  • Familiaridad básica con configuración YAML y herramientas de línea de comandos.

Arquitectura de Loki

Loki distribuye su trabajo entre cuatro componentes principales:

  • Distributor — Recibe flujos de logs de Promtail/agentes, valida etiquetas y los distribuye a los ingesters mediante hashing consistente.
  • Ingester — Mantiene logs recientes en memoria (tamaño de chunk configurable), descarga chunks comprimidos a almacenamiento de objetos y mantiene un WAL para recuperación ante fallos.
  • Querier — Ejecuta consultas LogQL sobre chunks tanto en memoria (recientes) como en almacenamiento (históricos).
  • Compactor — Fusiona y deduplica las tablas de índice de boltdb-shipper y aplica las políticas de retención.

En modo monolítico (por defecto en instalaciones de nodo único), los cuatro componentes corren en un solo binario. Cuando el volumen de logs supera los ~50 GB/día, conviene cambiar a modo microservicios donde cada componente escala de forma independiente.

La clave del diseño: Loki nunca construye un índice invertido del contenido de los logs. Una consulta con |= "OutOfMemoryError" realiza un grep distribuido sobre los chunks que coinciden con las etiquetas seleccionadas.


¿Por Qué Loki en Lugar de ELK?

CaracterísticaLokiElasticsearch
Modelo de indexadoSolo etiquetasÍndice invertido full-text
Costo de almacenamiento~10x menosAlto (índice + datos)
Lenguaje de consultaLogQL (similar a PromQL)Lucene / KQL
Nativo en GrafanaSí — primera claseVia plugin
Escala horizontalSí (sharding por etiquetas)Sí (shards)
Cambios de esquemaNo necesariosRequiere re-indexado
AlertasLoki ruler + AlertmanagerElastAlert / SIEM
Nativo en KubernetesSí (Helm chart)Sí (operador ECK)

Paso 1: Instalación con Docker Compose

Crea un directorio de proyecto y el siguiente 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:

Paso 2: Configurar Loki

Crea 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

Paso 3: Configurar Promtail

Crea 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

Inicia el stack:

docker compose up -d
docker compose logs -f loki

Paso 4: Lenguaje de Consultas LogQL

LogQL tiene dos formas: consultas de log y consultas de métricas.

Selectores de stream (obligatorios, siempre primero):

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

Expresiones de filtro (pipe tras el selector):

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

Expresiones de parser — extrae campos de logs estructurados:

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

Consultas de métricas — convierte logs en series temporales:

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])))

Paso 5: Fuente de Datos y Dashboards en Grafana

  1. En Grafana ve a Connections → Data sources → Add data source → Loki.
  2. Configura la URL a http://loki:3100 (o tu dirección de Loki).
  3. Haz clic en Save & test — deberías ver “Data source connected.”

Consejos para el panel Explore:

  • Usa Live tail para hacer streaming de logs en tiempo real — ideal para depurar despliegues.
  • Haz clic en cualquier línea de log para ver el contexto del log — líneas anteriores y posteriores al match.
  • Cambia a la pestaña Metrics para visualizar consultas rate() como paneles de series temporales.

Paso 6: Alertas con Loki Ruler

Agrega un bloque ruler a 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

Crea /loki/rules/fake/rules.yaml:

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

Loki vs. Alternativas

HerramientaModelo de almacenamientoAuto-alojadoCostoNativo GrafanaMejor para
LokiÍndice de etiquetas + chunks comprimidosBajoEquipos con Grafana/Prometheus
ElasticsearchÍndice invertido full-textAltoVia pluginBúsqueda full-text, SIEM
FluentdSolo agente (sin almacenamiento)GratisNoEnrutamiento y transformación
Datadog LogsSaaS, propietarioNoAltoNoEmpresa, observabilidad completa
CloudWatch LogsSaaS (AWS)NoMedioVia pluginCargas de trabajo nativas de AWS

Multi-Tenancy

Habilita multi-tenancy con auth_enabled: true en Loki. Cada solicitud debe incluir X-Scope-OrgID: <tenant-id>. Configura Promtail para enviar el encabezado:

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

Recomendaciones de Dimensionamiento en Producción

Volumen de logsArquitecturaAlmacenamientoRAM
< 5 GB/díaMonolítico, nodo únicoFilesystem local2 GB
5–50 GB/díaMonolítico + S3/GCSAlmacenamiento de objetos4–8 GB
50–500 GB/díaMicroservicios, 3 ingestersObjetos + TSDB16–32 GB
> 500 GB/díaMicroservicios + Thanos rulerObjetos multi-AZ64 GB+

Siempre habilita el WAL (ingester.wal.enabled: true) en producción para evitar pérdida de datos durante reinicios del ingester.


Resolución de Problemas

ProblemaSolución
connection refused al enviarVerifica que el contenedor Loki esté corriendo y el puerto 3100 sea accesible desde Promtail
Timeout en consultasAumenta querier.query_timeout y divide las consultas en rangos de tiempo más cortos
Logs de Docker faltantesRevisa positions.yaml de Promtail para la ruta del log del contenedor
Alta memoria en ingesterReduce chunk_idle_period para descargar chunks al almacenamiento más pronto
Retención no funcionaAsegúrate de que compactor.retention_enabled: true y limits_config.retention_period estén configurados

Resumen

  • El indexado solo de etiquetas hace a Loki 10 veces más económico que Elasticsearch para el mismo volumen.
  • Promtail envía logs desde archivos, journal de systemd y Docker con enriquecimiento de etiquetas.
  • LogQL soporta filtrado de streams, parseo JSON/logfmt, cálculos de tasa y agregación.
  • El Loki ruler evalúa alertas LogQL en un calendario y las enruta a Alertmanager.
  • Para producción, reemplaza el almacenamiento en filesystem con S3/GCS y habilita WAL y caché de chunks.

Artículos Relacionados