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
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.txtsudo 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
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
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 filebeatfilebeat 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
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
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.1mbCon 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) sobretags: securitycon 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 →