Análisis de Logs

Introducción a los Sistemas de Observabilidad

Comprender el comportamiento interno de un sistema requiere el análisis de tres pilares fundamentales. El monitoreo se ocupa del estado actual y la estabilidad del sistema, rastreando métricas como el uso de CPU, memoria, latencia y tasas de errores. El análisis de registros (logs) proporciona información detallada y secuencial sobre eventos específicos. El rastreo distribuido permite seguir el flujo de una solicitud a través de múltiples servicios.

La Ingeniería del Caos y los ensayos de fallos complementan estos pilares, permitiendo validar la resiliencia del sistema de forma proactiva. Cuando surge un problema, un reporte eficaz debe incluir el entorno, los comandos ejecutados, los mensajes de error observados y los registros relevantes generados en el momento.

Los sistemas de logs no solo son útiles para la depuración. Por ejemplo, en una plataforma de streaming, los registros pueden capturar datos de interacción del usuario, como el tipo de contenido consumido y la duración de la visita, información que los sistemas de big data pueden utilizar para construir perfiles de usuario.

Arquitecturas Comunes para Procesamiento de Logs

Existen varias arquitecturas populares. La pila ELK (Elasticsearch, Logstash, Kibana) es una solución madura pero, según algunos, presenta complejidades en su agente de recolección y puede ser pesada en recursos. Una alternativa evolucionada es EFK, que reemplaza Logstash por Fluentd, un agente más ligero y flexible. Para entornos que priorizan la simplicidad y un menor consumo, la pila PLG (Promtail, Loki, Grafana) ofrece una solución moderna y eficiente.

Una arquitectura más robusta y desacoplada para producción a gran escala se estructura típicamente en cuatro etapas. Primero, un agente ligero (como Filebeat o Fluent Bit) se despliega en los servidores de aplicaciones; su única función es recolectar los registros crudos y transmitirlos a un sistema de mensajería, minimizando el impacto en el rendimiento de la aplicación. Segundo, Apache Kafka actúa como un búfer central, absorbiendo picos de tráfico y proporcionando desacople entre la recolección y el procesamiento. Tercero, un procesador central (como Logstash o Fluentd) consume los registros desde Kafka, realizando transformaciones críticas: filtrado, parseo de formatos (JSON, logs de Nginx, stacktraces de Java), enriquecimiento y estructuración de campos. Finalmente, los registros procesados se almacenan en un motor de búsqueda como Elasticsearch para su consulta a través de Kibana.

Implementación Práctica en un Entorno de Ejemplo

1. Despliegue del Almacén de Datos (Elasticsearch)

El primer paso es crear un servicio headless para la comunicación entre nodos del clúster de Elasticsearch.

apiVersion: v1
kind: Service
metadata:
  name: elastic-svc-headless
  namespace: log-system
  labels:
    app: es-cluster
spec:
  selector:
    app: es-cluster
  clusterIP: None
  ports:
  - port: 9200
    name: rest-api
  - port: 9300
    name: inter-node

A continuación, se define un StatefulSet para garantizar una identidad de red estable y almacenamiento persistente para cada réplica del clúster. La sección volumeClaimTemplates solicita automáticamente un volumen persistente (PV) por cada Pod, asegurando la supervivencia de los datos ante reinicios o reprogramaciones.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-node
  namespace: log-system
spec:
  serviceName: elastic-svc-headless
  replicas: 1
  selector:
    matchLabels:
      app: es-cluster
  template:
    metadata:
      labels:
        app: es-cluster
    spec:
      initContainers:
        - name: sysctl-init
          image: busybox
          command: ["sysctl", "-w", "vm.max_map_count=262144"]
          securityContext:
            privileged: true
      containers:
        - name: es-container
          image: docker.elastic.co/elasticsearch/elasticsearch:7.17.1
          resources:
            requests:
              cpu: "250m"
            limits:
              cpu: "1000m"
          ports:
            - containerPort: 9200
              name: rest-api
            - containerPort: 9300
              name: inter-node
          volumeMounts:
            - name: es-data-volume
              mountPath: /usr/share/elasticsearch/data
          env:
            - name: cluster.name
              value: "logs-cluster"
            - name: node.name
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: discovery.type
              value: single-node
            - name: ES_JAVA_OPTS
              value: "-Xms512m -Xmx512m"
  volumeClaimTemplates:
    - metadata:
        name: es-data-volume
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 10Gi

2. Despliegue de la Interfaz de Consulta (Kibana)

La configuración de Kibana se gestiona mediante un ConfigMap, y el servicio se expone con un tipo NodePort para acceso externo.

apiVersion: v1
kind: ConfigMap
metadata:
  name: kibana-config
  namespace: log-system
data:
  kibana.yml: |
    server.name: "kibana-prod"
    server.host: "0.0.0.0"
    i18n.locale: "es"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana-deploy
  namespace: log-system
spec:
  selector:
    matchLabels:
      app: kibana-app
  template:
    metadata:
      labels:
        app: kibana-app
    spec:
      containers:
      - name: kibana
        image: docker.elastic.co/kibana/kibana:7.17.1
        ports:
        - containerPort: 5601
        env:
        - name: ELASTICSEARCH_HOSTS
          value: "http://elastic-svc-headless:9200"
        volumeMounts:
        - name: config-volume
          mountPath: /usr/share/kibana/config/kibana.yml
          subPath: kibana.yml
          readOnly: true
      volumes:
      - name: config-volume
        configMap:
          name: kibana-config
---
apiVersion: v1
kind: Service
metadata:
  name: kibana-svc
  namespace: log-system
spec:
  type: NodePort
  selector:
    app: kibana-app
  ports:
    - port: 5601
      targetPort: 5601
      nodePort: 30044

3. Despliegue del Sistema de Mensajería (Kafka)

Kafka actúa como intermediario duradero entre los agentes de recolección y los procesadores. Utiliza Zookeeper para la gestión del clúster. La configuración siguiente despliega ambos servicios.

version: '3.8'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.3.0
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    volumes:
      - zk-data:/var/lib/zookeeper/data
      - zk-log:/var/lib/zookeeper/log

  kafka:
    image: confluentinc/cp-kafka:7.3.0
    depends_on:
      - zookeeper
    ports:
      - "9092:9092"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    volumes:
      - kafka-logs:/var/lib/kafka/data

volumes:
  zk-data:
  zk-log:
  kafka-logs:

4. Despliegue del Procesador y Enrutador (Logstash)

Logstash se configurará para consumir mensajes de un tópico específico de Kafka, aplicar filtros y enviarlos a Elasticsearch. La configuración de la canalización (pipeline) define estos flujos.

# /usr/share/logstash/pipeline/app-logs.conf
input {
  kafka {
    bootstrap_servers => "kafka-host:9092"
    topics_pattern => "app-.*"
    auto_offset_reset => "earliest"
    codec => json
    group_id => "logstash-consumer-group"
  }
}

filter {
  if [fields][log_type] == "json" {
    json {
      source => "message"
      target => "parsed_data"
    }
  }
  mutate {
    add_field => { "processing_timestamp" => "%{@timestamp}" }
  }
}

output {
  elasticsearch {
    hosts => ["http://elastic-svc-headless:9200"]
    index => "processed-logs-%{+YYYY.MM.dd}"
    document_id => "%{[@metadata][kafka][offset]}"
  }
}

5. Despliegue del Agente de Recolección Ligero (Filebeat)

Finalmente, se despliega un DaemonSet de Filebeat para recolectar logs de contenedores en cada nodo del clúster de Kubernetes y enviarlos al tópico apropiado en Kafka.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: filebeat
  namespace: log-system
spec:
  selector:
    matchLabels:
      app: filebeat
  template:
    metadata:
      labels:
        app: filebeat
    spec:
      serviceAccountName: filebeat
      terminationGracePeriodSeconds: 30
      containers:
      - name: filebeat
        image: docker.elastic.co/beats/filebeat:8.5.0
        args: [
          "-c", "/etc/filebeat.yml",
          "-e",
        ]
        volumeMounts:
        - name: config
          mountPath: /etc/filebeat.yml
          readOnly: true
          subPath: filebeat.yml
        - name: data
          mountPath: /usr/share/filebeat/data
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      volumes:
      - name: config
        configMap:
          defaultMode: 0600
          name: filebeat-config
      - name: data
        hostPath:
          path: /var/lib/filebeat-data
          type: DirectoryOrCreate
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

Con estos componentes desplegados, se establece una canalización completa y desacoplada: los agentes (Filebeat) recolectan -> Kafka almacena y amortigua -> Logstash procesa y estructura -> Elasticsearch indexa -> Kibana permite la exploración y el análisis.

Etiquetas: Elasticsearch Fluentd Kibana Kafka Logstash

Publicado el 6-15 18:47