eBPF: Rastreando eventos de Kernel para IRIS Workloads
.png)
Eu estive na Cloud Native Security Con em Seattle com total intenção de entrar no dia de OTEL, então lendo o assunto de segurança aplicada a fluxos de trabalho Cloud Native nos dias seguintes até CTF como um exercício profissional. Isso foi felizmente finalizado por um novo entendimento de eBPF, que tem minhas telas, carreira, fluxos de trabalho e atitude com muita necessidade de uma melhoria com novas abordagens para resolver problemas de fluxo de trabalho.
Então, consegui chegar à festa do eBPF e desde então tenho participado de clínica após clínica sobre o assunto. Aqui, gostaria de "desempacotar" o eBPF como uma solução técnica, mapeada diretamente para o que fazemos na prática (mesmo que esteja um pouco fora), e passar pelo eBPF através da minha experimentação em suporte a cargas de trabalho InterSystems IRIS, particularmente no Kubernetes, mas não necessariamente vazio em cargas de trabalho autônomas.
.png)
Passos eBee com eBPF e fluxos de trabalho InterSystems IRIS
eBPF
eBPF (extended Berkeley Packet Filter) é um recurso poderoso do kernel Linux que implementa uma VM dentro do espaço do kernel e torna possível executar aplicativos sandboxes de forma segura com guias. Esses aplicativos podem "mapear" dados para o espaço do usuário para observabilidade, rastreamento, segurança e redes. Eu penso nisso como um "cheirador" do SO, onde tradicionalmente estava associado ao BPF e redes, e a versão estendida "cheira" pontos de rastreamento, processos, agendamento, execução e acesso a dispositivos de bloco. Se você não comprou minha analogia de eBPF, aqui está uma dos prós:
"O que JavaScript é para o browser, eBPF é para o Linux Kernel"
JavaScript permite que você anexe callbacks a eventos no DOM para trazer recursos dinâmicos à sua página web. De maneira semelhante, o eBPF permite conectar-se a eventos do kernel e estender sua lógica quando esses eventos são acionados!
Immediatamente Aplicável
SE; a seguinte métrica Prometheus parece impossível para você, empregue eBPF para observar processos que devem estar lá e monitorar em banda através do kernel
# HELP iris_instance_status The thing thats down telling us its down.
# TYPE iris_instance_status gauge
iris_instance_status 0SE; você está cansado de implorar para o seguinte para um sidecar obter a observabilidade necessária, Adeus sidecars
iris-sidecar:
resources:
requests:
memory: "2Mi"
cpu: "125m"onde
Uma das coisas mais satisfatórias sobre como o eBPF é aplicado é onde ele roda... em uma VM, dentro do kernel. E graças ao Linux Namespacing, você pode adivinhar o quão poderoso isso é em um ambiente nativo da nuvem, quanto mais em um kernel baseado em algum tipo de virtualização ou uma grande máquina ghetto blaster de ferro com hardware admirável.
.png)
"Olá Mundi" Obrigatório
Para aqueles de vocês que gostam de experimentar coisas por si mesmos e desde o "começo", por assim dizer, eu saúdo vocês com um obrigatório "Olá Mundo", torcido para ser um pouco "iristico". No entanto, é amplamente entendido que programar em eBPF não se tornará uma habilidade exercida com frequência, mas concentrada em indivíduos que desenvolvem o kernel Linux ou constroem ferramentas de monitoramento de próxima geração.
Eu uso Pop OS/Ubuntu, e aqui está meu código de consulta para entrar rapidamente no mundo do eBPF em 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 bindingpushd ../src/python/
make
sudo make install
popdcd bcc
make installPrimeiro assegure que o kernel objetivo tem as coisas necessárias...
cat /boot/config-$(uname -r) | grep 'CONFIG_BPF'
CONFIG_BPF=ySe `CONFIG_BPF=y` está na sua janela em algum lugar, podemos seguir.
O que queremos alcançar aqui com esse simples olá mundo é obter visibilidade de quando o IRIS está fazendo chamadas de sistema LInux, sem o uso de nada além de ferramentas eBPF e o próprio kernel.
Aqui está uma boa maneira de seguir com a exploração:
1️⃣ Ache uma chamada de sistema Linux de interesse
sudo ls /sys/kernel/debug/tracing/events/syscallsPara este exemplo, vamos
Para este exemplo, vamos interceptar quando alguém (modificado para interceptar IRIS), cria um diretório através da chamada de sistema sys_enter_mkdir.
2️⃣ Insira isso no Olá Mundo que segue
Seu programa BPF para carregar e rodar está na variável BPF_SOURCE_CODE, modifique para incluir a syscall que você deseja interceptar.
# Example eBPF program to a Linux kernel tracepoint# Modified to trap irisdb# requires bpfcc-tools# To run: sudo python3 irismadeadir.pyfrom 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")
whileTrue:
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:
continueexcept KeyboardInterrupt:
break
3️⃣ Carregue no Kernel, Rode
.png)
Crie um diretório em IRIS
.png)
Inspecione o rastreio!
.png)
Binários Impulsionados por eBPF
Não leva muito tempo quando se passa pelo repositório bcc e percebe que há muitos exemplos, ferramentas e binários por aí que aproveitam o eBPF para fazer traçamentos divertidos, e "grep" nesse caso será suficiente para derivar algum valor.
Vamos fazer exatamente isso em um start e stop do IRIS com alguns exemplos fornecidos.
execsnoop Rastreie novos processes via syscalls exec().
Este aqui conta uma história dos argumentos ao irisdb no start/stop
sudo python3 execsnoopy.py | grep iris
statsnoop Rastreie stat() syscalls... retorna atributos de arquivo sobre um inode, acesso arquivo/diretório.
Este aqui é informativo para o acesso ao nível de diretório e arquivo durante uma inicialização/parada... um pouco loquaz, mas informativo sobre o que o iris está fazendo durante a inicialização, incluindo acesso cpf, diários, atividade wij e o uso de ferramentas de sistema para realizar o trabalho.
sudo python3 statsnoop.py | grep iris
3016831 irisdb 0 0 /data/IRIS/mgr/
3016831 irisdb 0 0 /data/IRIS/mgr/
3016825 irisstop 0 0 /data/IRIS/mgr
3016825 irisstop 0 0 /usr/irissys/bin/irisuxsusr
3016825 irisstop 0 0 ./irisdb
3016825 irisstop 0 0 ../bin
3016832 sh -1 2 /usr/irissys/bin/glibc-hwcaps/x86-64-v3/
3016832 sh -1 2 /usr/irissys/bin/glibc-hwcaps/x86-64-v2/
3016832 sh 0 0 /usr/irissys/bin/
3016832 sh 0 0 /home/irisowner
3016833 irisdb -1 2 /usr/irissys/bin/glibc-hwcaps/x86-64-v3/
3016833 irisdb -1 2 /usr/irissys/bin/glibc-hwcaps/x86-64-v2/
3016833 irisdb 0 0 /usr/irissys/bin/
3016833 irisdb 0 0 /data/IRIS/mgr/
3016833 irisdb 0 0 /data/IRIS/mgr/
3016833 irisdb 0 0 /data/IRIS/mgr/
3016834 irisstop 0 0 ./irisdb
3016834 irisstop 0 0 ../bin
3016834 irisdb -1 2 /usr/irissys/bin/glibc-hwcaps/x86-64-v3/
3016834 irisdb -1 2 /usr/irissys/bin/glibc-hwcaps/x86-64-v2/
3016834 irisdb 0 0 /usr/irissys/bin/
3016834 irisdb 0 0 /data/IRIS/mgr/
3016834 irisdb 0 0 /data/IRIS/mgr/
3016835 irisstop 0 0 ./irisrecov
3016835 irisstop 0 0 ../bin
3016835 irisrecov -1 2 /usr/irissys/bin/glibc-hwcaps/x86-64-v3/
3016835 irisrecov -1 2 /usr/irissys/bin/glibc-hwcaps/x86-64-v2/
3016835 irisrecov 0 0 /usr/irissys/bin/
3016835 irisrecov 0 0 /home/irisowner
3016835 irisrecov 0 0 .
3016835 irisrecov 0 0 iris.cpf
3016841 irisrecov 0 0 /usr/bin/cut
3016841 irisrecov 0 0 /usr/bin/tr
3016841 irisrecov 0 0 /usr/bin/sed
3017761 requirements_ch 0 0 /home/irisowner
3017761 requirements_ch -1 2 /usr/irissys/bin/requirements.isc
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb 0 0 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf_5275
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf_5275
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf_5275
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /usr/lib64/libcrypto.so.1.1
3017691 irisdb -1 2 /usr/lib64/libcrypto.so.3
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/iris.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf_20240908
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/_LastGood_.cpf_5275
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/_LastGood_.cpf_5275
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb -1 2 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /etc/localtime
3017691 irisdb 0 0 /data/IRIS/_LastGood_.cpf
3017691 irisdb 0 0 /data/IRIS/mgr/irisaudit/
3017691 irisdb 0 0 /data/IRIS/mgr/irisaudit/
3017691 irisdb 0 0 /data/IRIS/mgr/irisaudit/
3017691 irisdb 0 0 /data/IRIS/mgr/irisaudit/
3017691 irisdb 0 0 /data/IRIS/mgr/irisaudit/
3017691 irisdb 0 0 /data/IRIS/mgr/irisaudit/
3017756 irisdb -1 2 /data/IRIS/mgr/journal/20240908.002
3017756 irisdb -1 2 /data/IRIS/mgr/journal/20240908.002
3017756 irisdb 0 0 /data/IRIS/mgr/journal/20240908.002z
3017756 irisdb -1 2 /data/IRIS/mgr/journal/20240908.002
3017756 irisdb 0 0 /data/IRIS/mgr/journal/20240908.002z
3017756 irisdb -1 2 /data/IRIS/mgr/journal/20240908.001
Flamegraphs

Uma das coisas mais legais que encontrei com a ferramenta eBPF foi a implementação de flamegraphs de Brendan Gregg por cima da saída bpf para entender desempenho e pilhas de chamadas.
Dado o seguinte registro de desempenho durante uma inicialização/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) ]Gere o seguinte flamegrapth com o descrito abaixo:
sudo perf script > out.perf
./stackcollapse-perf.pl out.perf > /tmp/gar.thing
./flamegraph.pl /tmp/gar.thing > flamegraph.svg.png)
Eu tentei o upload do svg, mas não funcionou com este editor e, por algum motivo, não consegui anexá-lo. Entenda, porém, que ele é interativo e clicável para detalhar as pilhas de chamadas, além de apenas parecer legal.
- "A função na parte inferior é a função on-CPU. Quanto mais alto no eixo y, mais profundamente aninhada está a função.
- A largura de cada função no gráfico representa a quantidade de tempo que a função levou para executar como uma porcentagem do tempo total de sua função pai.
- Encontrar funções que são altas no eixo y (profundamente aninhadas) e largas no eixo x (intensivas em tempo) é uma ótima maneira de reduzir problemas de desempenho e otimização.
"altas e largas" <--- 👀
red == nível de usuário
orange == kernel
yellow == c++
green == JIT, java etc.
Eu realmente gostei dessa explicação da interpretação de flamegraphs apresentada aqui (crédito para o acima), onde derivei uma compreensão básica sobre como ler flamegraphs. Especialmente poderoso para aqueles que estão executando Python em IRIS em produções com código de usuário e procurando otimização.
Em frente, espero que isso desperte seu interesse, agora vamos passar para o mundo dos aplicativos eBPF, onde os profissionais reuniram soluções fenomenais para colocar o eBPF a trabalhar em frotas de sistemas de forma segura e leve.