Files
server-mv/ebpf-sockmap/README.md
T

11 KiB

eBPF Sockmap — Acelerador de comunicação entre containers Docker

📖 O que é isso?

Este projeto usa eBPF sockmap para acelerar a comunicação de rede entre containers Docker que rodam no mesmo servidor (host).

A stack de exemplo inclui:

Container Função Porta
Nginx Proxy reverso 8080 → 80
Node.js Servidor da aplicação 3000
MySQL Banco de dados relacional 3306
PostgreSQL Banco de dados relacional 5432

🧠 Como funciona? (Explicação para iniciantes)

O problema: comunicação lenta entre containers

Quando dois containers Docker no mesmo host se comunicam (ex: Node.js → MySQL), os dados passam por toda a pilha de rede do Linux:

Container A (Node.js)
    ↓ syscall send()
    ↓ TCP (segmentação, controle de fluxo)
    ↓ IP (roteamento)
    ↓ Netfilter (iptables/nftables)
    ↓ Bridge (docker0)
    ↓ veth pair (interface virtual)
    ↓ IP (de novo)
    ↓ TCP (de novo)
    ↓ syscall recv()
Container B (MySQL)

Isso é desnecessário quando os containers estão no mesmo host — os dados poderiam ir direto de um para o outro!

A solução: eBPF Sockmap

Com o sockmap, os dados são redirecionados diretamente entre sockets, pulando TODA essa pilha:

Container A (Node.js)
    ↓ syscall send()
    ↓ eBPF intercepta aqui! ⚡
    ↓ Dados vão direto para o socket do outro container
    ↓ syscall recv()
Container B (MySQL)

Os dois programas BPF

O projeto tem dois programas eBPF que trabalham juntos:

1. sock_ops — O Registrador

  • É ativado toda vez que uma conexão TCP é estabelecida
  • Quando o Node.js conecta no MySQL, ele registra esses sockets num mapa (tabela hash)
  • O mapa guarda: IP origem, IP destino, porta origem, porta destino → socket

2. sk_msg — O Redirecionador

  • É ativado toda vez que um socket registrado envia dados
  • Ele procura no mapa o socket do outro lado da conexão
  • Se encontrar, redireciona os dados diretamente para lá
  • Se não encontrar, deixa passar normalmente pela rede
┌─────────────────────────────────────────────────────────────┐
│                    KERNEL LINUX                              │
│                                                              │
│  ┌──────────┐     sock_ops          ┌──────────────┐       │
│  │ Conexão  │ ──registra socket──→  │   SOCKHASH   │       │
│  │ TCP nova │                       │  (mapa BPF)  │       │
│  └──────────┘                       │              │       │
│                                      │  key → sock  │       │
│  ┌──────────┐     sk_msg           │  key → sock  │       │
│  │ Socket   │ ──procura par──────→ │  key → sock  │       │
│  │ envia    │                       └──────────────┘       │
│  │ dados    │ ←─redireciona direto──────────┘              │
│  └──────────┘                                               │
│                                                              │
│  ✂️ Pula: TCP/IP → Netfilter → Bridge → veth → TCP/IP      │
└─────────────────────────────────────────────────────────────┘

📋 Requisitos

Requisito Mínimo Verificar
Linux Kernel ≥ 5.4 uname -r
BTF Habilitado no kernel ls /sys/kernel/btf/vmlinux
Cgroup v2 Ativo mount | grep cgroup2
Clang/LLVM ≥ 11 clang --version
bpftool Instalado bpftool version
libbpf-dev Headers instalados dpkg -l libbpf-dev
Docker ≥ 20.10 docker --version
Docker Compose v2 (plugin) docker compose version

⚠️ IMPORTANTE: eBPF roda apenas no Linux. Se você está no Windows, use WSL2 ou uma VM Linux.


🚀 Guia passo a passo

Passo 0: Entrar no Linux (se usa Windows)

Se você está no Windows, precisa usar WSL2:

# No PowerShell do Windows, abra o WSL:
wsl

# Navegue até o projeto:
cd /mnt/c/Users/rodri/build/ebpf-sockmap

Passo 1: Instalar dependências

# Instala TUDO que é necessário automaticamente
sudo bash scripts/setup.sh

Ou instale manualmente (Ubuntu/Debian):

sudo apt update
sudo apt install -y \
    clang llvm libbpf-dev \
    linux-tools-common linux-tools-$(uname -r) \
    linux-headers-$(uname -r) \
    build-essential pkg-config curl \
    docker.io docker-compose-plugin

Passo 2: Compilar o programa eBPF

# Compila tudo (gera vmlinux.h + compila o .c para .o)
make

O que acontece:

  1. bpftool gera o arquivo vmlinux.h a partir do kernel atual (contém todas as definições de estruturas do kernel)
  2. clang compila o sockmap.bpf.c para bytecode BPF → build/sockmap.bpf.o

Se der tudo certo, você verá:

✅ Compilação concluída! Objeto BPF em: build/sockmap.bpf.o

Passo 3: Subir os containers Docker

# Sobe nginx + node.js + mysql + postgresql
make docker-up

Aguarde até todos estarem prontos (pode levar ~30s na primeira vez):

# Verifique se está funcionando:
curl http://localhost:8080/
curl http://localhost:8080/mysql/users
curl http://localhost:8080/postgres/products

Passo 4: Benchmark SEM sockmap (linha de base)

# Anota os tempos sem o sockmap ativo
bash scripts/benchmark.sh

Guarde os resultados para comparar depois!

Passo 5: Ativar o eBPF Sockmap

# Carrega e ativa o sockmap
make load

Saída esperada:

[OK] Programa sock_ops carregado
[OK] Programa sk_msg carregado
[OK] sock_ops anexado ao cgroup
[OK] sk_msg anexado ao sockhash
[OK] eBPF Sockmap ATIVADO com sucesso!

Passo 6: Benchmark COM sockmap (comparar!)

# Roda o mesmo benchmark, agora com sockmap ativo
bash scripts/benchmark.sh

Você deve ver melhoria na latência, especialmente nas queries ao banco de dados.

Passo 7: Ver status e estatísticas

make status

Passo 8: Desativar quando quiser

# Remove o sockmap (volta ao comportamento normal)
make unload

# Para os containers
make docker-down

📁 Estrutura do projeto

ebpf-sockmap/
├── bpf/
│   ├── sockmap.bpf.c        ← Programa eBPF (sock_ops + sk_msg)
│   └── vmlinux.h            ← (gerado) Definições do kernel
├── build/
│   └── sockmap.bpf.o        ← (gerado) Bytecode BPF compilado
├── docker/
│   ├── docker-compose.yml   ← Definição dos 4 containers
│   ├── nginx/
│   │   └── nginx.conf       ← Config do proxy reverso
│   ├── app/
│   │   ├── Dockerfile       ← Build do container Node.js
│   │   ├── server.js        ← Servidor com endpoints de teste
│   │   └── package.json     ← Dependências Node.js
│   └── db/
│       ├── mysql-init.sql   ← Schema inicial MySQL
│       └── postgres-init.sql← Schema inicial PostgreSQL
├── scripts/
│   ├── loader.sh            ← Carrega/descarrega o eBPF sockmap
│   ├── setup.sh             ← Instala todas as dependências
│   └── benchmark.sh         ← Testa latência
├── Makefile                  ← Comandos de build e operação
└── README.md                ← Este arquivo

🔍 Comandos úteis

make help         # Lista todos os comandos disponíveis
make              # Compila tudo
make docker-up    # Sobe os containers
make docker-down  # Para os containers
make docker-logs  # Vê logs em tempo real
make load         # Ativa o sockmap
make unload       # Desativa o sockmap
make status       # Mostra estatísticas
make benchmark    # Testa performance
make clean        # Limpa compilados

🐛 Solução de problemas

"bpftool não encontrado"

# Ubuntu/Debian
sudo apt install linux-tools-common linux-tools-$(uname -r)

# Se ainda não funcionar, procure o caminho:
find /usr/lib -name bpftool 2>/dev/null

"BTF não encontrado" (/sys/kernel/btf/vmlinux não existe)

Seu kernel não foi compilado com BTF. Opções:

  • Atualizar para um kernel mais recente da distro
  • Compilar o kernel com CONFIG_DEBUG_INFO_BTF=y

"Cgroup v2 não detectado"

# Adicione ao GRUB:
sudo nano /etc/default/grub
# Na linha GRUB_CMDLINE_LINUX, adicione:
# systemd.unified_cgroup_hierarchy=1
sudo update-grub
sudo reboot

"Permissão negada"

O eBPF precisa de privilégios de root:

sudo make load

Containers não sobem

# Veja os logs:
make docker-logs

# Verifique se as portas estão livres:
sudo lsof -i :8080
sudo lsof -i :3306
sudo lsof -i :5432

WSL2: eBPF não funciona

O kernel padrão do WSL2 pode não ter suporte completo. Opções:

  1. Compilar um kernel customizado para WSL2 com BTF
  2. Usar uma VM Linux (VirtualBox ou VMware) com Ubuntu 22.04+

📚 Conceitos para entender melhor

O que é eBPF?

eBPF (extended Berkeley Packet Filter) é uma tecnologia do Linux que permite executar pequenos programas dentro do kernel, de forma segura. É usado para:

  • Monitoramento de rede (tcpdump, Cilium)
  • Segurança (Falco, Tetragon)
  • Performance (bcc tools, bpftrace)
  • Aceleração de rede (sockmap — nosso caso!)

O que é um Sockmap/Sockhash?

Um sockhash é um tipo de mapa BPF que armazena referências a sockets. É como uma tabela onde:

  • Chave = informações da conexão (IPs + portas)
  • Valor = referência ao socket do kernel

Por que isso é mais rápido?

Sem sockmap, dados entre containers passam por ~12 camadas de processamento no kernel. Com sockmap, passam por apenas 2 (o programa BPF e a cópia de memória).

Limitações

  • Funciona apenas para containers no mesmo host
  • Apenas TCP (não UDP)
  • Apenas IPv4 (esta implementação)
  • Precisa de kernel moderno (≥ 5.4)
  • Precisa de cgroup v2

📊 Resultados esperados

A melhoria depende do cenário, mas tipicamente:

Métrica Sem Sockmap Com Sockmap Melhoria
Latência query MySQL ~0.5ms ~0.2ms ~60%
Latência query PostgreSQL ~0.5ms ~0.2ms ~60%
Latência HTTP (nginx→node) ~1.0ms ~0.4ms ~60%
Throughput (req/s) ~5000 ~8000 ~60%

Os valores são aproximados e variam conforme o hardware e carga.


📜 Licença

GPL-2.0 (exigido pelo eBPF/kernel Linux)