Monitoreo avanzado con eBPF y Prometheus en servidores Linux
Imagina que tu servidor Linux empieza a mostrar latencia aleatoria en peticiones de red. El top muestra CPU al 30%, free indica memoria libre, y iostat no reporta nada extraño. Sin embargo, los usuarios se quejan de timeouts. ¿Qué está pasando? El problema probablemente reside en una cola de red bloqueada por un controlador defectuoso, un byte leak en el kernel, o una contención de bloqueos imposible de ver con herramientas tradicionales.
Aquí es donde entra en juego el monitoreo avanzado con eBPF y Prometheus. No hablamos de simples checks SNMP o scripts en bash. Hablamo de sondear el propio kernel Linux para extraer métricas a nivel de instrucción de máquina, sin modificar el código fuente de las aplicaciones. En este artículo, te voy a mostrar cómo construir un pipeline de monitoreo que detecte anomalías en tiempo real, usando eBPF como sonda y Prometheus como recolector y alertador.
¿Por qué el monitoreo tradicional se queda corto en 2025?
Las herramientas clásicas (top, htop, vmstat, netstat) son como mirar el salpicadero de un coche: te dicen la velocidad y el nivel de gasolina, pero no qué cilindro falla. En entornos de alto rendimiento y microservicios, los cuellos de botella suelen ser:
- Contención de bloqueos del kernel: Un
mutexmal diseñado en un módulo de red puede paralizar todo el sistema. - Fugas de memoria en el kernel: No se ven en
freeporque están en slabs del kernel. - Syscalls lentas: Una sola llamada
write()a un FS corrupto puede degradar todo el proceso. - Tráfico de red a nivel de paquete: No basta con ver bytes/segundo; necesitas ver si hay retransmisiones TCP o colas de backlog llenas.
eBPF (Extended Berkeley Packet Filter) resuelve esto permitiéndote ejecutar programas sándwich dentro del kernel, de forma segura y controlada. No necesitas cargar módulos de kernel peligrosos; eBPF verifica el código antes de ejecutarlo.
[INFO] eBPF no es solo para redes. Puedes engancharte a kprobes (puntos de sonda dinámicos), tracepoints, y hasta a funciones específicas del kernel. Esto te da visibilidad de lo que antes era una caja negra.
Arquitectura del sistema: eBPF + Prometheus
La arquitectura típica para monitoreo avanzado se compone de tres capas:
- Sonda eBPF: Un programa (generalmente en C o Rust) que se carga en el kernel y extrae métricas específicas (latencia de syscalls, número de context switches, conteo de paquetes perdidos).
- Exportador (Exporter): Un proceso en espacio de usuario que lee los mapas de eBPF (estructuras de datos compartidas entre kernel y user-space) y los expone en formato
/metricsde Prometheus. - Prometheus y Alertmanager: Recolectan las métricas cada
scrape_intervaly disparan alertas cuando se detectan anomalías.
Componentes clave
- BCC (BPF Compiler Collection): Conjunto de herramientas y librerías para escribir programas eBPF en Python/Lua.
- bpftrace: Lenguaje de alto nivel para sondas rápidas (similar a awk pero para kernel).
- ebpf_exporter (de Cloudflare/Elastic): Exportador genérico que permite definir programas eBPF en YAML y exponerlos a Prometheus.
- Prometheus: Almacenamiento de series temporales y sistema de alertas.
Implementación paso a paso: Detectando syscalls lentas
Vamos a construir un ejemplo práctico: detectar llamadas al sistema (syscalls) que tarden más de 100ms. Esto es un indicador de anomalía clásico: un servidor que de repente empieza a hacer read() lentos probablemente tenga un problema de E/S.
1. Programa eBPF (C)
Necesitamos un programa que se enganche al tracepoint sys_enter y sys_exit, mida el tiempo y si supera un umbral, lo cuente.
// slow_syscall.c
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct key_t {
u32 pid;
u64 syscall_nr;
};
BPF_HASH(start, struct key_t);
BPF_PERCPU_ARRAY(latency_hist, u64, 100); // 100 buckets de 10ms
int trace_entry(struct pt_regs *ctx) {
u64 id = bpf_get_current_pid_tgid();
u32 pid = id >> 32;
u64 syscall_nr = PT_REGS_PARM1(ctx);
struct key_t key = { .pid = pid, .syscall_nr = syscall_nr };
u64 ts = bpf_ktime_get_ns();
start.update(&key, &ts);
return 0;
}
int trace_exit(struct pt_regs *ctx) {
u64 id = bpf_get_current_pid_tgid();
u32 pid = id >> 32;
u64 syscall_nr = PT_REGS_PARM1(ctx);
struct key_t key = { .pid = pid, .syscall_nr = syscall_nr };
u64 *tsp = start.lookup(&key);
if (tsp == 0) return 0;
u64 delta = bpf_ktime_get_ns() - *tsp;
start.delete(&key);
// Convertir a ms
u64 delta_ms = delta / 1000000;
if (delta_ms > 100) {
// Incrementar contador de latencia alta
u64 *counter = latency_hist.lookup(&delta_ms);
if (counter) {
__sync_fetch_and_add(counter, 1);
}
}
return 0;
}
Este programa es simple pero poderoso. Usa un mapa BPF_HASH para guardar timestamps y un array BPF_PERCPU_ARRAY para contar latencias altas.
2. Exportador ebpf_exporter
El exportador de Cloudflare (o el de Elastic) puede cargar este programa y exponer los mapas como métricas. La configuración en YAML sería:
# ebpf_exporter.yaml
programs:
slow_syscalls:
code: |
// Código eBPF aquí (o ruta a archivo .o)
metrics:
counters:
- name: ebpf_slow_syscalls_total
help: "Total de syscalls lentas (>100ms)"
table: latency_hist
index: 0
labels:
- name: pid
size: 4
decoders:
- name: pid
Luego ejecutamos el exportador:
./ebpf_exporter --config.file=ebpf_exporter.yaml --web.listen-address=:9435
3. Configuración de Prometheus
Añadimos un job al prometheus.yml:
scrape_configs:
- job_name: 'ebpf_syscalls'
scrape_interval: 10s
static_configs:
- targets: ['localhost:9435']
Y una regla de alerta para detectar anomalías:
groups:
- name: ebpf_alerts
rules:
- alert: HighSyscallLatency
expr: rate(ebpf_slow_syscalls_total[1m]) > 5
for: 2m
labels:
severity: critical
annotations:
summary: "Alta tasa de syscalls lentas en {{ $labels.pid }}"
[TIP] No te limites a syscalls. Puedes monitorizar
runqueue latency(cuánto espera un proceso para obtener CPU),oom_kill, o incluso la latencia desk_buffen la pila de red.
Detección de anomalías con métricas de kernel
Una vez que tienes las métricas en Prometheus, puedes construir dashboards en Grafana que muestren rendimiento en tiempo real del kernel. Algunas métricas clave que deberías exponer:
ebpf_sched_runqueue_latency: Tiempo que un proceso espera en la cola de ejecución. Un pico indica contención de CPU.ebpf_oom_kills_total: Conteo de procesos matados por OOM. Esto es una anomalía grave.ebpf_net_skb_drop_total: Paquetes descartados en la cola de recepción. Indica que el kernel no puede procesar tráfico suficientemente rápido.ebpf_block_rq_complete_latency: Latencia de operaciones de bloque (disco). Útil para detectar cuellos de botella en almacenamiento.
Ejemplo de alerta para detección de anomalías
# Alarma si la latencia media de runqueue supera 10ms durante 5 minutos
avg_over_time(ebpf_sched_runqueue_latency[5m]) > 0.01
Consideraciones de seguridad y rendimiento
Aunque eBPF es seguro por diseño (el verificador del kernel rechaza código que pueda bloquear o corromper el sistema), hay que tener cuidado:
- Overhead de muestreo: Si te enganchas a cada syscall en un servidor con 1000 peticiones/segundo, puedes agregar un 1-2% de overhead. Usa muestreo estadístico o limita las sondas a eventos específicos.
- Permisos: Cargar programas eBPF requiere
CAP_BPForoot. En producción, usa contenedores consecurityContext.capabilities.add: ["BPF"]. - Versiones de kernel: Necesitas kernel 4.18+ para la mayoría de features. Para
BPF_PROG_TYPE_SK_LOOKUPoBPF_PROG_TYPE_CGROUP_SYSCTL, necesitas 5.2+.
[WARNING] No cargues programas eBPF no verificados. El verificador del kernel es estricto, pero un bug en tu programa puede causar un panic. Siempre prueba en staging.
Casos de uso reales en 2025
1. Depuración de microservicios en Kubernetes
Un pod de API Gateway empieza a tener latencia alta. Con eBPF, detectas que la syscall connect() tarda 500ms debido a un problema de resolución DNS dentro del kernel (el ndisc está saturado). Sin eBPF, habrías mirado logs de aplicación y no habrías visto nada.
2. Prevención de fugas de memoria en kernel
Un módulo de red (ej. ixgbe) tiene una fuga de memoria en sk_buff. eBPF puede rastrear las asignaciones y liberaciones de kmem_cache_alloc, y alertar cuando el contador de allocated - freed supera un umbral.
3. Seguridad en tiempo real
Detectar execve() de binarios no esperados (ej. mineros de criptomonedas) mediante un programa eBPF que monitoree la syscall y envíe alertas a Prometheus.
Conclusión
El monitoreo avanzado con eBPF y Prometheus no es una moda pasajera; es la evolución natural de la observabilidad en servidores Linux. Te permite ir más allá de las métricas de caja negra y entender qué está haciendo realmente el kernel en cada ciclo de reloj.
En 2025, con la proliferación de hardware heterogéneo y cargas de trabajo serverless, tener visibilidad a nivel de kernel es un diferenciador clave. Herramientas como ebpf_exporter, Pixie y Cilium están democratizando el acceso a estas capacidades.
Mi recomendación: empieza con un solo probe (ej. latencia de syscall), configúralo en Prometheus, crea una alerta, y luego escala. No necesitas monitorear todo de golpe; empieza por lo que duele.
[INFO] Si usas Kubernetes, mira
KubeArmoroFalcopara seguridad basada en eBPF. Si buscas redes,Ciliumes el estándar de facto.
¿Listo para dejar de adivinar y empezar a ver el kernel en tiempo real? Tu futuro yo (y tus usuarios) te lo agradecerán.
