Guías · Nivel 3

Elasticsearch, Logstash y Kibana: centralizar los logs de los servidores y buscar un error

Un nodo de Elasticsearch, Filebeat en los servidores, el pipeline de Logstash que da forma a los eventos y la búsqueda en Kibana que sustituye a veinte sesiones SSH.

Qué vas a conseguir

Que los logs de todos los servidores Linux (y los de Windows, con el mismo agente) lleguen a un único sitio donde buscar "permission denied" o un ID de correlación en segundos, con gráficos de cuántos errores hubo y cuándo. Al terminar tendrás la pila mínima en Docker y dos servidores enviando.

Antes de empezar

  • El servidor de herramientas de la guía de Docker, con 8 GB de RAM y 100 GB de disco para los índices.
  • Puertos abiertos en su firewall: 5044 (Beats → Logstash) desde los servidores, 5601 (Kibana) desde la red de IT.
  • Decidir la retención: cuántos días de logs guardas. Treinta es un buen punto de partida.

1. La pila con Compose

docker-compose.yml
services:  elasticsearch:    image: docker.elastic.co/elasticsearch/elasticsearch:8.15.3    environment:      - discovery.type=single-node      - xpack.security.enabled=true      - ELASTIC_PASSWORD_FILE=/run/secrets/elastic      - ES_JAVA_OPTS=-Xms2g -Xmx2g    volumes: [es-data:/usr/share/elasticsearch/data]    secrets: [elastic]    ulimits: { memlock: { soft: -1, hard: -1 } }  logstash:    image: docker.elastic.co/logstash/logstash:8.15.3    volumes: [./pipeline:/usr/share/logstash/pipeline:ro]    ports: ["5044:5044"]    depends_on: [elasticsearch]  kibana:    image: docker.elastic.co/kibana/kibana:8.15.3    ports: ["5601:5601"]    environment:      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200    depends_on: [elasticsearch]volumes:  es-data:secrets:  elastic:    file: ./secrets/elastic.txt
bash · srv-tools-01
sudo sysctl -w vm.max_map_count=262144 && echo "vm.max_map_count=262144" | sudo tee /etc/sysctl.d/99-elastic.confdocker compose up -d && sleep 60curl -s -u elastic:$(cat secrets/elastic.txt) http://localhost:9200/_cluster/health?pretty | grep -E 'status|number_of_nodes'  "status" : "green",  "number_of_nodes" : 1,

Un solo nodo y status: green es lo normal para un entorno de IT interno. Kibana necesita su propia cuenta de servicio: docker compose exec elasticsearch bin/elasticsearch-reset-password -u kibana_system y la pones en la variable ELASTICSEARCH_PASSWORD de Kibana.

2. El pipeline de Logstash

pipeline/syslog.conf
input {  beats { port => 5044 }}filter {  if [event][dataset] == "system.syslog" {    grok {      match => { "message" => "%{SYSLOGTIMESTAMP:ts} %{SYSLOGHOST:host.name} %{DATA:process.name}(?:\[%{POSINT:process.pid}\])?: %{GREEDYDATA:log.message}" }    }    date { match => ["ts", "MMM  d HH:mm:ss", "MMM dd HH:mm:ss"] }    mutate { remove_field => ["ts"] }  }  if "Permission denied" in [message] or "Failed password" in [message] {    mutate { add_tag => ["security"] }  }}output {  elasticsearch {    hosts => ["http://elasticsearch:9200"]    user => "elastic"    password => "${ELASTIC_PASSWORD}"    index => "logs-linux-%{+YYYY.MM.dd}"  }}

El grok convierte una línea de texto en campos (host.name, process.name, log.message): sin eso solo podrías buscar texto libre. La etiqueta security es el primer ejemplo de algo que luego usarás en una alerta. Comprueba la sintaxis antes de arrancar: docker compose exec logstash bin/logstash --config.test_and_exit -f /usr/share/logstash/pipeline/syslog.conf.

3. Filebeat en los servidores

bash · srv-web-01
curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-8.15.3-x86_64.rpm && sudo rpm -vi filebeat-8.15.3-x86_64.rpmsudo filebeat modules enable systemsudo sed -i 's|#output.logstash:|output.logstash:|; s|#hosts: \["localhost:5044"\]|hosts: ["srv-tools-01.contoso.local:5044"]|' /etc/filebeat/filebeat.ymlsudo sed -i 's|^output.elasticsearch:|#output.elasticsearch:|; s|^  hosts: \["localhost:9200"\]|#  hosts: ["localhost:9200"]|' /etc/filebeat/filebeat.ymlsudo filebeat test outputlogstash: srv-tools-01.contoso.local:5044...  connection...    parse host... OK    dns lookup... OK    TCP... OK  talk to server... OKsudo systemctl enable --now filebeat

filebeat test output es la prueba que haces antes de abrir un ticket al equipo de red: si TCP... OK falla, es el firewall; si falla talk to server, es Logstash. Con Ansible (guía anterior), este paso es un playbook de diez líneas para toda la flota.

4. Buscar en Kibana

Kibana · Discover
AnalyticsDiscoverlogs-linux-*
RangoLast 24 hours
KQLhost.name : "srv-web-01" and tags : "security"
Resultados37 hits
Oct 22, 2026 07:58:12 srv-web-01 sshd[2231] Failed password for invalid user admin from 10.20.4.77 port 51022 ssh2
Oct 22, 2026 07:58:14 srv-web-01 sshd[2231] Failed password for invalid user admin from 10.20.4.77 port 51030 ssh2
Oct 22, 2026 08:02:11 srv-web-01 httpd[1842] (13)Permission denied: AH00072: make_sock: could not bind to address 0.0.0.0:8081
Add filter
Save
Share

Para que Kibana vea los índices hace falta una Data view (Stack Management → Data views → Create, patrón logs-linux-*, campo de tiempo @timestamp). A partir de ahí: la misma búsqueda en todos los servidores a la vez, el histograma arriba te dice a qué hora empezó el problema, y Visualize sobre process.name te enseña qué servicio genera más errores.

5. Retención y espacio

bash · srv-tools-01
curl -s -u elastic:$(cat secrets/elastic.txt) -X PUT http://localhost:9200/_ilm/policy/logs-30d -H 'Content-Type: application/json' -d '{"policy":{"phases":{"hot":{"actions":{}},"delete":{"min_age":"30d","actions":{"delete":{}}}}}}'{"acknowledged":true}curl -s -u elastic:$(cat secrets/elastic.txt) -X PUT http://localhost:9200/_index_template/logs-linux -H 'Content-Type: application/json' -d '{"index_patterns":["logs-linux-*"],"template":{"settings":{"index.lifecycle.name":"logs-30d","number_of_replicas":0}}}'{"acknowledged":true}curl -s -u elastic:$(cat secrets/elastic.txt) 'http://localhost:9200/_cat/indices/logs-*?v&h=index,docs.count,store.size&s=index' | tail -3logs-linux-2026.10.20   412033   211.4mblogs-linux-2026.10.21   398870   204.9mblogs-linux-2026.10.22    88121    46.1mb

Con un solo nodo, number_of_replicas: 0 evita que el clúster esté siempre en amarillo. Unos 200 MB al día por veinte servidores es el orden de magnitud; mira _cat/indices al mes para ajustar la retención al disco que tienes.

Siguientes pasos

  • Windows: el agente es Winlogbeat con el mismo output.logstash; los eventos de seguridad (4625, 4740) llegan ya con campos.
  • Una alerta en Kibana (Alerts and Insights → Rules) sobre tags: security con más de 20 eventos en 5 minutos, enviada al canal del equipo.
  • Si el volumen crece, la opción es Elastic Agent + Fleet en lugar de Beats sueltos, o pasar los dashboards a Grafana (siguiente guía) con Elasticsearch como origen de datos.

¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.

Tech Career Lab · newsletter

¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.

Apúntate y llévate las 33 ideas →