Envío de Logs con Filebeat a Elasticsearch

Filebeat es el agente ligero de Elastic para el envío de logs desde servidores hacia Elasticsearch o Logstash. Con sus módulos predefinidos para los servicios más comunes (Nginx, Apache, MySQL, PostgreSQL, systemd) y la posibilidad de definir inputs personalizados, Filebeat simplifica la configuración del pipeline de logs y añade capacidades de parsing y enriquecimiento antes de indexar los datos en Elasticsearch.

Requisitos Previos

  • Ubuntu 22.04/20.04, Debian 11, o CentOS/Rocky Linux 8+
  • Elasticsearch 8.x y Kibana accesibles en red
  • El usuario que ejecuta Filebeat debe tener permisos de lectura en los archivos de log
  • Espacio en disco suficiente para el buffer interno de Filebeat

Instalación de Filebeat

# Agregar el repositorio de Elastic (Ubuntu/Debian)
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add -
echo "deb https://artifacts.elastic.co/packages/8.x/apt stable main" \
  | tee /etc/apt/sources.list.d/elastic-8.x.list

apt update && apt install -y filebeat

# Para CentOS/Rocky Linux
rpm --import https://packages.elastic.co/GPG-KEY-elasticsearch
cat > /etc/yum.repos.d/elastic.repo << 'EOF'
[elasticsearch]
name=Elasticsearch repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
EOF

yum install -y filebeat

# Verificar la instalación
filebeat version

Configuración de Inputs Personalizados

El archivo principal de configuración es /etc/filebeat/filebeat.yml.

cat > /etc/filebeat/filebeat.yml << 'EOF'
# ================ Filebeat Inputs ================
filebeat.inputs:

# Input tipo log: recopilar archivos de log
- type: filestream  # Reemplaza al tipo "log" en versiones modernas
  id: nginx-access-logs
  enabled: true
  paths:
    - /var/log/nginx/access.log
  tags: ["nginx", "access", "produccion"]
  fields:
    service: nginx
    environment: produccion
    datacenter: eu-west-1
  fields_under_root: true  # Mover los fields al nivel raíz del documento

- type: filestream
  id: nginx-error-logs
  enabled: true
  paths:
    - /var/log/nginx/error.log
  tags: ["nginx", "error"]
  fields:
    service: nginx
    log_type: error
  fields_under_root: true

# Input para la aplicación personalizada (logs JSON)
- type: filestream
  id: mi-aplicacion
  enabled: true
  paths:
    - /var/log/mi_aplicacion/*.log
  tags: ["mi_aplicacion"]
  # Parsear logs JSON directamente en Filebeat
  parsers:
    - ndjson:
        target: ""         # Mover los campos al nivel raíz
        add_error_key: true
        overwrite_keys: true

# Input del journal de systemd
- type: journald
  id: systemd-journal
  enabled: true
  seek: cursor
  units:
    - sshd.service
    - nginx.service
    - postgresql.service
  tags: ["systemd"]

# ================ Output a Elasticsearch ================
output.elasticsearch:
  hosts: ["https://elasticsearch.miempresa.com:9200"]
  username: "filebeat_writer"
  password: "contraseña-segura"
  
  # Usar SSL/TLS
  ssl.certificate_authorities: ["/etc/filebeat/certs/ca.crt"]
  ssl.certificate: "/etc/filebeat/certs/filebeat.crt"
  ssl.key: "/etc/filebeat/certs/filebeat.key"
  
  # Índice donde escribir los logs
  index: "logs-%{[fields.service]}-%{+yyyy.MM.dd}"
  
  # Número de workers de salida
  worker: 2
  
  # Tamaño del lote de documentos por petición bulk
  bulk_max_size: 1000
  
  # Tiempo máximo de espera para enviar un lote
  flush_timeout: 5s

# ================ Configuración de ILM ================
setup.ilm.enabled: true
setup.ilm.rollover_alias: "filebeat"
setup.ilm.policy_name: "filebeat-policy"

# ================ Kibana (para instalar dashboards) ================
setup.kibana:
  host: "https://kibana.miempresa.com:5601"
  username: "elastic"
  password: "contraseña-elastic"

# ================ Logging de Filebeat ================
logging.level: warning
logging.to_files: true
logging.files:
  path: /var/log/filebeat
  name: filebeat
  keepfiles: 7
  permissions: 0644

# ================ Configuración del registro de posición ================
filebeat.registry.path: /var/lib/filebeat/registry
filebeat.registry.flush: 1s
EOF

# Verificar la configuración
filebeat test config -c /etc/filebeat/filebeat.yml

# Probar la salida a Elasticsearch
filebeat test output -c /etc/filebeat/filebeat.yml

# Iniciar el servicio
systemctl enable filebeat && systemctl start filebeat

Módulos de Filebeat

Los módulos simplifican la configuración con parsers y dashboards predefinidos.

# Listar los módulos disponibles
filebeat modules list

# Activar los módulos más comunes
filebeat modules enable nginx
filebeat modules enable system
filebeat modules enable mysql
filebeat modules enable postgresql
filebeat modules enable elasticsearch
filebeat modules enable logstash
filebeat modules enable kibana

# Ver la configuración del módulo de Nginx
cat /etc/filebeat/modules.d/nginx.yml
# Personalizar el módulo de Nginx con rutas no estándar
cat > /etc/filebeat/modules.d/nginx.yml << 'EOF'
- module: nginx
  access:
    enabled: true
    var.paths: ["/var/log/nginx/access.log", "/var/log/nginx/*/access.log"]
    
  error:
    enabled: true
    var.paths: ["/var/log/nginx/error.log", "/var/log/nginx/*/error.log"]
EOF

# Personalizar el módulo de MySQL
cat > /etc/filebeat/modules.d/mysql.yml << 'EOF'
- module: mysql
  error:
    enabled: true
    var.paths: ["/var/log/mysql/error.log"]
    
  slowlog:
    enabled: true
    var.paths: ["/var/log/mysql/slow-query.log"]
    var.convert_timezone: true
EOF

# Instalar los assets de los módulos (índices, pipelines, dashboards)
filebeat setup --modules nginx,system,mysql \
  -E output.elasticsearch.hosts='["https://elasticsearch:9200"]' \
  -E output.elasticsearch.username=elastic \
  -E output.elasticsearch.password=contraseña \
  -E setup.kibana.host=https://kibana:5601

# Reiniciar Filebeat para aplicar los módulos
systemctl restart filebeat

Manejo de Logs Multilínea

# Configurar Filebeat para manejar stack traces de Java u otros logs multilínea
cat >> /etc/filebeat/filebeat.yml << 'EOF'

# Agregar este input para logs Java con stack traces
- type: filestream
  id: java-aplicacion
  enabled: true
  paths:
    - /var/log/mi-java-app/application.log
  parsers:
    - multiline:
        type: pattern
        # Una nueva entrada de log siempre empieza con timestamp ISO
        pattern: '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}'
        negate: true           # El patrón se cumple cuando NO coincide
        match: after           # Las líneas sin patrón se agregan a la anterior
        max_lines: 500         # Número máximo de líneas por entrada
        timeout: 5s            # Tiempo máximo antes de enviar el evento
EOF

# Ejemplo: log de Python con traceback multilínea
cat >> /etc/filebeat/filebeat.yml << 'EOF'

- type: filestream
  id: python-app
  paths:
    - /var/log/python-app/app.log
  parsers:
    - multiline:
        type: pattern
        # Los tracebacks empiezan con "Traceback" y terminan con la excepción
        pattern: '^(ERROR|WARNING|INFO|DEBUG|CRITICAL)\s+\d{4}-\d{2}-\d{2}'
        negate: true
        match: after
EOF

Procesadores y Transformaciones

# Los procesadores de Filebeat transforman los eventos antes de enviarlos
cat >> /etc/filebeat/filebeat.yml << 'EOF'

processors:
  # Agregar la IP del host automáticamente
  - add_host_metadata:
      when.not.contains.tags: forwarded
  
  # Agregar información del proceso que escribe el log
  - add_process_metadata:
      match_pids: [1]
      include_fields: ["process.name", "process.pid"]
  
  # Enriquecer con geolocalización de IPs (requiere base de datos GeoIP)
  - geoip:
      field: source.ip
      target_field: source.geo
      database_file: /etc/filebeat/GeoLite2-City.mmdb
      ignore_missing: true
  
  # Eliminar campos que no queremos indexar (para reducir tamaño)
  - drop_fields:
      fields: ["agent.ephemeral_id", "ecs.version"]
      ignore_missing: true
  
  # Renombrar campos
  - rename:
      fields:
        - from: "nginx.access.remote_ip"
          to: "source.ip"
        - from: "nginx.access.response_code"
          to: "http.response.status_code"
      ignore_missing: true
  
  # Filtrar eventos que no queremos enviar (health checks)
  - drop_event:
      when:
        or:
          - contains:
              nginx.access.url: "/health"
          - contains:
              nginx.access.url: "/metrics"
          - equals:
              nginx.access.remote_ip: "127.0.0.1"
EOF

Dashboards de Kibana

# Instalar todos los dashboards predefinidos de Filebeat
filebeat setup \
  --dashboards \
  -E output.elasticsearch.hosts='["https://elasticsearch:9200"]' \
  -E output.elasticsearch.username=elastic \
  -E output.elasticsearch.password=contraseña \
  -E setup.kibana.host=https://kibana:5601

# Instalar solo los índices (sin dashboards)
filebeat setup \
  --index-management \
  -E output.elasticsearch.hosts='["https://elasticsearch:9200"]' \
  -E output.elasticsearch.username=elastic \
  -E output.elasticsearch.password=contraseña

# Ver los dashboards disponibles en Kibana
# Navegar a: Kibana → Analytics → Dashboard
# Buscar "Filebeat" para ver los dashboards instalados

# Verificar que los índices se están creando correctamente
curl -s -u elastic:contraseña \
  "https://elasticsearch:9200/_cat/indices/filebeat-*?v&s=index"

Ingest Node Pipelines

# Los Ingest Pipelines de Elasticsearch procesan los documentos antes de indexarlos
# Crear un pipeline personalizado para parsear logs de Nginx

curl -s -X PUT \
  -H "Content-Type: application/json" \
  -u elastic:contraseña \
  "https://elasticsearch:9200/_ingest/pipeline/parse-nginx-logs" \
  -d '{
    "description": "Pipeline para parsear logs de acceso de Nginx",
    "processors": [
      {
        "grok": {
          "field": "message",
          "patterns": [
            "%{IPORHOST:source.ip} - %{DATA:user.name} \\[%{HTTPDATE:@timestamp}\\] \"%{WORD:http.method} %{DATA:url.original} HTTP/%{NUMBER:http.version}\" %{NUMBER:http.response.status_code:int} %{NUMBER:http.response.body.bytes:int}"
          ]
        }
      },
      {
        "date": {
          "field": "@timestamp",
          "formats": ["dd/MMM/yyyy:HH:mm:ss Z"]
        }
      },
      {
        "geoip": {
          "field": "source.ip",
          "target_field": "source.geo",
          "ignore_missing": true
        }
      },
      {
        "remove": {
          "field": "message",
          "ignore_missing": true
        }
      }
    ]
  }'

# Configurar Filebeat para usar el pipeline
cat >> /etc/filebeat/filebeat.yml << 'EOF'

# Usar el pipeline de ingest para los logs de Nginx
- type: filestream
  id: nginx-con-pipeline
  paths:
    - /var/log/nginx/custom_access.log
  pipeline: "parse-nginx-logs"
EOF

Solución de Problemas

Filebeat no envía logs:

# Ver los logs de Filebeat
journalctl -u filebeat -f
tail -f /var/log/filebeat/filebeat

# Verificar la conexión a Elasticsearch
filebeat test output

# Ver las métricas internas de Filebeat
curl -s http://localhost:5066/stats | jq '.filebeat.events'

# Forzar el reenvío desde el principio (CUIDADO: puede duplicar logs)
systemctl stop filebeat
rm /var/lib/filebeat/registry/filebeat/*
systemctl start filebeat

Harvester deja de leer el archivo:

# Verificar que Filebeat tiene permisos de lectura
ls -la /var/log/nginx/access.log
id filebeat

# Agregar el usuario filebeat al grupo que tiene acceso a los logs
usermod -aG adm filebeat

# Verificar que el archivo no está rotado sin notificar a Filebeat
# En /etc/logrotate.d/nginx, asegurarse de usar "copytruncate" o "postrotate/reload"

Conclusión

Filebeat proporciona un agente de recolección de logs maduro y bien integrado con el ecosistema Elastic. Sus módulos predefinidos reducen drásticamente la configuración necesaria para los servicios más comunes, mientras que los procesadores y los Ingest Pipelines permiten enriquecer y transformar los eventos antes de indexarlos. Para organizaciones que ya utilizan Elasticsearch y Kibana, Filebeat es la solución natural para completar el pipeline de observabilidad con una gestión centralizada de logs.