Entradas Mensuales

Síguenos en:

Canal Oficial Telegram de elhacker.NET Grupo Facebook elhacker.NET Twitter elhacker.NET Canal Youtube elhacker.NET Comunidad Steam: Grupo elhacker.NET Mastodon

Entradas populares

PostHeaderIcon eBPF: observabilidad sin sobrecarga en Linux


eBPF revoluciona la observabilidad en Linux al permitir ejecutar programas seguros en el kernel sin modificar su código ni reiniciar el sistema, ofreciendo monitorización en tiempo real con mínima sobrecarga (menos del 1%). Esta tecnología elimina la necesidad de agentes pesados, usando sondas ligeras para analizar rendimiento, tráfico de red y seguridad, con soporte maduro en kernels 5.8+ y distribuciones como Ubuntu 24.04, RHEL 9 y Debian 11.




La monitorización con eBPF está revolucionando la seguridad y gestión en servidores Linux. eBPF permite que se puedan ejecutar programas seguros dentro del kernel de Linux para obtener una visibilidad profunda y en tiempo real. 

eBPF es una tecnología que permite ejecutar programas personalizados de forma segura dentro del kernel de Linux sin necesidad de modificar el código fuente o reiniciar el sistema. Se utiliza principalmente para observar el rendimiento, filtrar tráfico de red y fortalecer la seguridad del sistema mediante el análisis de eventos en tiempo real. En la actualidad, los sysadmins usan eBPF para optimizar la observabilidad, seguridad y rendimiento operativo.

Por qué eBPF cambia las reglas del juego

Si llevas tiempo en esto, sabes que la monitorización siempre ha sido un compromiso. O instalabas agentes pesados que te daban mucha información pero consumían CPU y memoria como si no hubiera un mañana, o te conformabas con métricas superficiales para no lastrar el rendimiento. Con eBPF, esta dicotomía empieza a difuminarse. La idea de poder engancharse directamente al kernel y observar lo que pasa, sin intermediarios, es simplemente revolucionaria.

Ya no hablamos de un agente que se ejecuta como un proceso más, compitiendo por recursos. Hablamos de pequeños programas verificados que se ejecutan en un entorno seguro dentro del propio kernel, como una extensión nativa del sistema operativo. Es pasar de tener un espía siguiendo a un objetivo (con el riesgo de que lo descubran y de que haga ruido) a ser el propio suelo que pisa el objetivo, sintiendo cada uno de sus pasos.

Los agentes de monitorización tradicionales, ya sean para APM (Application Performance Monitoring) o para métricas de sistema, funcionan en el espacio de usuario. Esto significa que, para obtener datos del kernel (como llamadas al sistema, operaciones de red o accesos a disco), necesitan realizar constantes cambios de contexto (de espacio de usuario a espacio de kernel y viceversa). Cada uno de estos saltos, aunque pequeños, suma y genera una sobrecarga. Si lo multiplicas por miles de eventos por segundo en un servidor concurrido, el overhead se vuelve tangible.

eBPF invierte este modelo. En lugar de un agente pesado, cargas «sondas» o programas eBPF extremadamente ligeros y específicos para lo que necesitas.

  • ¿Solo quieres ver la latencia de las consultas DNS? Cargas un programa eBPF que se engancha a las funciones sendto y recvfrom en el socket correspondiente.
  • ¿Necesitas rastrear qué proceso está abriendo un fichero de configuración específico? Enganchas una sonda a la llamada al sistema openat.

Estos programas se compilan a un bytecode que es verificado por el kernel antes de cargarse. Este verificador es como un portero de discoteca extremadamente estricto: se asegura de que el programa no pueda caer en bucles infinitos, no acceda a memoria no autorizada ni pueda, en definitiva, colgar el sistema. Una vez aprobado, el JIT (Just-In-Time) compiler del kernel lo traduce a código máquina nativo, haciéndolo increíblemente rápido. El resultado es una monitorización con un impacto en el rendimiento casi nulo, a veces estimado en menos del 1%.

eBPF soluciona esto con el Verificador. Antes de que un programa eBPF se cargue y se enganche a un evento del kernel, pasa por un proceso de análisis estático extremadamente riguroso.

Si has investigado sobre eBPF antes de, digamos, 2020, probablemente leíste sobre la pesadilla de tener que recompilar tus programas eBPF para cada versión específica del kernel. Ese infierno se acabó gracias a una cosa llamada BTF (BPF Type Format) y el concepto que habilita: CO-RE (Compile Once – Run Everywhere).

La buena noticia es que, a día de hoy, en 2026, eBPF ya no es una rareza exótica. Es una tecnología madura y soportada por todas las distribuciones Linux serias. Sin embargo, no todas las «experiencias eBPF» son iguales, y el diablo, como siempre, está en los detalles de la versión.

  • Kernel Linux: El soporte sólido para BTF empezó a tomar forma alrededor del kernel 5.2, pero se ha ido puliendo mucho desde entonces. Para una experiencia sin sobresaltos, cualquier kernel 5.8 o superior es una apuesta segura.
  • Ubuntu: Desde la versión 20.10 en adelante, Ubuntu incluye BTF por defecto. En la última LTS, Ubuntu 24.04, el soporte es de primera clase.
  • RHEL y derivados (CentOS Stream, Rocky Linux, AlmaLinux): A partir de RHEL 8.2, se empezó a incluir el soporte BTF. En RHEL 9 y sus clones, es una característica estándar y robusta.
  • Debian: Debian habilitó BTF por defecto a partir de la versión 11 («Bullseye»).
  • SLES (SUSE Linux Enterprise Server): SLES 15 SP3 y versiones posteriores también vienen con soporte BTF de serie.

Ya hemos visto el «porqué», ahora vamos a meternos en el fango y ver el «cómo» funciona esto por dentro. Para usar eBPF de verdad y no solo recitar conceptos, hay que entender sus piezas fundamentales.

En este artículo de SysAdminOk desmontar el mecanismo pieza por pieza. Léelo!

De agentes pesados a sondas de micro-impacto

La diferencia fundamental es la «intrusividad». Los agentes de monitorización tradicionales, ya sean para APM (Application Performance Monitoring) o para métricas de sistema, funcionan en el espacio de usuario. Esto significa que, para obtener datos del kernel (como llamadas al sistema, operaciones de red o accesos a disco), necesitan realizar constantes cambios de contexto (de espacio de usuario a espacio de kernel y viceversa). Cada uno de estos saltos, aunque pequeños, suma y genera una sobrecarga. Si lo multiplicas por miles de eventos por segundo en un servidor concurrido, el overhead se vuelve tangible.

Además, estos agentes suelen venir en «paquetes» grandes y monolíticos. ¿Quieres monitorizar solo las conexiones TCP? mala suerte, te instalas el agente completo con sus 200MB de dependencias y su consumo de memoria base, aunque no uses el 90% de sus funcionalidades.

eBPF invierte este modelo. En lugar de un agente pesado, cargas «sondas» o programas eBPF extremadamente ligeros y específicos para lo que necesitas.

  • ¿Solo quieres ver la latencia de las consultas DNS? Cargas un programa eBPF que se engancha a las funciones sendto y recvfrom en el socket correspondiente.
  • ¿Necesitas rastrear qué proceso está abriendo un fichero de configuración específico? Enganchas una sonda a la llamada al sistema openat.

Estos programas se compilan a un bytecode que es verificado por el kernel antes de cargarse. Este verificador es como un portero de discoteca extremadamente estricto: se asegura de que el programa no pueda caer en bucles infinitos, no acceda a memoria no autorizada ni pueda, en definitiva, colgar el sistema. Una vez aprobado, el JIT (Just-In-Time) compiler del kernel lo traduce a código máquina nativo, haciéndolo increíblemente rápido. El resultado es una monitorización con un impacto en el rendimiento casi nulo, a veces estimado en menos del 1%.

Qué ganamos (latencia, granularidad) y qué cedemos (complejidad)

Seamos sinceros, ninguna tecnología es una bala de plata. eBPF nos da superpoderes, pero como siempre, un gran poder conlleva… bueno, ya sabes.

Lo que ganamos, y es mucho:

  • Latencia casi cero en la recolección: Al operar a nivel de kernel, los datos se capturan en tiempo real en el momento exacto en que ocurren los eventos. Esto es oro puro para detectar micro-ráfagas de latencia o problemas transitorios que los agentes tradicionales, con sus ciclos de sondeo, simplemente no ven.
  • Granularidad sin precedentes: Podemos observar prácticamente cualquier cosa. Desde el nivel más bajo, como las interrupciones de hardware, hasta el seguimiento de una petición HTTP a través de toda la pila de red, pasando por el análisis de qué funciones de una aplicación están consumiendo más CPU. Tienes visibilidad completa sin necesidad de modificar el código de la aplicación.
  • Seguridad y estabilidad: El verificador de eBPF es la clave. A diferencia de los módulos del kernel (LKM), un programa eBPF mal hecho no puede provocar un kernel panic. En el peor de los casos, el programa es rechazado y no se carga. Esto hace que experimentar sea mucho más seguro.

Lo que cedemos, o mejor dicho, el peaje a pagar:

  • Complejidad inicial: No vamos a engañarnos, escribir programas eBPF no es como hacer un script en Bash. Requiere conocimientos de C (aunque hay abstracciones en Go, Rust y Python que lo facilitan) y, sobre todo, entender cómo funciona el sistema a bajo nivel. La curva de aprendizaje es más pronunciada que la de configurar un agente estándar.
  • Dependencia del Kernel: La magia de eBPF depende de la versión del kernel de Linux que estés usando. Aunque las funcionalidades básicas están presentes desde hace tiempo (kernel 4.x), muchas de las características avanzadas y los helpers que facilitan la vida requieren kernels más modernos (5.x en adelante). Esto puede ser un problema en entornos con políticas de actualización lentas o que usan distribuciones con kernels más antiguos y de soporte extendido (LTS).
  • Herramientas en evolución: El ecosistema de herramientas alrededor de eBPF está explotando, pero todavía es un campo en desarrollo. A veces, depurar un programa eBPF puede ser complicado, ya que se ejecuta en un entorno muy restringido y las herramientas de debugging no son tan maduras como las del espacio de usuario.

En resumen, eBPF nos da la capacidad de obtener una visibilidad profunda y con un rendimiento que antes era impensable. Sin embargo, exige a cambio una mayor inversión técnica por nuestra parte. Es el paso de ser un simple usuario de herramientas de monitorización a ser un constructor de nuestras propias herramientas, perfectamente adaptadas a nuestras necesidades.

Lo imprescindible para entender eBPF

Vale, ya hemos visto el «porqué», ahora vamos a meternos en el fango y ver el «cómo» funciona esto por dentro. Para usar eBPF de verdad y no solo recitar conceptos, hay que entender sus piezas fundamentales. No te preocupes, no hace falta que saques el manual de ensamblador del kernel, pero sí que tengas claros tres o cuatro conceptos que son el motor de toda esta historia. Vamos a desmontar el mecanismo pieza por pieza.

Programas y mapas: dónde corre el código y dónde vive el estado

La arquitectura de eBPF se sostiene sobre dos pilares: los programas y los mapas. Entender la relación entre ambos es clave.

  • Programas eBPF: Son el «código» que se ejecuta. Piensa en ellos como pequeños fragmentos de lógica, escritos normalmente en un subconjunto de C y compilados a un bytecode especial de eBPF. Estos programas son stateless (sin estado) por diseño. Se ejecutan, hacen su trabajo (inspeccionar datos, contar un evento, etc.) y terminan. No pueden guardar información entre una ejecución y la siguiente. Si un programa se ejecuta porque ha llegado un paquete de red, cuando llegue el siguiente paquete, la ejecución anterior no sabe nada de la nueva.
  • Mapas eBPF (eBPF Maps): Aquí es donde reside el «estado». Los mapas son estructuras de datos genéricas (clave-valor) que viven dentro de la memoria del kernel y a las que tanto los programas eBPF como las aplicaciones en espacio de usuario pueden acceder. Son el puente de comunicación y persistencia. Un programa eBPF puede escribir en un mapa para registrar un evento (por ejemplo, «la IP 1.2.3.4 ha hecho una petición»), y una aplicación en el espacio de usuario (como tu agente de monitorización) puede leer ese mapa para recolectar las métricas. También sirven para que varios programas eBPF se comuniquen entre sí.

Imagina un contador de paquetes. El programa eBPF se ejecuta por cada paquete que llega, lee la IP de origen, y actualiza una entrada en un mapa (un hash map, por ejemplo) donde la clave es la IP y el valor es el número de paquetes. El programa en sí no recuerda nada, toda la inteligencia y el estado se delegan en los mapas.

El verificador y la seguridad del kernel (por qué no “se cuelga”)

Aquí viene la parte que hace que todo esto sea seguro y viable en producción. Si has trabajado con módulos del kernel (LKMs), sabrás el pánico (literalmente, kernel panic) que puede dar cargar código nuevo. Un puntero nulo, un bucle infinito… y adiós servidor.

eBPF soluciona esto con el Verificador. Antes de que un programa eBPF se cargue y se enganche a un evento del kernel, pasa por un proceso de análisis estático extremadamente riguroso. El verificador es como un guardia de seguridad paranoico pero increíblemente eficiente que se asegura de varias cosas:

  • 1
    El programa siempre termina: Analiza todos los posibles caminos del código para garantizar que no hay bucles infinitos. Por diseño, los bucles hacia atrás (que podrían crear bucles infinitos) están prohibidos, aunque sí se permiten bucles acotados que el verificador pueda demostrar que siempre terminan.
  • 2
    No hay accesos a memoria inválida: Se asegura de que el programa no pueda leer o escribir en direcciones de memoria arbitrarias del kernel. Solo puede acceder a la memoria del contexto que se le pasa (como los datos de un paquete de red) y a la de los mapas, y siempre con comprobaciones de límites.
  • 3
    No se puede colgar el sistema: El programa no puede llamar a funciones del kernel de forma arbitraria, solo a un conjunto predefinido de «funciones ayudante» (helper functions) que son seguras y están expuestas por la API de eBPF.
  • 4
    El tamaño es limitado: Los programas tienen una limitación en el número de instrucciones para evitar que sean demasiado complejos.

Si el programa no pasa alguna de estas comprobaciones, el kernel simplemente se niega a cargarlo. Esto nos da una garantía de seguridad que era impensable con los módulos del kernel tradicionales, permitiendo extender la funcionalidad del sistema operativo de forma segura y dinámica.

Hooks disponibles: kprobes/tracepoints/uprobes/LSM/XDP/cgroup

Un programa eBPF no hace nada por sí solo. Necesita «engancharse» a un evento para que el kernel lo ejecute. Estos puntos de enganche se conocen como hooks. La potencia de eBPF reside en la enorme variedad de hooks disponibles, que nos dan visibilidad en casi cualquier subsistema. Los más importantes son:

  • Kprobes (Kernel Probes): Permiten engancharse a la entrada (kprobe) o a la salida (kretprobe) de prácticamente cualquier función del kernel. Son increíblemente flexibles y potentes para la depuración dinámica, pero tienen una pega: dependen de los símbolos internos del kernel, que pueden cambiar entre versiones, haciéndolos algo frágiles.
  • Tracepoints: Son puntos de instrumentación estáticos y estables colocados por los desarrolladores del kernel en puntos clave del código (ej. eventos de red, llamadas al sistema, planificador). Al ser una API estable, son la opción preferida para herramientas de producción, ya que es menos probable que se rompan con una actualización del kernel.
  • Uprobes (User Probes): Son el equivalente a los kprobes, pero para el espacio de usuario. Permiten engancharse a funciones dentro de una aplicación o una librería (como OpenSSL, libc, etc.) sin modificar su código. Ideal para obtener telemetría a nivel de aplicación.
  • LSM (Linux Security Modules): Estos hooks se integran con el framework de seguridad de Linux. Permiten a los programas eBPF tomar decisiones de seguridad, como permitir o denegar el acceso a un fichero, una conexión de red o la ejecución de un proceso. Es la base de muchas herramientas de seguridad modernas.
  • XDP (eXpress Data Path): Este es el hook de red de más alto rendimiento. Un programa eBPF enganchado a XDP se ejecuta directamente en el driver de la tarjeta de red, tan pronto como un paquete es recibido y antes de que el kernel asigne memoria para él (sk_buff). Esto permite tomar decisiones a velocidades de millones de paquetes por segundo, ideal para firewalls, balanceadores de carga y mitigación de DDoS.
  • cgroup hooks: Permiten ejecutar programas eBPF cuando un proceso entra en un cgroup o cuando el tráfico de red entra o sale de un cgroup. Esto es fundamental en el mundo de los contenedores, ya que permite aplicar políticas de red o de recursos por contenedor.

Cuándo usar cada hook según la señal que buscas

Elegir el hook correcto es como elegir la herramienta adecuada: podrías clavar un tornillo con un martillo, pero no es lo ideal. Aquí una guía rápida:

Intención / ObjetivoHook Recomendado¿Por qué?
Observabilidad de Red de Alto Rendimiento (DDoS, FW)XDPSe ejecuta antes que nada en la pila de red, máximo rendimiento.
Monitorizar el comportamiento de una app (sin tocar código)uprobesPermite inspeccionar llamadas a funciones específicas dentro de binarios de usuario.
Métricas estables y a largo plazo del kernel (syscalls, red)tracepointsOfrecen una ABI estable. Tu herramienta no se romperá con cada actualización del kernel.
Depuración profunda y exploratoria del kernelkprobesFlexibilidad total para engancharse a casi cualquier función interna, aunque sea inestable.
Aplicar políticas de seguridad (ej. «este proceso no puede abrir este fichero»)LSMSe integra directamente con los puntos de decisión de seguridad del kernel.
Aplicar políticas de red a contenedores (Kubernetes)cgroup hooksAsocia la lógica eBPF directamente a los grupos de control que aíslan los contenedores.

Requisitos y compatibilidad en 2026

Muy bien, ya entendimos la magia, pero ahora toca la parte menos glamurosa pero más importante: la fontanería. ¿Puedo usar esto en mis servidores? ¿Qué necesito? Porque de nada sirve una tecnología revolucionaria si solo funciona en el portátil de un desarrollador del kernel.

La buena noticia es que, a día de hoy, en 2026, eBPF ya no es una rareza exótica. Es una tecnología madura y soportada por todas las distribuciones Linux serias. Sin embargo, no todas las «experiencias eBPF» son iguales, y el diablo, como siempre, está en los detalles de la versión.

Kernel, BTF y distros: qué versiones te permiten CO-RE

Si has investigado sobre eBPF antes de, digamos, 2020, probablemente leíste sobre la pesadilla de tener que recompilar tus programas eBPF para cada versión específica del kernel. Era un auténtico dolor de cabeza. Cada vez que actualizabas un servidor, tenías que tener las cabeceras del kernel (kernel-headers) instaladas y un compilador (Clang/LLVM) a mano para que la herramienta de monitorización pudiera adaptar su programa eBPF a las estructuras de datos de ese kernel concreto. Un lío.

Ese infierno se acabó gracias a una cosa llamada BTF (BPF Type Format) y el concepto que habilita: CO-RE (Compile Once – Run Everywhere).

BTF es, en esencia, una forma de empaquetar la información de depuración sobre las estructuras de datos del kernel directamente dentro de la propia imagen del kernel (el vmlinuz). Piensa en ello como si el kernel llevara consigo un «mapa» de sus propias estructuras internas. Con este mapa, una herramienta eBPF moderna puede leerlo al arrancar y adaptar dinámicamente sus programas para que coincidan con la versión del kernel en la que se está ejecutando. Sin necesidad de compiladores ni cabeceras. Es un cambio brutal en la usabilidad.

Entonces, la pregunta clave es:

¿Qué necesito para tener CO-RE?

La respuesta corta es: un kernel con soporte para BTF. La mayoría de las distribuciones principales ya lo habilitan por defecto en sus versiones recientes:

  • Kernel Linux: El soporte sólido para BTF empezó a tomar forma alrededor del kernel 5.2, pero se ha ido puliendo mucho desde entonces. Para una experiencia sin sobresaltos, cualquier kernel 5.8 o superior es una apuesta segura.
  • Ubuntu: Desde la versión 20.10 en adelante, Ubuntu incluye BTF por defecto. En la última LTS, Ubuntu 24.04, el soporte es de primera clase.
  • RHEL y derivados (CentOS Stream, Rocky Linux, AlmaLinux): A partir de RHEL 8.2, se empezó a incluir el soporte BTF. En RHEL 9 y sus clones, es una característica estándar y robusta.
  • Debian: Debian habilitó BTF por defecto a partir de la versión 11 («Bullseye»).
  • SLES (SUSE Linux Enterprise Server): SLES 15 SP3 y versiones posteriores también vienen con soporte BTF de serie.

En resumen, si estás trabajando con servidores desplegados en los últimos 2-3 años y los mantienes actualizados, lo más probable es que ya tengas todo lo necesario para disfrutar de la portabilidad de CO-RE. Puedes comprobarlo tú mismo con un simple comando en tu servidor:

Si este fichero existe, tienes soporte BTF.

Copy to Clipboard

Si el comando te devuelve la ruta del fichero, ¡Enhorabuena! Estás en el mundo moderno de eBPF. Si no, es hora de empezar a planificar esa actualización del kernel.

Contenedores y hosts mixtos: límites prácticos de permisos

Ahora la pregunta del millón en cualquier entorno cloud native: ¿cómo funciona esto con Docker, Kubernetes y compañía? Porque mis aplicaciones corren en contenedores, no directamente sobre el metal.

Aquí es donde la cosa se pone delicada. Por su naturaleza, eBPF opera a nivel del kernel del host. Un contenedor comparte el kernel con el host y con otros contenedores, así que un programa eBPF cargado desde un contenedor tiene, potencialmente, visibilidad sobre todo lo que ocurre en el nodo. Esto es increíblemente potente para herramientas de observabilidad y seguridad (como Cilium o Falco), pero también es un riesgo de seguridad si no se gestiona bien.

Por defecto, un contenedor no tiene permisos para cargar programas eBPF. Y menos mal. Para hacerlo, el proceso dentro del contenedor necesita privilegios especiales. Históricamente, la única forma era darle la capacidad CAP_SYS_ADMIN, que es básicamente entregarle las llaves del reino. Es la capacidad más poderosa, permitiendo hacer casi cualquier cosa, desde montar dispositivos hasta cargar módulos del kernel. Demasiado arriesgado para la mayoría de los casos de uso.

Afortunadamente, el kernel evolucionó. A partir del kernel 5.8, se introdujo una capacidad mucho más granular y segura: CAP_BPF. Esta capacidad otorga a un proceso el derecho a usar la llamada al sistema bpf() para cargar programas y gestionar mapas, pero sin concederle otros superpoderes de CAP_SYS_ADMIN.

En la práctica, en un entorno Kubernetes, esto significa que las herramientas de monitorización o red basadas en eBPF (que suelen desplegarse como un DaemonSet) necesitarán que su SecurityContext especifique estas capacidades. No es «rootless» en el sentido estricto, pero es un modelo de privilegios mínimos mucho más seguro que el todo o nada de antes.

Montaje de bpffs, capacidades y ejecución sin root (cuando aplica)

Hay otro componente en esta ecuación de permisos: el sistema de ficheros virtual de BPF, o bpffs.

¿Qué es esto? Es un pequeño sistema de ficheros virtual, similar a /proc o /sys, que se usa para «pinnear» (anclar) programas y mapas de eBPF. Cuando un proceso carga un programa o crea un mapa, estos suelen desaparecer cuando el proceso termina. Si los «pineas» en el bpffs, se convierten en nodos del sistema de ficheros (simples ficheros) que persisten. Esto permite que otras aplicaciones se comuniquen con ellos o que el estado se mantenga aunque el proceso original se reinicie. Es fundamental para herramientas que operan como servicios.

Para que esto funcione, primero hay que montar el bpffs, normalmente en /sys/fs/bpf. Esto, por supuesto, requiere privilegios. Se suele hacer una sola vez en el arranque del sistema con una línea en /etc/fstab o con un servicio de systemd.

Comando manual para montar el bpffs.

Copy to Clipboard

Una vez montado, la interacción con los objetos pineados depende de los permisos de los ficheros y directorios dentro de /sys/fs/bpf, como con cualquier otro sistema de ficheros.

Entonces,

¿Es posible la ejecución «sin root»?

Depende de lo que queramos decir con eso.

  • 1
    Cargar programas de observabilidad potentes (kprobes, XDP, etc.): No, esto no se puede hacer sin privilegios. Necesitarás, como mínimo, CAP_BPF y a menudo también CAP_PERFMON (para acceder a ciertos tracepoints) o incluso CAP_NET_ADMIN (para programas de red). Olvídate de que un usuario normal pueda hacer esto.
  • 2
    Interactuar con mapas/programas ya cargados: Sí, esto es posible. Un proceso con privilegios (tu agente de monitorización, por ejemplo) puede cargar los programas y crear los mapas en el bpffs, y luego establecer permisos para que una aplicación sin privilegios pueda leer los datos de los mapas. Este es un patrón común: un daemon privilegiado recolecta los datos y los expone de forma segura a otros procesos.

Así que el modelo de trabajo realista en 2026 es este: un servicio centralizado en el host (o un pod de DaemonSet en Kubernetes) se ejecuta con las capacidades CAP_BPF y CAP_PERFMON bien definidas. Este servicio se encarga de cargar y gestionar el ciclo de vida de los programas eBPF y de pinear los mapas necesarios en bpffs. El resto de aplicaciones, si necesitan los datos, los leen de los mapas a través del sistema de ficheros, con permisos de UNIX estándar. Es un equilibrio sensato entre poder y seguridad.

De la señal al panel: el pipeline de observabilidad con eBPF

Ok, la teoría está clara. Sabemos que podemos enganchar código a eventos del kernel de forma segura. Genial. Pero, ¿cómo pasa esa información de ser un evento fugaz dentro del kernel a una línea en un gráfico de Grafana o a una alerta de Slack? Esa es la verdadera miga del asunto. No basta con capturar datos, hay que transportarlos, procesarlos y darles sentido. Este es el viaje, el pipeline completo de un dato de telemetría nacido en eBPF.

Captura → agregación en kernel → exportación a user space

La estrategia más importante que eBPF nos obliga a aprender es a ser selectivos. Si intentas enviar cada evento individual desde el kernel al espacio de usuario, vas a tener un problema. Y gordo. Imagina un servidor concurrido: millones de llamadas al sistema, paquetes de red o accesos a ficheros por segundo. Intentar enviar todo eso crearía un «incendio» de datos que saturaría la CPU, solo para mover información de un lado a otro. El overhead de tu «monitorización sin overhead» se dispararía.

El patrón correcto es pensar como un embudo: filtrar y agregar tan pronto como sea posible.

Captura:

Un programa eBPF se activa en un hook (ej. tracepoint:syscalls:sys_enter_openat). En este punto, tiene acceso al contexto completo del evento: qué proceso lo ha llamado, con qué argumentos, etc.

Agregación en kernel:

Aquí está la magia. En lugar de enviar inmediatamente el evento, lo procesamos dentro del kernel. Usamos mapas eBPF para agregar la información.

  • Ejemplo de contador: Queremos contar cuántas veces cada proceso abre el fichero /etc/passwd. El programa eBPF se ejecuta, obtiene el nombre del proceso (comm) y el nombre del fichero. Si el fichero es /etc/passwd, busca el nombre del proceso en un mapa hash y simplemente incrementa un contador. Eso es todo. El espacio de usuario no ve los millones de eventos, solo lee el mapa cada 10 segundos para ver los totales.
  • Ejemplo de histograma: Queremos medir la latencia de las consultas DNS. Enganchamos un kprobe al inicio de la función de envío de paquetes y un kretprobe a su retorno. En el inicio, guardamos el timestamp en un mapa. Al retorno, calculamos la diferencia (fin – inicio), y en lugar de enviar la latencia, la clasificamos en un bucket de un mapa de tipo histograma. Así, el kernel ya nos da la distribución de latencias pre-calculada.

Exportación a User Space:

El agente de monitorización que corre en espacio de usuario adopta un rol pasivo. Periódicamente (ej. cada X segundos), lee los mapas de eBPF para recolectar los datos ya agregados. Este modelo «pull» es infinitamente más eficiente que un «push» constante desde el kernel.

Este enfoque de «agregar primero» reduce el tráfico entre el kernel y el espacio de usuario en órdenes de magnitud, y es la clave para lograr una observabilidad de alto rendimiento con un impacto mínimo.

Buffers (ring/perf), tasas, backpressure y pérdida de eventos

Claro, no siempre podemos agregar. A veces necesitamos el evento crudo, por ejemplo, para una alerta de seguridad («¡El proceso nginx ha intentado abrir /etc/shadow!»). En estos casos, no nos vale un contador, necesitamos los detalles del evento individual.

Para esto, se usan buffers de memoria compartida para pasar eventos del kernel al espacio de usuario. Históricamente, se usaba el perf buffer, que en realidad es un conjunto de buffers, uno por cada CPU. Funcionaba, pero tenía problemas: los eventos de diferentes CPUs podían llegar desordenados y el uso de memoria no era óptimo.

Ring buffer

La solución moderna, disponible desde el kernel 5.8, es el ring buffer (BPF_MAP_TYPE_RINGBUF). Es un único buffer MPSC (Múltiples Productores, Consumidor Único) que soluciona los problemas del perf buffer:

  • Garantiza el orden de los eventos tal y como ocurrieron.
  • Es mucho más eficiente en el uso de la memoria.
  • Ofrece una API más cómoda para reservar espacio y enviar datos.

Pero, ¿qué pasa si el agente en espacio de usuario no puede leer el buffer a la misma velocidad que el kernel escribe? Esto es lo que se conoce como backpressure. Y la respuesta del kernel es simple y brutal: los eventos se pierden. El kernel no puede permitirse el lujo de esperar a un proceso lento de espacio de usuario. Si el buffer está lleno cuando llega un nuevo evento, se descarta.

Una herramienta de monitorización bien diseñada debe tener esto en cuenta. El propio programa eBPF, al intentar enviar un evento y ver que el buffer está lleno, debe incrementar un contador de «eventos perdidos» en un mapa separado. De esta forma, tu dashboard no solo mostrará los eventos que has recibido, sino también un aviso crítico que diga «¡Ojo, me he perdido 1.5 millones de eventos en los últimos 5 minutos!». Sin ese contador, estarías ciego a tus propios puntos ciegos.

Etiquetado por proceso, cgroup, pod y namespace (trazabilidad real)

Hemos capturado un evento, genial. Sabemos que el proceso con PID 12345 ha hecho algo. En un servidor de hace 10 años, eso sería suficiente. En un clúster de Kubernetes, el PID 12345 no significa absolutamente nada. ¿Es parte del pod de frontend? ¿Del de la base de datos? ¿Pertenece al namespace produccion o al de staging? Sin ese contexto, el dato es inútil.

Aquí es donde se completa el pipeline. El programa eBPF puede enriquecer el dato con la información que tiene a mano en el kernel. Usando funciones helper, puede obtener metadatos de bajo nivel en el momento de la captura:

  • bpf_get_current_pid_tgid(): Obtiene el ID del proceso y del thread.
  • bpf_get_current_comm(): Obtiene el nombre del proceso (ej. «nginx»).
  • bpf_get_current_cgroup_id(): Obtiene el ID numérico del cgroup al que pertenece el proceso. Este es el ancla que nos une al mundo de los contenedores.

Agente en espacio de usuario

El programa eBPF captura el evento y lo etiqueta con este ID de cgroup. Pero el kernel no sabe qué es un «Pod» o un «Deployment». La pieza final del puzzle la pone el agente en espacio de usuario.

Este agente tiene una doble vida. Por un lado, está leyendo los datos de los mapas y buffers de eBPF. Por otro, está constantemente hablando con la API del kubelet o del runtime de contenedores (Docker, containerd). Con esa información, construye y mantiene en su memoria un mapa de correspondencias:

  • Cgroup ID 54321 → Container ID abc123def
  • Container ID abc123def → Pod frontend-7b5d… del Namespace produccion, con las etiquetas app=webapp, tier=frontend.

Cuando el agente recibe un evento de eBPF etiquetado con el Cgroup ID 54321, hace una búsqueda en su caché, lo enriquece con toda la metadata de Kubernetes y, solo entonces, lo envía al sistema de visualización o alertas.

Este proceso de «costura» o enriquecimiento en espacio de usuario es lo que transforma un evento críptico del kernel en una pieza de información accionable. Además, con contexto completo en un entorno cloud-native. Es el paso final que nos lleva de la señal en el kernel al panel de control.

Qué puedes medir con “casi cero” impacto

Aquí es donde eBPF pasa de ser una tecnología interesante a una herramienta indispensable en el arsenal de cualquier SysAdmin o SRE. La promesa del «casi cero» impacto no es marketing; es una consecuencia directa de su arquitectura. Como el trabajo pesado de filtrado y agregación se hace en el kernel, evitamos el coste brutal de copiar cantidades masivas de datos al espacio de usuario.

El resultado es que podemos obtener métricas increíblemente granulares de los subsistemas más críticos del sistema operativo, en producción y de forma continua, sin miedo a «matar» el servidor con la propia herramienta de monitorización. Vamos a ver qué tipo de «joyas» podemos extraer.

Red: latencia TCP, retransmisiones, colas y DNS “lento”

La red es, probablemente, el área donde eBPF brilla con más intensidad. Antes, para depurar problemas de red, nuestra principal herramienta era tcpdump, que es como intentar beber de una boca de incendios. Funciona para análisis post-mortem, pero su overhead lo hace inviable para una monitorización continua. eBPF cambia las reglas del juego.

Podemos engancharnos a puntos clave de la pila TCP/IP para extraer métricas de oro:

  • Latencia de conexión TCP: Al enganchar un kprobe al inicio del handshake (tcp_v4_connect) y otro a su finalización, podemos medir con precisión cuánto tarda en establecerse una conexión TCP (el famoso SYN -> SYN/ACK). Herramientas como tcpconnlat del toolkit BCC hacen exactamente esto.
  • Retransmisiones TCP: Una retransmisión es una señal inequívoca de pérdida de paquetes o congestión. Podemos enganchar un programa eBPF al tracepoint tcp:tcp_retransmit_skb que se activa justo cuando el kernel decide reenviar un paquete. Esto nos permite contar, por conexión, cuántas retransmisiones están ocurriendo y detectar problemas de red antes de que los usuarios se quejen.
  • Latencia de DNS: ¿Cuántas veces hemos culpado a la red cuando el problema era un servidor DNS lento? eBPF puede resolver este dilema. Enganchando uprobes a las funciones de resolución de nombres en la libc (como getaddrinfo) o monitorizando el tráfico UDP en el puerto 53, podemos medir exactamente cuánto tiempo tarda cada consulta DNS, identificar los dominios más lentos y saber qué procesos las están realizando.

Histogramas p50/p95/p99 por servicio o puerto

Los promedios mienten. Una latencia promedio de 50ms puede ocultar que el 5% de tus usuarios (los que pagan, probablemente) están sufriendo latencias de 2 segundos. La verdadera historia está en los percentiles.

Aquí es donde los mapas de eBPF de tipo BPF_MAP_TYPE_HISTOGRAM son una maravilla. Nuestro programa eBPF, en lugar de enviar cada medida de latencia individual al espacio de usuario, la clasifica directamente en un bucket del histograma dentro del kernel. Por ejemplo, al medir la latencia de una petición a un servicio en el puerto 443, calculamos el delta de tiempo y hacemos algo como: bpf_map_update_elem(lat_histogram, &slot, 1, BPF_ANY).

El agente de espacio de usuario solo tiene que leer este mapa cada pocos segundos para obtener una distribución completa de las latencias. A partir de ahí, calcular los percentiles p50 (la mediana), p95 y p99 es trivial y computacionalmente barato. Esto nos da una visión precisa y realista del rendimiento que experimentan nuestros usuarios, con un overhead mínimo.

E/S y sistema de archivos: IOPS, colas y esperas por dispositivo

Los problemas de disco son de los más difíciles de diagnosticar. ¿Es el hardware? ¿El sistema de archivos? ¿Una aplicación haciendo un mal patrón de acceso? eBPF nos permite diseccionar la pila de E/S.

Enganchándonos a los tracepoints del subsistema de bloque (block:*), podemos obtener una visibilidad sin precedentes:

  • Latencia por operación: Herramientas como biolatency (parte de BCC) miden el tiempo desde que una petición de E/S se envía al dispositivo hasta que se completa. Esto nos permite crear histogramas de latencia para cada disco, separando lecturas de escrituras, y detectar al instante si un dispositivo se está degradando.
  • Profundidad de cola (Queue Depth): Podemos rastrear cuántas peticiones de E/S están encoladas esperando a ser atendidas por el dispositivo. Una cola que crece constantemente es un síntoma claro de que el disco está saturado.
  • Patrones de acceso: Herramientas como biopattern pueden diferenciar entre accesos secuenciales y aleatorios. Esto es útil para optimizar bases de datos. Si esperas escrituras secuenciales en tu log de transacciones pero ves un patrón 100% aleatorio, has encontrado un problema de configuración.
  • Rendimiento del sistema de archivos: Más arriba en la pila, podemos usar herramientas como fileslower o ext4slower para ver qué operaciones de sistema de archivos (lecturas, escrituras, fsync) están tardando más de un umbral determinado, y qué proceso es el culpable.

CPU y scheduler: run queue, context switches y throttling por cgroup

Cuando una aplicación va lenta y el top muestra CPU de sobra, el siguiente sospechoso es el planificador (scheduler). Quizás la aplicación quiere ejecutarse, pero el scheduler no le da tiempo de CPU. Este tipo de problemas de «latencia de run queue» eran históricamente muy difíciles de cuantificar.

Con eBPF, podemos engancharnos a los tracepoints del scheduler (sched:*) para medir con precisión:

  • Latencia de la Run Queue: Herramientas como runqlat miden el tiempo que pasa desde que un proceso está listo para ejecutarse (wakeup) hasta que realmente se le asigna una CPU (sched_switch). Esto nos ayuda a detectar si el sistema está sobrecargado de procesos, incluso si el uso de CPU no parece ser del 100%.
  • Cambios de contexto (Context Switches): Un número excesivo de cambios de contexto puede ser un síntoma de «thrashing», donde el sistema gasta más tiempo cambiando entre tareas que haciendo trabajo útil.
  • CPU Throttling por cgroup: Este es el dolor de cabeza número uno en Kubernetes. Un contenedor puede tener un límite de CPU (ej. 500 millicores). Cuando excede su cuota en un intervalo de tiempo, el kernel lo «congela» (throttle) por un breve período. Desde fuera, el uso de CPU del pod parece estar por debajo del límite, pero la aplicación sufre picos de latencia inexplicables. eBPF puede capturar directamente estos eventos de throttling del cgroup, dándonos la prueba irrefutable («el pod frontend-xyz fue throttled durante 300ms en el último segundo») y resolviendo el misterio.

Syscalls y librerías: tiempos por endpoint y ruta crítica

Finalmente, podemos subir un nivel y mirar no solo el kernel, sino el comportamiento de las propias aplicaciones, ¡sin tocar su código!

Usando tracepoints para las llamadas al sistema y uprobes para las librerías de espacio de usuario, podemos trazar la ruta crítica de una petición.

Imagina una petición que llega a tu aplicación web. Podemos:

  • 1
    Enganchar un uprobe a la función main_handler de tu aplicación Go o Java. En este punto, guardamos un timestamp de inicio.
  • 2
    Trazar las llamadas al sistema clave que realiza ese hilo: read() para leer datos del socket, write() para escribir en un log, sendto() para hacer una consulta a otra API interna.
  • 3
    Enganchar un uprobe al final de la función main_handler para registrar el timestamp de fin.

Al correlacionar todos estos eventos, podemos reconstruir el ciclo de vida completo de la petición: «La petición X tardó 200ms en total, de los cuales 150ms se pasaron esperando una respuesta de la base de datos (syscall recvfrom), 10ms escribiendo logs (syscall write) y 40ms en computación pura». Este nivel de detalle, obtenido de forma segura y en producción, es el santo grial del profiling de rendimiento.

Perfiles continuos sin frenar la app

El profiling ha sido durante mucho tiempo una especie de «arte oscura». Todos sabemos que es la herramienta definitiva para encontrar cuellos de botella, pero la idea de ejecutar un profiler en producción era, hasta hace poco, casi un tabú. Las herramientas tradicionales, aunque potentes, solían ser tan pesadas que el propio acto de medir el rendimiento alteraba (o directamente hundía) el sistema. Era algo que hacías en un entorno de pre-producción o, si te atrevías, durante una ventana de mantenimiento a las 3 de la mañana.

eBPF cambia radicalmente este paradigma. Mueve el acto de muestrear (sampling) al kernel. Ya no es necesario un agente pesado en espacio de usuario que detenga procesos para hurgar en sus entrañas. Un programa eBPF puede capturar los stack traces (las «pilas de llamadas» que nos dicen qué función llamó a qué otra) de forma segura y con una eficiencia asombrosa.

Esto nos abre las puertas al perfilado continuo (continuous profiling): la capacidad de tener un profiler funcionando siempre, en todos tus servidores de producción. Ya no es una herramienta de emergencia, sino una fuente constante de datos sobre la salud y eficiencia de tu código. Es como pasar de hacer una radiografía puntual cuando sospechas que algo va mal, a tener un TAC en tiempo real 24/7. Las implicaciones para la optimización de costes y la resolución de incidentes son enormes.

CPU profiling con eBPF vs perf clásico

Para entender por qué eBPF es un salto cualitativo, comparémoslo con la herramienta clásica por excelencia: perf. perf es una navaja suiza increíblemente potente, parte del propio kernel de Linux. Para hacer profiling de CPU, perf utiliza el subsistema perf_events para tomar muestras de en qué función se encuentra la CPU en un momento dado. Es el estándar de oro para el análisis de rendimiento en Linux.

perf en grandes entornos de producción

Sin embargo, perf tiene sus «peros» en entornos de producción a gran escala:

  • Overhead de la recolección: Aunque es muy eficiente, generar los datos de perf y, sobre todo, procesarlos después (el famoso perf script) puede consumir bastantes recursos de CPU y disco, especialmente si lo haces de forma continua.
  • Complejidad en contenedores: Usar perf para analizar un proceso específico dentro de un contenedor desde el host es posible, pero puede ser engorroso y requiere gestionar correctamente los espacios de nombres y los símbolos.

El profiling con eBPF aborda estos puntos débiles. Un profiler basado en eBPF, como los que utilizan herramientas como Parca o Pyroscope, se engancha a los mismos eventos de temporizador del kernel que perf. Cada vez que salta el temporizador (por ejemplo, 100 veces por segundo), el programa eBPF se activa, captura el stack trace del proceso que se estaba ejecutando en esa CPU en ese preciso instante y lo guarda en un mapa eBPF.

La diferencia clave es la eficiencia de la captura y la agregación inicial dentro del kernel. El programa eBPF puede contar las ocurrencias de cada stack trace único directamente en un mapa hash. Esto significa que el agente de espacio de usuario no recibe un torrente de miles de muestras por segundo, sino que periódicamente recoge un resumen ya agregado de los «puntos calientes». Este enfoque reduce drásticamente el overhead de mover y procesar los datos, haciendo factible el perfilado continuo con un impacto mínimo.

Flamegraphs continuos y atribución a contenedores

El resultado final de un profiling de CPU suele ser un flame graph. Esta visualización, popularizada por Brendan Gregg, es una forma increíblemente intuitiva de ver dónde se está consumiendo el tiempo de CPU. Cada caja en el gráfico es una función, y el ancho de la caja es proporcional a la frecuencia con la que apareció en las muestras. Los «picos» anchos en la parte superior del gráfico son tus culpables.

La verdadera revolución es tener estos flame graphs de forma continua y para todo el clúster. Herramientas como Parca se especializan precisamente en esto: ingieren los datos de profiling de los agentes eBPF en cada nodo y te permiten explorar los flame graphs a lo largo del tiempo. Puedes seleccionar un pico de CPU de hace tres horas en tu dashboard de métricas y saltar directamente al flame graph exacto de ese momento para ver qué código lo causó.

Pero, ¿cómo sabemos a qué servicio, pod o contenedor pertenece una función caliente? Aquí es donde se une todo lo que hemos visto. El programa eBPF, en el momento de capturar el stack trace, también captura el cgroup_id del proceso. El agente en espacio de usuario, que está hablando con la API de Kubernetes, enriquece esa muestra con los metadatos correspondientes: «este stack trace pertenece al pod frontend-xyz del namespace produccion».

Esto es un cambio de juego. Permite filtrar y agrupar los datos de profiling por etiquetas de Kubernetes. Puedes pedir un flame graph agregado de todos los pods con la etiqueta app=checkout-service, comparar el flame graph de la versión v1.2 con la v1.3 para detectar regresiones, o aislar el perfil de un único pod problemático. La atribución se vuelve precisa y nativa del entorno en el que trabajamos.

Muestreo, periodos y costes aceptables por nodo

Nada es gratis, ni siquiera con eBPF. El profiling continuo tiene un coste, pero la clave es que es increíblemente bajo y predecible. El factor principal que determina el overhead es la frecuencia de muestreo.

Una frecuencia típica para profiling de CPU es de 99Hz o 101Hz. Se suelen usar números primos para evitar sincronizarse accidentalmente con otras tareas periódicas del sistema, lo que podría sesgar los resultados. Esto significa que tomamos una muestra 99 o 101 veces por segundo en cada CPU.

Con esta configuración, el overhead de CPU de un agente de profiling moderno basado en eBPF suele estar en el rango del 1-3%. Algunos proyectos altamente optimizados reportan incluso menos del 1% de impacto. Comparemos esto con los profilers tradicionales, que fácilmente podían introducir un 10-20% de overhead o más, lo que los hacía insostenibles para un uso continuo en producción.

Este coste tan bajo es lo que hace que la balanza se incline. Pagar un peaje de un 1-3% de CPU en cada nodo a cambio de tener visibilidad completa y continua a nivel de código sobre el rendimiento de todo tu clúster es un negocio redondo para cualquier equipo de SRE o desarrollo. Permite optimizar el código de forma proactiva, reducir los costes de la nube encontrando ineficiencias y resolver incidentes de rendimiento en una fracción del tiempo que llevaba antes. El profiling ha dejado de ser una herramienta de emergencia para convertirse en un pilar fundamental de la observabilidad moderna.

Herramientas que mandan en 2026

Vale, la tecnología es una pasada, pero ¿cómo la usamos en el día a día? Nadie empieza escribiendo código eBPF en C desde cero para resolver un problema a las 2 de la mañana. El ecosistema ha madurado una barbaridad y hoy tenemos un abanico de herramientas que cubren distintos casos de uso, desde la exploración rápida hasta agentes de producción a prueba de balas. Vamos a clasificar el «quién es quién» en el mundo eBPF.

bpftrace/BCC para explorar y aprender rápido

Aquí es donde casi todos empezamos. Piensa en bpftrace y BCC (BPF Compiler Collection) como tu navaja suiza para el análisis de sistemas. Son las herramientas que usas cuando aterrizas en un servidor en llamas y necesitas entender qué diablos está pasando, ahora.

BCC es una colección de herramientas y un framework, principalmente en Python y Lua, que te permite escribir scripts para tareas de análisis muy específicas. Es increíblemente potente y la colección de herramientas pre-hechas (opensnoop, execsnoop, tcplife, etc.) es un recurso de aprendizaje brutal. El «pero» de BCC es que, tradicionalmente, compila el código eBPF en la máquina de destino en tiempo de ejecución. Esto significa que necesita tener las cabeceras del kernel y un compilador (LLVM/Clang) instalado, lo que puede ser un engorro y añadir un overhead considerable, sobre todo al inicio.

bpftrace, por otro lado, es la respuesta a «¿y si tuviéramos algo como awk o sed pero para el kernel?». Es un lenguaje de tracing de alto nivel que te permite escribir one-liners increíblemente potentes directamente en la terminal. ¿Quieres ver qué proceso está abriendo un fichero específico? Una sola línea de bpftrace te lo dice. Su sintaxis es mucho más concisa y está diseñada para el análisis ad-hoc.

La línea divisoria es sencilla: si estás depurando un problema en vivo, aprendiendo o prototipando una idea, bpftrace y BCC son tus mejores amigos. Te dan una agilidad que es difícil de superar. Para un agente que va a correr de forma permanente en producción, sin embargo, hay una opción mejor.

libbpf + CO-RE para agentes productivos y portables

Cuando la cosa se pone seria y quieres construir un agente de monitorización, profiling o seguridad que corra en toda tu flota, la eficiencia y la portabilidad no son negociables. Aquí es donde libbpf y el paradigma CO-RE (Compile Once – Run Everywhere) entran en juego y se han convertido en el estándar de facto para el desarrollo de aplicaciones eBPF serias.

El problema histórico de eBPF era que un programa compilado para una versión del kernel no funcionaba en otra, porque las estructuras de datos internas del kernel cambian. BCC lo solucionaba recompilando en cada host, con el coste que eso implica.

CO-RE, junto con BTF (BPF Type Format), soluciona esto de raíz. BTF es, básicamente, una especie de «mapa» de las estructuras de datos del kernel que se incluye con los kernels modernos. Cuando compilas tu programa eBPF, este incluye reubicaciones que libbpf (la librería que carga tu programa en el kernel) puede usar en tiempo de carga para «ajustar» los accesos a memoria según el BTF del kernel donde se está ejecutando.

El resultado es un único binario, pequeño y estático, que no necesita dependencias pesadas como LLVM o las cabeceras del kernel en la máquina de destino. Es más rápido, consume muchísima menos memoria (hasta 9 veces menos que su equivalente en BCC, según algunas pruebas) y es infinitamente más fácil de desplegar. Prácticamente todos los proyectos modernos y robustos de eBPF, como Cilium o Falco, han migrado o usan este enfoque. Es el camino para construir software de producción.

Exportadores y colectores: Prometheus/OpenTelemetry con eBPF

eBPF es una fuente de datos increíble, pero no vive en una isla. Necesitamos integrar esas métricas, trazas y logs en nuestros sistemas de observabilidad existentes, como Prometheus y OpenTelemetry. Afortunadamente, la comunidad ha creado los puentes necesarios.

Para Prometheus, existen varios exportadores de eBPF. Uno de los más conocidos es el ebpf_exporter de Cloudflare, que permite cargar programas eBPF y exponer los datos de sus mapas como métricas de Prometheus. Esto te da la flexibilidad de crear tus propias métricas a bajo nivel y engancharlas a tu sistema de dashboards y alertas de siempre.

En el mundo de OpenTelemetry (OTel), la integración es cada vez más profunda. eBPF se está convirtiendo en una de las formas más potentes de generar telemetría sin instrumentación manual. Proyectos como Grafana Beyla, donado a OTel, permiten capturar métricas RED (Requests, Errors, Duration) y trazas de aplicaciones de forma completamente automática, simplemente observando el tráfico de red a nivel de kernel. Esto significa que puedes tener un service map de tus aplicaciones en Go, Rust, o cualquier lenguaje compilado, sin tocar una sola línea de código. La convergencia de eBPF como fuente de datos y OTel como estándar para la telemetría es una de las tendencias más potentes del sector.

Proyectos listos: Cilium/Tetragon (seguridad), Pixie (app-level)

Finalmente, llegamos a los «productos» o proyectos de código abierto que usan eBPF para resolver problemas complejos de forma integral. No tienes que escribir código eBPF, simplemente los despliegas y obtienes el valor.

  • Cilium y Tetragon: Cilium se ha convertido en el CNI (Container Network Interface) de referencia en el mundo Kubernetes, utilizando eBPF para proporcionar networking, observabilidad y seguridad de red de alto rendimiento. Su «hermano», Tetragon, se enfoca específicamente en la observabilidad de seguridad y la aplicación de políticas en tiempo de ejecución (runtime enforcement). Tetragon usa eBPF para monitorizar eventos críticos como accesos a ficheros, llamadas al sistema y escalada de privilegios, todo con contexto de Kubernetes. Puede detectar y hasta bloquear actividades maliciosas en tiempo real, directamente desde el kernel.
  • Pixie: Si lo que buscas es visibilidad a nivel de aplicación sin instrumentación manual, Pixie es una herramienta casi mágica. Aceptado como proyecto Sandbox de la CNCF, Pixie utiliza eBPF para capturar automáticamente telemetría de alto nivel, como peticiones HTTP, gRPC, consultas a bases de datos y DNS. Te da acceso a service maps, latencias, tasas de error y hasta flame graphs de CPU sin que tengas que modificar tu código. Su arquitectura única almacena los datos recientes en el propio clúster, permitiendo un análisis en tiempo real con un overhead mínimo, y puede exportar a sistemas como OpenTelemetry para retención a largo plazo.

Buenas prácticas para “casi cero” overhead

Hablemos claro: «cero overhead» es el unicornio del marketing. Cualquier cosa que hagas en un sistema, por muy optimizada que esté, tiene un coste. La pregunta no es si eBPF tiene overhead, sino si podemos conseguir que ese overhead sea tan ridículamente bajo que resulte insignificante. Y la respuesta es un rotundo sí.

La clave está en ser inteligentes y quirúrgicos. No se trata de encender una manguera de datos y apuntar al kernel, sino de usar un bisturí láser. Con eBPF, el poder reside en hacer el trabajo pesado y la toma de decisiones dentro del kernel, enviando solo la información esencial y ya procesada al espacio de usuario. Si sigues algunas reglas de oro, puedes mantener el impacto en un nivel casi imperceptible.

Agregar en kernel, filtrar temprano (PID/cgroup/puerto)

Este es el mandamiento número uno y el que separa a los profesionales de los novatos. La mayor fuente de overhead no suele ser la ejecución del programa eBPF en sí, sino el coste de enviar cantidades masivas de datos desde el kernel al espacio de usuario. Cada evento enviado implica un cruce de contexto y consumo de memoria en el búfer.

Proceso de datos en origen

La estrategia ganadora es simple: procesa los datos donde se originan.

  • Filtrado agresivo: Tu programa eBPF se va a ejecutar para cada evento al que se enganche. Si estás trazando la syscall openat en un sistema ocupado, eso son miles de activaciones por segundo. Lo primero que debe hacer tu código es decidir si este evento te interesa. ¿Es del PID que estoy monitorizando? ¿Pertenece al cgroup de mi contenedor de Kubernetes? ¿Es en el puerto 8080? Si la respuesta es no, return 0; y sal de ahí inmediatamente. El coste de este chequeo temprano es de unos pocos nanosegundos, mientras que enviar el evento completo a un agente de usuario puede costar microsegundos. La diferencia es de órdenes de magnitud.
  • Agregación en mapas: En lugar de enviar cada evento individual, usa mapas eBPF para agregar los datos en el kernel. Por ejemplo, si quieres contar las llamadas a openat por proceso, no envíes cada llamada. En su lugar, crea un mapa hash donde la clave sea el PID y el valor sea un contador. En tu programa eBPF, obtienes el PID, buscas la entrada en el mapa e incrementas el contador. El agente de espacio de usuario solo tiene que leer el mapa cada 10 o 15 segundos para obtener el resumen completo. Has reducido miles de eventos por segundo a una sola lectura de mapa cada varios segundos.

Piensa en ello como tener un asistente increíblemente rápido dentro del kernel. En vez de pedirle que te traiga cada grano de arena de la playa, le pides que te traiga un informe con el número total de granos.

Muestreo conservador y presupuestos de CPU por nodo

No siempre necesitas el 100% de los datos. Para casos de uso como el profiling, el muestreo (sampling) es tu mejor aliado. La idea es tomar instantáneas del sistema a una frecuencia determinada en lugar de registrar cada acción.

La frecuencia de muestreo es el dial que ajusta el equilibrio entre la granularidad de los datos y el overhead. Un profiler de CPU que muestrea a 99Hz (99 veces por segundo por CPU) suele tener un impacto de CPU del 1-3%. Si lo subes a 199Hz, obtendrás datos más detallados, pero el overhead podría duplicarse.

Aquí es donde entra una mentalidad de SRE: define un presupuesto de recursos para tu agente de observabilidad. Trátalo como cualquier otro servicio en tu clúster. Decide cuál es un coste aceptable (por ejemplo, «el agente de monitorización no debe consumir más del 2% de una CPU en ningún nodo») y ajusta la configuración para mantenerte dentro de ese límite. Esto podría significar reducir la frecuencia de muestreo, desactivar algunas sondas menos críticas o aumentar el intervalo de recolección de métricas.

Evitar prints en caliente; usa mapas y batch

Hay una herramienta en el arsenal de eBPF que debes tratar con extremo respeto y casi nunca usar en producción: bpf_printk. Es el printf del mundo eBPF, una forma rápida de escupir mensajes de depuración al log de trazas del kernel. Es fantástico para desarrollar y depurar, pero es una bomba de relojería para el rendimiento. bpf_printk es lento, síncrono y puede inundar fácilmente los buffers del kernel si se usa en un punto de anclaje muy concurrido.

La forma correcta de enviar datos es a través del búfer de perf (bpf_perf_event_output) o, idealmente, no enviar eventos individuales en absoluto y depender de la lectura de mapas.

Además, cuando interactúas con mapas desde el espacio de usuario, la eficiencia importa. En lugar de leer o actualizar entradas una por una, lo que implica una llamada al sistema por cada elemento, usa las operaciones por lotes (batch operations) que proporciona libbpf. Funciones como bpf_map_lookup_batch() y bpf_map_update_batch() te permiten leer o escribir múltiples elementos con una sola syscall. Esta es una optimización clave para los agentes que necesitan sincronizar grandes cantidades de datos desde los mapas.

Cómo medir tu propio overhead y detectar caídas de sonda

No te fíes de la palabra de nadie. Mide el impacto en tus sistemas y con tu carga de trabajo. La confianza se gana con datos.

  • 1
    Establece una línea base: Antes de desplegar tu agente eBPF, mide el rendimiento de tu aplicación y el consumo de recursos del nodo. Usa herramientas estándar como top, mpstat y benchmarks de aplicación.
  • 2
    Mide después del despliegue: Con el agente funcionando, repite las mediciones. Observa el uso de CPU del proceso del agente. ¿Ha aumentado la latencia de tu aplicación? ¿Ves más cambios de contexto (vmstat)?
  • 3
    Monitoriza al monitor: Tu agente de eBPF es una aplicación crítica y debe ser monitorizada como tal. Una de las métricas más importantes que un buen agente debe exponer es la tasa de muestras perdidas o caídas (lost/dropped samples).

Una caída de sonda ocurre cuando los eventos del kernel se generan más rápido de lo que el agente de espacio de usuario puede procesarlos desde el búfer de perf. El kernel, para protegerse, simplemente descarta los datos. Si esto ocurre, significa que tienes un punto ciego en tu observabilidad y que tus datos son incompletos. Una tasa de caídas distinta de cero es una señal de alerta: o tu programa eBPF es demasiado complejo, la frecuencia de eventos es demasiado alta, o el agente de espacio de usuario no es lo suficientemente eficiente. Es un indicador claro de que necesitas volver a la mesa de diseño y optimizar tu enfoque.

Operación en clusters (sin dolores)

Muy bien, ya hemos visto la potencia de eBPF, pero ahora viene la pregunta del millón para cualquier sysadmin: ¿cómo diantres se gestiona esto a escala, en un cluster de Kubernetes con cientos de nodos, sin volverse loco? Desplegar un agente en tu portátil es una cosa; mantenerlo funcionando, actualizado y siendo fiable en un entorno de producción dinámico es un juego completamente distinto.

Aquí es donde la elegancia del ecosistema moderno de eBPF realmente brilla. Los problemas operativos que nos quitaban el sueño hace años (compatibilidad de kernels, actualizaciones sin caídas, gestión de estado) tienen hoy soluciones robustas y probadas en batalla. La clave es tratar a nuestro agente de observabilidad como lo que es: una aplicación crítica de primer nivel.

Despliegue como DaemonSet y compatibilidad multi-kernel

En el universo Kubernetes, hay un patrón de despliegue que encaja como un guante para los agentes basados en eBPF: el DaemonSet. Un DaemonSet asegura que una réplica de tu pod (en este caso, tu agente de monitorización) se ejecute en cada uno de los nodos del clúster. ¿Añades un nodo nuevo? Kubernetes se encarga de desplegar el agente allí. ¿Un nodo se va? El pod se elimina con él. Es el modelo «instálalo y olvídate» para la cobertura total.

Proyectos como Cilium o Tetragon se despliegan de esta manera. Pero esto nos lleva directamente al mayor dolor de cabeza histórico de eBPF: la compatibilidad del kernel. En un clúster grande, es casi seguro que no todos los nodos ejecuten la misma versión exacta del kernel. Puede que estés en medio de una actualización del sistema operativo, o que uses distintos tipos de instancia con AMIs diferentes. Si tu programa eBPF fue compilado contra la versión 5.4.0 del kernel, es muy probable que falle al cargarse en un kernel 5.10.0 porque alguna estructura de datos interna que usa ha cambiado de tamaño o sus campos se han reorganizado.

CO-RE

La solución a esto, y la que ha permitido que eBPF despegue en producción, es CO-RE (Compile Once – Run Everywhere). Gracias al BTF (BPF Type Format), que es una especie de mapa de depuración que describe todas las estructuras de datos de un kernel específico, el cargador de eBPF (como libbpf) puede realizar «ajustes» en tiempo de carga. Tu programa eBPF se compila una sola vez, y el binario resultante incluye información sobre qué quiere leer (por ejemplo, «el PID del proceso actual»), en lugar de dónde está exactamente («en el byte 1248 de la struct task_struct»). Al cargarse en un nodo, libbpf consulta el BTF de ese kernel, averigua dónde está el PID en esa versión concreta y parchea el programa en memoria antes de adjuntarlo.

El resultado es un único agente, desplegado como un DaemonSet, que funciona sin problemas a través de las diferentes versiones de kernel de tu clúster. Se acabaron las pesadillas de mantener múltiples builds de tu agente.

Firmas, rollback y versionado por BTF

Operar código a nivel de kernel exige una confianza y una disciplina férreas. No puedes permitirte un «ups, desplegué un bug» que tira abajo un nodo. Aquí es donde las prácticas de CI/CD y la gestión de versiones se vuelven cruciales.

  • Firmado de programas: Aunque es un área aún en desarrollo en la comunidad, la idea de firmar criptográficamente los programas eBPF está ganando tracción. Esto permitiría al kernel verificar que solo los programas firmados por una autoridad de confianza (por ejemplo, tu pipeline de CI) puedan ser cargados, añadiendo una capa de seguridad fundamental.
  • Rollback y Versionado: Gracias a CO-RE y BTF, el versionado se simplifica enormemente. Ya no versionas por kernel, sino por la lógica de tu agente. La actualización de un agente desplegado como DaemonSet es una operación estándar en Kubernetes: kubectl rollout restart daemonset/mi-agente-ebpf. Si la nueva versión introduce una regresión (por ejemplo, consume demasiada CPU), el rollback es igual de simple: kubectl rollout undo daemonset/mi-agente-ebpf. Tratas tu infraestructura de observabilidad con las mismas herramientas y flujos de trabajo probados que usas para tus aplicaciones.

La capacidad de tener un único artefacto que funciona en todas partes, combinado con las primitivas de despliegue robustas de Kubernetes, convierte la gestión de agentes eBPF a escala de un problema complejo a una tarea operativa estándar.

Pinning en bpffs y rotación segura de programas

Aquí entramos en un terreno un poco más avanzado pero fundamental para las operaciones «cero downtime». ¿Qué pasa con el estado que acumula tu agente? Imagina que tienes un mapa eBPF que cuenta el número de errores de red por pod. Si actualizas el agente simplemente reiniciando el pod del DaemonSet, el proceso de usuario muere y, por defecto, el kernel limpia todos sus mapas y programas asociados. Perderías todos los contadores.

La solución es el pinning (o «anclaje») a través del sistema de ficheros virtual de BPF, bpffs, que normalmente se monta en /sys/fs/bpf. Cuando un objeto eBPF (un mapa o un programa) se «ancla», el kernel crea un archivo especial en bpffs que mantiene una referencia a ese objeto. Esto significa que el objeto persistirá en la memoria del kernel incluso si el proceso que lo creó termina.

Patrón de actualización

Esto nos permite un patrón de actualización increíblemente elegante y seguro:

  • 1
    Versión 1 del agente está corriendo: Sus mapas (con los contadores de errores, por ejemplo) están anclados en /sys/fs/bpf/mi_mapa_de_errores.
  • 2
    Inicia la actualización: Kubernetes empieza a desplegar la Versión 2 del agente. El nuevo pod se inicia.
  • 3
    La Versión 2 se inicializa: Antes de hacer nada, busca el mapa anclado en bpffs. Si lo encuentra, en lugar de crear un mapa nuevo, obtiene una referencia al ya existente y puede seguir trabajando con los datos que la Versión 1 estaba recolectando.
  • 4
    Rotación atómica: La Versión 2 carga su nueva lógica de programa eBPF y la adjunta al mismo punto de anclaje (por ejemplo, a la entrada de la pila de red). El kernel realiza este reemplazo de forma atómica.
  • 5
    La Versión 1 se apaga: Una vez que la Versión 2 está lista y saludable, Kubernetes detiene el pod de la Versión 1. Como los mapas están anclados, su estado no se pierde.

Este mecanismo es crucial para la continuidad de los datos y permite actualizaciones transparentes sin perder una sola métrica o evento de seguridad. Es el tipo de detalle operativo que distingue a una herramienta de producción de un simple prototipo.

¿Sustituye a los agentes tradicionales?

Vale, llegamos a la pregunta del millón, la que probablemente te estás haciendo ahora mismo: ¿tiro a la basura mi node_exporter, mis agentes de APM y todo lo que he montado durante años y lo apuesto todo a eBPF? La respuesta, como casi siempre en nuestro mundo, es un rotundo «depende».

No, eBPF no es un reemplazo universal para todo. Pensar así es caer en la trampa del «martillo de oro», donde creemos que una nueva herramienta fantástica sirve para clavar cualquier clavo. La realidad es mucho más interesante. eBPF no sustituye tanto a los agentes tradicionales como que los redefine y, en muchos casos, los complementa de una forma increíblemente potente. Es un cambio de paradigma. Mueve el punto de recolección de datos desde el espacio de usuario (con sus ineficiencias y puntos ciegos) a un lugar privilegiado y universal: el kernel.

Los agentes tradicionales siguen teniendo su sitio, pero el centro de gravedad de la observabilidad de bajo nivel se está desplazando, y con razón, hacia el kernel.

Coste y latencia vs. cobertura y facilidad de uso

Seamos honestos, la principal razón por la que eBPF está causando tanto revuelo es su eficiencia. Los agentes tradicionales, especialmente los que se ejecutan como sidecars o instrumentan el código de la aplicación, tienen un coste. Cada uno consume su propia porción de CPU y memoria, y el cambio de contexto entre la aplicación, el agente y el kernel añade latencia. eBPF, al operar en el kernel, reduce drásticamente este overhead. La recolección de datos es más directa, sin intermediarios.

Pero los agentes tradicionales tienen a su favor años de desarrollo, un ecosistema maduro y una facilidad de uso para ciertos casos que es difícil de superar. ¿Quieres saber cuántos usuarios han completado una compra? Esa es una métrica de negocio, y el lugar más sencillo para generarla sigue siendo el código de tu aplicación, probablemente exportada a través de un endpoint de Prometheus. Intentar deducir eso desde el kernel sería un ejercicio de ingeniería inversa complicadísimo y muy frágil.
Aquí la balanza se inclina:

eBPF gana en:

  • Rendimiento: Menor sobrecarga de CPU/memoria y latencia casi nula.
  • Cobertura universal: Ve todo lo que pasa por el kernel, independientemente del lenguaje de programación de la aplicación.
  • Seguridad: El verificador y el sandbox garantizan que los programas no pueden dañar el kernel.
  • No intrusivo: No requiere cambios en el código de la aplicación ni reinicios.

Agentes tradicionales ganan en:

  • Métricas de negocio: Tienen el contexto de la aplicación para medir lógicas específicas.
  • Facilidad de despliegue (para casos simples): Un apt install prometheus-node-exporter es un comando que todos conocemos y confiamos.
  • Ecosistema maduro: Hay miles de exportadores y dashboards listos para usar para casi cualquier tecnología imaginable.

Enfoque híbrido inteligente: qué dejar a eBPF y qué no

El verdadero poder no está en elegir uno u otro, sino en combinarlos de forma inteligente. Un enfoque híbrido te da lo mejor de ambos mundos: la profundidad y eficiencia del kernel con la inteligencia de negocio de la aplicación.

La regla de oro es esta: Usa eBPF para todo lo que el kernel «sabe» de forma natural. Usa la instrumentación tradicional para todo lo que solo la aplicación «sabe».

 

Caso de UsoHerramienta Recomendada¿Por qué?
Tráfico de Red (Golden Signals)eBPFEl kernel gestiona cada paquete. eBPF puede medir latencia, errores y volumen sin sobrecarga.
Seguridad a Nivel de SistemaeBPF¿Qué proceso está abriendo qué fichero? ¿Quién se comunica con quién? eBPF lo ve en tiempo real.
Rendimiento de Llamadas al SistemaeBPFEs su hábitat natural. Puede perfilar el rendimiento del disco, la memoria y la CPU a un nivel granular.
Métricas de Lógica de NegocioInstrumentación de AplicaciónEl kernel no sabe qué es un «carrito de la compra». Esto debe vivir en el código de la aplicación.
Trazas Distribuidas EspecíficasHíbrido (OpenTelemetry + eBPF)Las librerías de OpenTelemetry propagan el contexto de la traza, pero eBPF puede enriquecerlas con saltos a nivel de kernel, dándote una visión completa.
Métricas Internas de RuntimesExportadores Tradicionales (JMX, etc.)El estado interno de la JVM (ej: recolección de basura) se expone mejor a través de sus propias interfaces como JMX. eBPF ve los síntomas (uso de CPU), JMX ve la causa.

Este enfoque híbrido te permite tener una base de observabilidad increíblemente rica y de bajo coste con eBPF, y luego añadir las métricas de alto nivel que realmente importan para el negocio donde sea necesario.

Plan de transición sin perder alertas ni SLOs

Migrar a un nuevo sistema de monitorización da miedo. Lo último que quieres es crear un punto ciego justo cuando más lo necesitas. Un «rip and replace» (arrancar y reemplazar) es una receta para el desastre. La clave es una transición gradual, basada en la confianza y la validación.

Así es como se debe hacer, paso a paso:

  • 1
    Ejecutar en Paralelo: Despliega tu nuevo agente basado en eBPF junto a tus agentes actuales. Sí, durante un tiempo tendrás dos sistemas recolectando datos similares. Asume este pequeño sobrecoste temporal. Es tu red de seguridad.
  • 2
    Calibrar y Validar: Crea nuevos dashboards en tu sistema de visualización (ej. Grafana) que muestren las métricas de eBPF junto a las antiguas. ¿Coinciden? ¿La latencia de red que mide eBPF es comparable a la que medía tu viejo agente? Pasa tiempo aquí. Construye confianza en los nuevos datos.
  • 3
    Migrar Dashboards, NO Alertas: Empieza por acostumbrar a tu equipo a usar los nuevos dashboards. Conviértelos en la fuente de verdad para la visualización del día a día, mientras las alertas críticas siguen dependiendo del sistema antiguo y probado.
  • 4
    Alertas en Sombra (Shadowing): Ahora, recrea tus alertas más importantes (las que protegen tus SLOs) usando las nuevas métricas. Configúralas para que se disparen, pero sin enviar notificaciones. Durante unas semanas, compara los disparos. Cuando la alerta antigua se activa, ¿se habría activado también la nueva? Si no es así, ¿por qué? Afina los umbrales hasta que estés 100% seguro de que el comportamiento es el esperado.
  • 5
    El Gran Cambio (Controlado): Una vez que las alertas en sombra han demostrado ser fiables, llega el momento. Durante una ventana de bajo riesgo (¡nunca un viernes por la tarde!), activa las notificaciones de las nuevas alertas y silencia las antiguas. Monitoriza de cerca.
  • 6
    Desmantelamiento: Solo después de que el nuevo sistema haya funcionado sin problemas durante un ciclo de negocio completo (una semana, un mes…), puedes empezar a apagar y desinstalar los agentes antiguos.

Este proceso metódico transforma una migración de alto riesgo en una serie de pasos controlados y seguros, asegurando que nunca pierdas visibilidad sobre la salud de tus sistemas.

Preguntas frecuentes

Vale, hemos cubierto mucha tela. Es normal que tengas preguntas rondando la cabeza. Hemos recopilado aquí algunas de las más comunes que suelen hacer cuando hablamos de meter eBPF en producción. Vamos al grano.

¿Qué kernel necesito para CO-RE?

Esta es la pregunta del millón. Y la respuesta corta es: para tener una buena experiencia con CO-RE (Compile Once – Run Everywhere), necesitas un kernel que tenga soporte para BTF (BPF Type Format).

La respuesta un poco más larga y útil: idealmente, cualquier kernel Linux 5.8 o superior te va a dar una base muy sólida. Aunque el soporte BTF se introdujo antes (alrededor del 4.18), fue en la rama 5.x donde maduró de verdad y la mayoría de las distribuciones empresariales empezaron a habilitarlo por defecto.

Distribuciones modernas como Ubuntu 20.10+, RHEL 8.2+, y Debian 11+ ya suelen venir con todo lo necesario. Lo más fácil para comprobarlo en tu sistema es ejecutar:

Copy to Clipboard

Si ese archivo existe, estás de enhorabuena. Tu kernel tiene la «chuleta» (BTF) que libbpf necesita para hacer la magia de CO-RE y ajustar tu programa eBPF al dedillo para ese kernel específico, sin que tengas que recompilar nada. Si no existe, te espera un mundo de dolor con cabeceras de kernel y compilaciones específicas. Mi consejo: si puedes, actualiza el kernel. Ahorrarás semanas de trabajo y dolores de cabeza.

¿Puedo ejecutar eBPF en contenedores sin privilegios?

Aquí hay que ser muy preciso. La respuesta es no, no realmente, pero puedes hacerlo de forma mucho más segura que antes.

«Sin privilegios» de verdad, como un proceso de usuario cualquiera, es prácticamente imposible para la mayoría de casos de observabilidad, porque por definición necesitas permiso para ver lo que hace el kernel. La comunidad Linux ha debatido mucho sobre esto y, por ahora, el consenso es que el riesgo de seguridad es demasiado alto.

Ahora, lo que sí puedes (y debes) hacer es abandonar el viejo y peligroso hábito de dar a tus agentes privilegios completos de root o la capacidad CAP_SYS_ADMIN. El enfoque moderno es el principio de mínimo privilegio. Desde el kernel 5.8, tenemos capacidades más granulares. Para un agente de eBPF de observabilidad, normalmente solo necesitas:

  • CAP_BPF: Permite cargar y gestionar programas y mapas eBPF.
  • CAP_PERFMON: Permite acceder a los subsistemas de tracing y kprobes.

Concediendo solo estas dos, limitas drásticamente la superficie de ataque. El agente puede hacer su trabajo de observabilidad, pero no puede, por ejemplo, reconfigurar la red o montar sistemas de ficheros. Esto es un mundo de diferencia en términos de seguridad.

Existen técnicas más avanzadas, como que un proceso privilegiado (un DaemonSet en Kubernetes) cargue los programas y luego comparta los mapas con contenedores sin privilegios a través del bpffs (el filesystem de BPF), pero esto añade complejidad. Para empezar, usar CAP_BPF y CAP_PERFMON es el equilibrio perfecto entre seguridad y funcionalidad.

¿Cómo evito impacto en horas pico?

Una preocupación totalmente legítima. Un programa eBPF mal diseñado puede consumir CPU en un momento crítico. La clave para evitarlo no es una sola cosa, sino una combinación de estrategias de diseño defensivo.

  • 1
    Filtra lo antes posible: La regla de oro es: no proceses eventos que no te interesan. Si solo te importa el tráfico de un proceso concreto, filtra por su PID (bpf_get_current_pid_tgid()) al principio de tu programa y haz un return 0; inmediato si no coincide. Cada ciclo de CPU que ahorras en el kernel cuenta.
  • 2
    Usa el muestreo (Sampling): ¿Realmente necesitas analizar CADA paquete de red o CADA llamada al sistema? A menudo, una muestra representativa es suficiente para calcular métricas como latencias p99. Puedes implementar un muestreo simple en tu programa eBPF (ej. procesa solo 1 de cada 100 eventos) para reducir drásticamente la carga.
  • 3
    Agrega en el kernel: Nunca envíes un torrente de eventos al espacio de usuario. Es la forma más rápida de saturar la CPU. En su lugar, usa mapas eBPF para agregar datos directamente en el kernel. Los mapas de tipo BPF_MAP_TYPE_HISTOGRAM, por ejemplo, son increíblemente eficientes para construir distribuciones de latencia con un coste casi nulo.
  • 4
    Descarga el trabajo pesado: El programa eBPF debe ser corto, rápido y hacer lo mínimo indispensable. Su trabajo es recolectar. El cálculo de percentiles, la correlación de datos y la exportación de métricas es trabajo para el agente en espacio de usuario, que se ejecuta con menor prioridad y puede ser controlado como cualquier otro proceso.
  • 5
    Cuidado con los ganchos de alta frecuencia: Engancharse a tcp_v4_rcv en un servidor con mucho tráfico es pedir problemas si no filtras agresivamente. A veces es más inteligente engancharse a un punto un poco más arriba en la pila que se llame con menos frecuencia pero que te dé la misma información.

Con estas técnicas, el overhead de un agente eBPF bien escrito debería ser mínimo y, lo más importante, predecible, incluso en los picos de carga.

¿Funciona en entornos mixtos con kernels antiguos?

Sí, pero con matices importantes. eBPF como tecnología existe desde el kernel 3.15, así que, técnicamente, «funciona». El problema es que las características que lo hacen realmente potente para la observabilidad moderna —CO-RE, BTF, tipos de mapas avanzados, helpers útiles— son mucho más recientes.

Si intentas desplegar un agente moderno basado en CO-RE en un host con un kernel 4.14, por ejemplo, simplemente no va a funcionar porque le faltará el soporte BTF.

En un entorno mixto, tienes dos caminos realistas:

  • 1
    El camino del dolor (no recomendado): Mantener diferentes versiones de tu agente, compiladas específicamente para cada versión de kernel que tengas. Esto es lo que se hacía antes de CO-RE. Es un infierno de mantenimiento, propenso a errores y te hará cuestionar tus decisiones vitales. Cada vez que actualices el agente, tendrás que compilar y probar para cada kernel. Huye de esto si puedes.
  • 2
    El camino pragmático: Adopta un enfoque híbrido también a nivel de parque. Despliega tu agente eBPF moderno solo en los nodos con kernels compatibles (ej. 5.8+). Para los nodos más antiguos, sigue usando tus agentes de monitorización tradicionales (Node Exporter, agentes de APM, etc.).

Esto te permite aprovechar lo mejor de eBPF donde puedes, sin la pesadilla de dar soporte a kernels antiguos que carecen de las funcionalidades necesarias. Con el tiempo, a medida que los servidores antiguos se vayan retirando, tu cobertura de eBPF se expandirá de forma natural. Intentar forzar la última tecnología en sistemas operativos viejos rara vez acaba bien.

Fuentes:
http://blog.segu-info.com.ar/2026/02/ebpf-observabilidad-sin-sobrecarga-en.html

0 comentarios :

Publicar un comentario

Los comentarios pueden ser revisados en cualquier momento por los moderadores.

Serán publicados aquellos que cumplan las siguientes condiciones:
- Comentario acorde al contenido del post.
- Prohibido mensajes de tipo SPAM.
- Evite incluir links innecesarios en su comentario.
- Contenidos ofensivos, amenazas e insultos no serán permitidos.

Debe saber que los comentarios de los lectores no reflejan necesariamente la opinión del STAFF.