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ística Loki Elasticsearch
Modelo de indexado Solo etiquetas Índice invertido full-text
Costo de almacenamiento ~10x menos Alto (índice + datos)
Lenguaje de consulta LogQL (similar a PromQL) Lucene / KQL
Nativo en Grafana Sí — primera clase Via plugin
Escala horizontal Sí (sharding por etiquetas) Sí (shards)
Cambios de esquema No necesarios Requiere re-indexado
Alertas Loki ruler + Alertmanager ElastAlert / SIEM
Nativo en Kubernetes Sí (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

Herramienta Modelo de almacenamiento Auto-alojado Costo Nativo Grafana Mejor para
Loki Índice de etiquetas + chunks comprimidos Bajo Equipos con Grafana/Prometheus
Elasticsearch Índice invertido full-text Alto Via plugin Búsqueda full-text, SIEM
Fluentd Solo agente (sin almacenamiento) Gratis No Enrutamiento y transformación
Datadog Logs SaaS, propietario No Alto No Empresa, observabilidad completa
CloudWatch Logs SaaS (AWS) No Medio Via plugin Cargas 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 logs Arquitectura Almacenamiento RAM
< 5 GB/día Monolítico, nodo único Filesystem local 2 GB
5–50 GB/día Monolítico + S3/GCS Almacenamiento de objetos 4–8 GB
50–500 GB/día Microservicios, 3 ingesters Objetos + TSDB 16–32 GB
> 500 GB/día Microservicios + Thanos ruler Objetos multi-AZ 64 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

Problema Solución
connection refused al enviar Verifica que el contenedor Loki esté corriendo y el puerto 3100 sea accesible desde Promtail
Timeout en consultas Aumenta querier.query_timeout y divide las consultas en rangos de tiempo más cortos
Logs de Docker faltantes Revisa positions.yaml de Promtail para la ruta del log del contenedor
Alta memoria en ingester Reduce chunk_idle_period para descargar chunks al almacenamiento más pronto
Retención no funciona Asegú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