Montar ELK rápido para debugging de problemas de performance
Hace poco me tocó resolver un problema urgente de performance en un servidor web Oracle Apache que estaba dando problemas. El cliente no tenía ELK montado y necesitábamos analizar millones de líneas de logs ya, para tomar decisiones sobre qué optimizar.
Este artículo documenta cómo monté un stack ELK temporal en pocas horas para analizar logs y encontrar el origen del problema. No es una solución para producción, sino una herramienta de debugging rápida y efectiva.
El problema: rendimiento crítico sin herramientas
El escenario:
- Servidor web Oracle Apache con problemas de rendimiento
- Millones de peticiones en los logs
- No había ELK ni herramientas de monitorización
- Necesitábamos respuestas rápidas para tomar decisiones
- El formato de logs era customizado (no estándar de Apache)
Necesitaba ver: qué endpoints eran más lentos, de dónde venía el tráfico, qué IPs generaban más carga, horarios pico, errores 5xx, etc.
¿Por qué ELK y no otra cosa?
Elastic ofrece muchas herramientas de ingesta: Beats, Fleet, Elastic Agent, integraciones pre-configuradas... Todas son excelentes para entornos de producción permanentes.
Pero para este caso puntual elegí Logstash + Docker porque:
- Rapidez: Podía levantar el stack completo en minutos con Docker
- Ingesta masiva simple: Con Logstash y
nc(netcat) podía descargar los logs del servidor e ingestarlos rápidamente sin configurar agentes - Flexibilidad: El formato de log era customizado, necesitaba crear un pipeline a medida
- Temporal: No era para dejarlo en producción, solo para debugging puntual
Arquitectura de la solución
┌─────────────────┐
│ Servidor Web │
│ (Oracle Apache) │
└────────┬────────┘
│ rsync/scp (descargar logs)
↓
┌─────────────────┐
│ Mi Laptop │
│ access.log │
└────────┬────────┘
│ cat logs | nc localhost 50000 (ingesta rápida)
↓
┌─────────────────┐
│ Logstash │ ← Parser custom con Grok
│ (puerto 50000)│
└────────┬────────┘
│ procesa y envía
↓
┌─────────────────┐
│ Elasticsearch │ ← Almacena e indexa
└────────┬────────┘
│ consulta
↓
┌─────────────────┐
│ Kibana │ ← Visualiza y analiza
└─────────────────┘
Paso 1: Levantar ELK con Docker
Usamos docker-elk que tiene todo preconfigurado:
git clone https://github.com/deviantony/docker-elk.git
cd docker-elk
docker compose up setup
docker compose up -d
En 5 minutos tienes:
- Elasticsearch en
localhost:9200 - Kibana en
localhost:5601 - Logstash escuchando en puertos 5044 y 50000
Paso 2: Crear el Index Template
Define cómo se almacenan los datos. Esto asegura que los campos como IPs, fechas y números se indexen correctamente:
curl -X PUT -u elastic:changeme "http://localhost:9200/_index_template/logs-debug-template?pretty" -H 'Content-Type: application/json' -d '{
"index_patterns": ["logs-debug-*"],
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"@timestamp": { "type": "date" },
"client_ip": { "type": "ip", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } },
"server_ip": { "type": "ip", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } },
"year": { "type": "integer" },
"month": { "type": "integer" },
"day": { "type": "integer" },
"time": { "type": "keyword" },
"hour_of_day": { "type": "integer" },
"method": { "type": "keyword" },
"request_path": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 512 } } },
"request_query": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 512 } } },
"request_full": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 512 } } },
"status": { "type": "integer" },
"request_size": { "type": "integer" },
"user_agent": {
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 512 }
}
},
"geoip": {
"properties": {
"country_name": { "type": "keyword" },
"region_name": { "type": "keyword" },
"city_name": { "type": "keyword" },
"coordinates": { "type": "geo_point" }
}
}
}
}
},
"priority": 200
}'
Nota: Usamos
number_of_replicas: 0porque es un entorno temporal de debugging. En producción usarías réplicas para alta disponibilidad.
Paso 3: Pipeline customizado para parsear logs
Mi caso tenía un formato específico de Oracle Apache Server. Creé un pipeline con Grok para extraer los campos:
curl -u elastic:changeme -X PUT "localhost:9200/_ingest/pipeline/logs-debug-pipeline?pretty" -H 'Content-Type: application/json' -d '
{
"description": "Parser para logs customizados",
"processors": [
{
"grok": {
"field": "message",
"patterns": [
"(?:\\\"?(%{IP:client_ip})?\\\"?|-)\\s+%{IP:server_ip}\\s+%{YEAR:year}-%{MONTHNUM:month}-%{MONTHDAY:day}\\s+%{TIME:time}\\s+%{WORD:method}\\s+%{URIPATH:request_path}(?:\\?(%{DATA:request_query}))?\\s+%{NUMBER:status}\\s+(?:%{NUMBER:request_size}|-)\\s+(?:\\\"%{GREEDYDATA:user_agent}\\\"|-)"
]
}
},
{
"set": {
"field": "timestamp",
"value": "-- "
}
},
{
"date": {
"field": "timestamp",
"formats": ["yyyy-MM-dd HH:mm:ss"],
"target_field": "@timestamp"
}
},
{
"script": {
"lang": "painless",
"source": "def date = Instant.parse(ctx[\"@timestamp\"]).atZone(ZoneId.of(\"GMT+1\")); ctx.hour_of_day = date.getHour();"
}
},
{
"set": {
"field": "request_full",
"value": ""
}
},
{
"geoip": {
"field": "client_ip",
"target_field": "geoip",
"properties": ["country_name", "region_name", "city_name", "location"],
"ignore_missing": true
}
},
{
"rename": {
"field": "geoip.location",
"target_field": "geoip.coordinates",
"ignore_missing": true
}
}
]
}
'
Importante: Este Grok pattern está customizado para mi formato específico. Si tus logs siguen el formato estándar de Apache, usa directamente la integración Apache HTTP Server que ya incluye parser + dashboards.
Paso 4: Configurar permisos en Elasticsearch
El usuario por defecto logstash_internal necesita permisos para crear índices:
curl -X POST -u elastic:changeme "localhost:9200/_security/role/logstash_debug_writer" \
-H 'Content-Type: application/json' -d '{
"cluster": ["manage_index_templates", "monitor"],
"indices": [
{
"names": ["logs-debug-*"],
"privileges": ["create_index", "write", "create", "manage"]
}
]
}'
curl -X PUT -u elastic:changeme "localhost:9200/_security/user/logstash_internal" \
-H 'Content-Type: application/json' -d '{
"roles": ["logstash_writer", "logstash_debug_writer"]
}'
Paso 5: Configurar Logstash
Edita logstash/pipeline/logstash.conf:
input {
tcp {
port => 50000
codec => plain
}
}
filter {
mutate {
add_field => { "pipeline" => "logs-ubwebsinst-pipeline" }
}
}
output {
elasticsearch {
hosts => "elasticsearch:9200"
user => "logstash_internal"
password => "${LOGSTASH_INTERNAL_PASSWORD}"
pipeline => "logs-debug-pipeline"
index => "logs-debug-%{+YYYY.MM.dd}"
}
}
La clave aquí es el input tcp en puerto 50000. Esto permite ingestar datos con un simple cat logs | nc localhost 50000.
Reinicia Logstash:
docker compose restart logstash
Paso 6: Descargar e ingestar logs
Descarga los logs del servidor:
# Desde el servidor remoto a tu laptop
rsync -avz user@server:/path/to/logs/access* ./logs/
Ingesta masiva con netcat:
# Ingestar todos los logs de golpe
find ./logs -name "access*" -type f -exec cat {} + | nc localhost 50000
Esto envía millones de líneas directamente a Logstash que las procesa y envía a Elasticsearch. En mi caso ingesté 5GB de logs en unos 20 minutos.
Paso 7: Analizar en Kibana
- Abre http://localhost:5601
- Ve a Management > Stack Management > Data Views
- Crea un Data View con
logs-debug-* - Ve a Discover y empieza a explorar
Análisis útiles que usé
Con Kibana puedes crear visualizaciones y filtrar por:
- Errores 5xx: Filtra por
status >= 500y agrupa por endpoint - Top IPs: Agrega por
client_ip.keywordy ordena por count - Tráfico por hora: Agrega por
hour_of_daypara ver patrones - Peticiones por país: Visualiza
geoip.country_nameen un mapa - Endpoints más solicitados: Agrega por
request_path.keyword
Mi caso real: Qué encontré
Tras analizar los logs con Kibana, descubrí:
- 3 endpoints específicos generaban el 60% de los errores 5xx
- Picos de tráfico entre las 10-12h que saturaban el servidor
- Una IP específica hacía scraping agresivo sin respetar rate limits
- Peticiones lentas a una API externa de traducciones que a veces tardaba +30 segundos
Acciones tomadas:
- Cache agresivo en los 3 endpoints problemáticos
- Rate limiting en el balanceador
- Timeout de 5s en la API de traducciones con fallback
- Escalado automático durante horas pico
Resultado: Errores 5xx bajaron de 15% a menos de 2%, tiempos de respuesta mejoraron un 40%.
Ventajas de este enfoque
✅ Rápido de montar: 30 minutos desde cero hasta analizar logs
✅ Flexible: Puedes adaptar el pipeline a cualquier formato de log
✅ Ingesta masiva simple: cat logs | nc sin instalar agentes
✅ Potente: Kibana te da análisis visual que con grep nunca conseguirías
✅ Temporal: Cuando terminas, docker compose down -v y listo
Limitaciones (no es para producción)
❌ No es tiempo real (descargas logs y los analizas después) ❌ Sin réplicas ni alta disponibilidad ❌ Requiere descargar logs manualmente ❌ No es escalable para logs continuos
Cuándo usar este setup
✅ Úsalo cuando:
- Necesitas debugar problemas puntuales urgentes
- No tienes tiempo de montar infraestructura completa
- Necesitas analizar logs históricos rápidamente
- Formato de log customizado que no sigue estándares
- Es temporal, solo para investigación
❌ NO lo uses cuando:
- Necesitas monitorización continua en producción
- Quieres ingesta en tiempo real
- Tus logs siguen formatos estándar (usa las integraciones oficiales)
- Necesitas retención a largo plazo
Para producción, usa las herramientas oficiales de Elastic: Elastic Agent, Fleet, Beats o las integraciones pre-configuradas.
Limpieza
Cuando termines el análisis:
# Para los contenedores y borra los datos
docker compose down -v
# Borra los logs descargados
rm -rf ./logs
Alternativas según tu caso
- Logs estándar de Apache: Usa la integración Apache HTTP Server con dashboards incluidos
- Monitorización continua: Usa Elastic Agent + Fleet
- Logs de múltiples servidores: Usa Filebeat
- Telemetría de apps: Usa APM
Conclusión
Este no es un tutorial para montar ELK en producción. Es una solución rápida y práctica para cuando necesitas analizar logs urgentemente y tomar decisiones basadas en datos.
Me salvó en una situación donde tenía que encontrar el origen de problemas de performance sin herramientas, sin tiempo y con logs en formato custom.
Si estás en una situación similar: descarga docker-elk, adapta el pipeline a tu formato de log, ingesta con netcat y en una hora estarás explorando tus datos en Kibana.
Para entornos de producción permanentes, definitivamente invierte tiempo en montar las herramientas oficiales de ingesta de Elastic correctamente.
Referencias
- Docker ELK Stack
- Elastic Ingest Tools
- Apache HTTP Server Integration
- Grok Patterns
- Logstash Documentation
Escrito por Miguel Ordóñez