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:
- Ve a Conexiones > Fuentes de datos > Agregar fuente de datos
- Selecciona Loki
- Establece la URL en
http://loki:3100 - Haz clic en Guardar y probar — deberías ver “Fuente de datos conectada y etiquetas encontradas”
Crear un panel de exploración de logs:
- Crea un nuevo dashboard y agrega un panel
- Selecciona Loki como fuente de datos
- Cambia la visualización a Logs
- Ingresa una consulta LogQL:
{job="varlogs"} |= "error" - 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ística | Loki | ELK Stack | Graylog |
|---|---|---|---|
| Estrategia de indexación | Solo etiquetas | Índice de texto completo | Índice de texto completo |
| Uso de RAM (10 GB/día) | ~512 MB | 8–16 GB | 4–8 GB |
| Lenguaje de consultas | LogQL | KQL / Lucene | GELF / Elasticsearch |
| Integración con Grafana | Nativa | Plugin requerido | Plugin requerido |
| Complejidad de configuración | Baja | Alta | Media |
| Escalabilidad horizontal | Buena (modo microservicios) | Excelente | Buena |
| Costo a escala | Muy bajo | Alto | Medio |
| Mejor para | Logs de Kubernetes / contenedores | Búsqueda de texto en logs | Cumplimiento, 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:
- 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.
- Agrega etiquetas
hostyenvpara que cada stream de log esté marcado con el servidor de origen y el entorno (producción/staging). - En Grafana, crea un dashboard con una variable
$hostvinculada a los valores de la etiquetahost. Ahora un único dashboard muestra logs de los 15 servidores, con un menú desplegable para filtrar por host. - 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.
- 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