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:
bpftoolgera o arquivovmlinux.ha partir do kernel atual (contém todas as definições de estruturas do kernel)clangcompila osockmap.bpf.cpara 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:
- Compilar um kernel customizado para WSL2 com BTF
- 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)