El logging centralizado se vuelve esencial en el momento en que gestionas más de un puñado de servidores, contenedores o microservicios. Sin él, depurar un incidente en producción significa conectarse por SSH a máquinas individuales y buscar en archivos de log dispersos. La agregación de logs con Loki, Promtail y Grafana — el stack PLG — resuelve esto enviando todos tus logs a un almacén único y consultable, manteniendo los costos de recursos muy por debajo de soluciones tradicionales como Elasticsearch.

Requisitos Previos

  • Un servidor Linux (Ubuntu 22.04 o Debian 12 recomendado) con al menos 2 GB de RAM
  • Docker 24+ y Docker Compose v2 instalados
  • Familiaridad básica con los dashboards de Grafana (ver Grafana Dashboards para Monitoreo de Infraestructura)
  • Puertos 3000 (Grafana), 3100 (Loki) y 9080 (Promtail) disponibles
  • Acceso sudo en la máquina host

Entendiendo el Stack PLG

El stack PLG consta de tres componentes que trabajan juntos como un pipeline de observabilidad ligero:

Promtail es el agente de recopilación de logs. Se ejecuta junto a tus aplicaciones, lee archivos de log o el journal de systemd, adjunta etiquetas (metadatos clave-valor) y envía los streams de log a Loki via HTTP.

Loki es el backend de agregación de logs. A diferencia de Elasticsearch, Loki no indexa el contenido del log — solo indexa las etiquetas adjuntas por Promtail. Las líneas de log sin procesar se comprimen y almacenan como chunks. Esta arquitectura hace que Loki sea dramáticamente más económico: un clúster que maneja gigabytes de logs por día puede funcionar con unos pocos cientos de megabytes de RAM.

Grafana proporciona la capa de consulta y visualización. Se conecta a Loki como fuente de datos y permite escribir consultas LogQL, construir dashboards y configurar alertas — todo en la misma interfaz que usas para las métricas de Prometheus (ver Configuración de Prometheus y Grafana para Monitoreo de Servidores).

El flujo de datos es: Aplicación → archivo de log/journal → Promtail → Loki → Grafana.

Instalando Loki con Docker Compose

Crea un directorio de trabajo y el siguiente docker-compose.yml:

version: "3.8"

networks:
  logging:
    driver: bridge

volumes:
  loki_data:
  grafana_data:

services:
  loki:
    image: grafana/loki:3.0.0
    container_name: loki
    ports:
      - "3100:3100"
    volumes:
      - ./loki-config.yaml:/etc/loki/loki-config.yaml
      - loki_data:/loki
    command: -config.file=/etc/loki/loki-config.yaml
    networks:
      - logging

  promtail:
    image: grafana/promtail:3.0.0
    container_name: promtail
    volumes:
      - ./promtail-config.yaml:/etc/promtail/promtail-config.yaml
      - /var/log:/var/log:ro
      - /run/log/journal:/run/log/journal:ro
      - /etc/machine-id:/etc/machine-id:ro
    command: -config.file=/etc/promtail/promtail-config.yaml
    networks:
      - logging
    depends_on:
      - loki

  grafana:
    image: grafana/grafana:11.0.0
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=changeme
    volumes:
      - grafana_data:/var/lib/grafana
    networks:
      - logging
    depends_on:
      - loki

Crea loki-config.yaml en el mismo directorio:

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  retention_period: 30d

compactor:
  working_directory: /loki/compactor
  retention_enabled: true
  delete_request_store: filesystem

Inicia el stack:

docker compose up -d
docker compose ps

Los tres servicios deberían mostrar estado healthy o running en menos de 30 segundos.

Configurando 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: varlogs
    static_configs:
      - targets:
          - localhost
        labels:
          job: varlogs
          host: ${HOSTNAME}
          __path__: /var/log/*.log

  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      - source_labels: [__meta_docker_container_name]
        regex: "/(.*)"
        target_label: container
      - source_labels: [__meta_docker_container_label_com_docker_compose_service]
        target_label: service

El archivo positions registra hasta dónde ha leído Promtail en cada archivo de log, evitando ingestión duplicada tras reinicios.

Reinicia Promtail para aplicar la configuración:

docker compose restart promtail
docker compose logs promtail --tail=20

Consultando Logs con LogQL

LogQL es el lenguaje de consultas de Loki, inspirado en PromQL. Las consultas tienen dos partes: un selector de stream entre llaves y etapas opcionales de pipeline.

Selector de stream básico — obtener todos los logs del job varlogs:

{job="varlogs"}

Filtro de línea — encontrar líneas que contengan “error”:

{job="varlogs"} |= "error"

Filtro regex — encontrar líneas que coincidan con un patrón:

{job="systemd-journal"} |~ "fail(ed)?"

Filtro de etiqueta tras parseo — parsear logs JSON y filtrar por nivel:

{job="varlogs"} | json | level="error"

Consulta métrica — tasa de errores en el tiempo:

rate({job="varlogs"} |= "error" [5m])

Contar errores por host:

sum by (host) (count_over_time({job="varlogs"} |= "error" [1h]))

Para patrones más complejos de parseo de logs, compara con el enfoque de Journalctl para Consultar y Analizar Logs del Sistema Linux para escenarios solo locales.

Configurando Dashboards en Grafana

Abre Grafana en http://tu-servidor:3000 e inicia sesión con admin / changeme.

Agregar Loki como fuente de datos:

  1. Ve a Conexiones > Fuentes de datos > Agregar fuente de datos
  2. Selecciona Loki
  3. Establece la URL en http://loki:3100
  4. Haz clic en Guardar y probar — deberías ver “Fuente de datos conectada y etiquetas encontradas”

Crear un panel de exploración de logs:

  1. Crea un nuevo dashboard y agrega un panel
  2. Selecciona Loki como fuente de datos
  3. Cambia la visualización a Logs
  4. Ingresa una consulta LogQL: {job="varlogs"} |= "error"
  5. Activa Deduplicación y Ajuste de líneas en las opciones del panel

Construir un dashboard de tasa:

Agrega un panel de Series de tiempo con la consulta:

sum by (host) (rate({job="varlogs"} |= "error" [5m]))

Esto te da un gráfico de tasa de errores en tiempo real desglosado por servidor.

Alertas sobre Patrones de Logs

Loki incluye un componente ruler que evalúa consultas métricas LogQL en un horario y dispara alertas a Alertmanager.

Agrega la configuración del ruler a loki-config.yaml:

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

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

groups:
  - name: log-alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate({job="varlogs"} |= "error" [5m])) > 0.1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Alta tasa de errores detectada en los logs del sistema"
          description: "La tasa de logs de error ha superado 0.1 líneas/seg durante 5 minutos."

      - alert: SSHAuthFailures
        expr: |
          sum(count_over_time({unit="sshd.service"} |= "Failed password" [10m])) > 10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Múltiples fallos de autenticación SSH"
          description: "Más de 10 fallos de auth SSH en 10 minutos — posible fuerza bruta."

Loki vs ELK vs Graylog — Comparativa

CaracterísticaLokiELK StackGraylog
Estrategia de indexaciónSolo etiquetasÍndice de texto completoÍndice de texto completo
Uso de RAM (10 GB/día)~512 MB8–16 GB4–8 GB
Lenguaje de consultasLogQLKQL / LuceneGELF / Elasticsearch
Integración con GrafanaNativaPlugin requeridoPlugin requerido
Complejidad de configuraciónBajaAltaMedia
Escalabilidad horizontalBuena (modo microservicios)ExcelenteBuena
Costo a escalaMuy bajoAltoMedio
Mejor paraLogs de Kubernetes / contenedoresBúsqueda de texto en logsCumplimiento, SIEM

Loki gana en costo y simplicidad cuando tu necesidad principal es la agregación y correlación de logs con métricas. Para una comparación con la configuración de Elasticsearch, ver Configuración de Elasticsearch para Análisis de Logs.

Escenario del Mundo Real

Gestionas 15 servidores de producción con una combinación de Nginx, PostgreSQL y APIs personalizadas en Python. Los incidentes ocurren pero el análisis de causa raíz lleva horas porque los logs están distribuidos en 15 máquinas. Así es como PLG resuelve esto:

  1. Despliega Promtail en cada servidor usando una herramienta de gestión de configuración (Ansible/Puppet), apuntando todas las instancias a un único endpoint de Loki.
  2. Agrega etiquetas host y env para que cada stream de log esté marcado con el servidor de origen y el entorno (producción/staging).
  3. En Grafana, crea un dashboard con una variable $host vinculada a los valores de la etiqueta host. Ahora un único dashboard muestra logs de los 15 servidores, con un menú desplegable para filtrar por host.
  4. Agrega una regla de alerta que se dispare cuando cualquier host produzca más de 50 líneas de error por minuto — recibes notificación en Slack antes de que los usuarios reporten el problema.
  5. Durante incidentes, usa la vista Explore para correlacionar logs y métricas de Prometheus lado a lado: pico de errores en logs a las 14:32 → pico de CPU en el mismo host a las 14:31.

Este flujo de trabajo reemplaza una investigación SSH manual de 45 minutos con un drill-down en Grafana de 3 minutos.

Errores Comunes y Casos Especiales

Explosión de cardinalidad de etiquetas. Cada combinación única de etiquetas crea un stream de log separado. Nunca uses valores de alta cardinalidad (IDs de usuario, UUIDs, IDs de solicitud) como etiquetas. Mantén las etiquetas en valores estables y de bajo conteo: host, job, env, service.

Configuración de retención. El retention_period en limits_config requiere que el compactor esté habilitado con retention_enabled: true. Sin esto, Loki almacena logs indefinidamente hasta que el disco se llena.

Caché de chunks y memoria. Loki almacena en caché chunks sin comprimir en memoria durante la ingestión. En servidores con menos de 2 GB de RAM, establece chunk_target_size: 1048576 y max_chunk_age: 1h para limitar la presión de memoria.

Pérdida del archivo de posiciones de Promtail. Si el contenedor de Promtail se recrea sin un volumen persistente para /tmp/positions.yaml, vuelve a leer todos los archivos de log desde el inicio e ingesta duplicados. Siempre monta el archivo de posiciones en un volumen persistente.

Ordenación de logs. Loki requiere que las líneas de log dentro de un único stream se ingesten en orden de timestamp. Si tu aplicación escribe logs con timestamps fuera de orden, habilita unordered_writes: true en la configuración del ingester de Loki.

Acceso al socket de Docker. La configuración de Docker SD en Promtail requiere acceso al socket de Docker. El contenedor de Promtail debe ejecutarse como root o agregarse al grupo docker.

Resumen

  • El stack PLG (Promtail + Loki + Grafana) proporciona agregación centralizada de logs a una fracción del costo de ELK
  • Loki indexa solo etiquetas, no el contenido del log — manteniendo el uso de almacenamiento y RAM mínimo
  • Los selectores de stream LogQL ({job="varlogs"}) más etapas de pipeline (|= "error", | json) permiten filtrado potente de logs
  • Promtail realiza scraping del journal de systemd, archivos y logs de contenedores Docker con adjunción automática de etiquetas
  • La fuente de datos Loki de Grafana permite dashboards unificados que combinan logs (Loki) y métricas (Prometheus)
  • Las etiquetas de alta cardinalidad son el error de rendimiento más común en Loki — mantén los valores de etiquetas estables y de bajo conteo
  • Las reglas de alertas de Loki usan consultas métricas LogQL evaluadas por el componente ruler

Artículos Relacionados