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.