Files

363 lines
11 KiB
Markdown

# 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:
```bash
# 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
```bash
# Instala TUDO que é necessário automaticamente
sudo bash scripts/setup.sh
```
Ou instale manualmente (Ubuntu/Debian):
```bash
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
```bash
# 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
```bash
# Sobe nginx + node.js + mysql + postgresql
make docker-up
```
Aguarde até todos estarem prontos (pode levar ~30s na primeira vez):
```bash
# 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)
```bash
# Anota os tempos sem o sockmap ativo
bash scripts/benchmark.sh
```
Guarde os resultados para comparar depois!
### Passo 5: Ativar o eBPF Sockmap
```bash
# 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!)
```bash
# 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
```bash
make status
```
### Passo 8: Desativar quando quiser
```bash
# 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
```bash
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"
```bash
# 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"
```bash
# 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:
```bash
sudo make load
```
### Containers não sobem
```bash
# 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)