Artículo
· 20 sep, 2024 Lectura de 15 min

eBPF: Seguimiento de eventos del Kernel para IRIS Workloads (Cargas de trabajo)

Asistí a la Cloud Native Security Con en Seattle con la plena intención de aprovechar el día de OTEL, para luego profundizar en el tema de la seguridad aplicada a las cargas de trabajo nativas en la nube en los días siguientes, culminando con el CTF como ejercicio profesional. Esto fue felizmente interrumpido por un nuevo entendimiento de eBPF, lo cual supuso una mejora muy necesaria para mis pantallas, carrera, cargas de trabajo y actitud, gracias a nuevos enfoques para resolver problemas relacionados con las cargas de trabajo.

Así que llegué a la "fiesta" de eBPF y desde entonces he estado asistiendo a clínica tras clínica sobre el tema. Aquí me gustaría "desempaquetar" eBPF como una solución técnica, mapeada directamente a lo que hacemos en la práctica (aunque no siempre coincida del todo), y recorrer eBPF a través de mis experimentos en el soporte de las cargas de trabajo de InterSystems IRIS, particularmente en Kubernetes, pero sin excluir las cargas de trabajo independientes.

Pasos de eBee con eBPF y Cargas de Trabajo de InterSystems IRIS


 

eBPF

eBPF (Extended Berkeley Packet Filter) es una increíble funcionalidad del kernel de Linux que implementa una máquina virtual dentro del espacio del kernel, lo que permite ejecutar aplicaciones en un entorno aislado de forma segura, con ciertas guías o restricciones. Estas aplicaciones pueden "mapear" datos al espacio de usuario para la observabilidad, rastreo, seguridad y redes. Lo veo como un "sniffer" del sistema operativo, donde tradicionalmente se asociaba con BPF y redes, y la versión extendida "olfatea" puntos de traza, procesos, planificación, ejecución y acceso a dispositivos de bloque. Si no te convence mi analogía de eBPF, aquí tienes una de los expertos:

"Lo que JavaScript es para el navegador, eBPF lo es para el kernel de Linux.

JavaScript os permite adjuntar funciones de callback a eventos en el DOM para añadir características dinámicas a vuestra página web. De manera similar, eBPF permite engancharse a eventos del kernel y extender su lógica cuando dichos eventos son activados. ¡Es como añadir dinamismo al funcionamiento del kernel!

Inmediatamente aplicable

SI; la siguiente métrica de Prometheus os parece imposible, emplead eBPF para vigilar los procesos que se supone deben estar presentes y monitorizad en banda a través del kernel.

# HELP iris_instance_status The thing thats down telling us its down.
# TYPE iris_instance_status gauge
iris_instance_status 0
    

SI; estáis cansados de rogar por un sidecar para obtener la observabilidad que necesitáis. Adiós, sidecars.

  iris-sidecar:    
    resources:
      requests:
        memory: "2Mi"
        cpu: "125m"

Donde

Una de las cosas más satisfactorias de cómo se aplica eBPF es dónde se ejecuta... en una máquina virtual dentro del kernel. Y gracias al Namespacing de Linux, os podéis imaginar lo potente que es en un entorno nativo de la nube, sin mencionar un kernel que corre en algún tipo de entorno virtualizado o en una máquina potente con hardware de alta gama.

 

Obligatorio "Hola Mundo".

Para aquellos de vosotros que os gusta probar las cosas por vosotros mismos y desde el "principio", os saludo con un obligatorio hello world o "Hola Mundo", ligeramente retorcido para ser un poco más "irisy". Sin embargo, es bien sabido que programar en eBPF no será una habilidad que se practique con frecuencia, sino que estará concentrada en quienes desarrollan el kernel de Linux o construyen herramientas de monitorización de próxima generación.

Yo uso Pop OS/Ubuntu, y aquí os dejo mi truco para adentrarse rápidamente en el mundo de eBPF en la versión 23.04:

sudo apt install -y zip bison build-essential cmake flex git libedit-dev \
  libllvm15 llvm-15-dev libclang-15-dev python3 zlib1g-dev libelf-dev libfl-dev python3-setuptools \
  liblzma-dev libdebuginfod-dev arping netperf iperf libpolly-15-dev
git clone https://github.com/iovisor/bcc.git
mkdir bcc/build; cd bcc/build
cmake ..
make
sudo make install
cmake -DPYTHON_CMD=python3 .. # build python3 binding
pushd ../src/python/
make
sudo make install
popd
cd bcc
make install

Primero, aseguraos de que el kernel de destino tenga todo lo necesario...

cat /boot/config-$(uname -r) | grep 'CONFIG_BPF'
CONFIG_BPF=y

Si `CONFIG_BPF=y` está habilitado en vuestro sistema, estamos listos para continuar.

Lo que queremos lograr con este simple hello world o "Hola Mundo" es obtener visibilidad sobre cuándo IRIS realiza llamadas al sistema de Linux, sin utilizar nada más que las herramientas de eBPF y el propio kernel.

Aquí hay una buena manera de abordar la exploración:

1️⃣ Encontrad una llamada al sistema de Linux que os interese.

sudo ls /sys/kernel/debug/tracing/events/syscalls

Para este ejemplo, vamos a capturar cuando alguien (modificado para capturar IRIS) cree un directorio a través de la llamada al sistema `sys_enter_mkdir`.



2️⃣ Incorporadlo en el siguiente Hello World o "Hola Mundo":

El programa BPF que debéis cargar y ejecutar está en la variable `BPF_SOURCE_CODE`. Modificadlo para incluir la llamada al sistema que queréis capturar.

# Example eBPF program to a Linux kernel tracepoint
# Modified to trap irisdb
# requires bpfcc-tools
# To run: sudo python3 irismadeadir.py
from bcc import BPF
from bcc.utils import printb

BPF_SOURCE_CODE = r"""
TRACEPOINT_PROBE(syscalls, sys_enter_mkdir) {
bpf_trace_printk("Directory was created by IRIS: %s\n", args->pathname);
return 0;
}
"""
bpf = BPF(text = BPF_SOURCE_CODE)

print("Go create a dir with IRIS...")
print("CTRL-C to exit")

while True:
    try:
        (task, pid, cpu, flags, ts, msg) = bpf.trace_fields()
        #print(task.decode("utf-8"))
        if "iris" in task.decode("utf-8"):
            printb(b"%s-%-6d %s" % (task, pid, msg))
    except ValueError:
        continue
    except KeyboardInterrupt:
        break

 

3️⃣ Cargadlo en el Kernel, activadlo

Cread un directorio en IRIS.

¡Inspeccionad el rastro!

Binarios potenciados por eBPF

No tarda mucho al explorar el repositorio de bcc y darse cuenta de que hay muchos ejemplos, herramientas y binarios que aprovechan eBPF para realizar trazas interesantes. En este caso, utilizar "grep" será suficiente para extraer algo de valor.

 

Hagamos exactamente eso en el inicio y parada de IRIS con algunos ejemplos proporcionados

`execsnoop` traza nuevos procesos a través de las llamadas al sistema `exec()`.

Este aquí cuenta la historia de los argumentos que se pasan a `irisdb` al iniciar y detener.

 
sudo python3 execsnoopy.py | grep iris

`statsnoop` traza llamadas al sistema `stat()`, devolviendo atributos de archivos sobre un inodo y el acceso a archivos/directorios.

Este aquí es informativo para el acceso a nivel de directorio y archivo durante el inicio/parada. Es un poco ruidoso, pero ofrece información sobre lo que IRIS está haciendo durante el arranque, incluyendo el acceso a CPF, diarios, actividad de WIJ y el uso de herramientas del sistema para completar el trabajo.

 
sudo python3 statsnoop.py | grep iris

Flamegraphs

Una de las cosas más interesantes que encontré con las herramientas de eBPF fue la implementación de flamegraphs de Brendan Gregg sobre la salida de bpf para entender el rendimiento y los rastros de pila.

Dado el siguiente registro de perf durante el inicio/parada de IRIS:

sudo perf record -F 99 -a -g -- sleep 60
[ perf record: Woken up 7 times to write data ]
[ perf record: Captured and wrote 3.701 MB perf.data (15013 samples) ]

Generad el siguiente flamegraph con lo siguiente:

sudo perf script > out.perf
./stackcollapse-perf.pl out.perf > /tmp/gar.thing
./flamegraph.pl /tmp/gar.thing > flamegraph.svg

Intenté subir el SVG, pero no funcionó con este editor y, por alguna razón, no pude adjuntarlo. Entended, sin embargo, que es interactivo y se puede hacer clic para profundizar en los rastros de pila, además de simplemente verse bien.

  1. La función en la parte inferior es la función en el CPU. Cuanto más arriba en el eje y, más anidada está la función.
  2. El ancho de cada función en el gráfico representa el tiempo que esa función tardó en ejecutarse como un porcentaje del tiempo total de su función principal.
  3. Encontrar funciones que estén tanto altas en el eje y (profundamente anidadas) como anchas en el eje x (intensivas en tiempo) es una excelente manera de identificar problemas de rendimiento y optimización.

"alto y ancho" <--- 👀

red == user-level

orange == kernel

yellow == c++

green == JIT, java etc.

Me gustó mucho esta explicación sobre la interpretación de flamegraphs que se presenta aquí (crédito a la explicación anterior), de donde obtuve una comprensión básica sobre cómo leer *flamegraphs*. Es especialmente potente para aquellos que ejecutan Python en IRIS en producciones con código en el espacio de usuario y buscan optimización.

Avanzando y mejorando, espero que esto haya despertado vuestro interés. Ahora, pasemos al mundo de las aplicaciones eBPF, donde los expertos han desarrollado soluciones fenomenales para poner eBPF a trabajar en flotas de sistemas de manera segura y ligera.

Comentarios (0)1
Inicie sesión o regístrese para continuar