Cluster Distribuído — Master + Workers
O cluster transforma o Permafrost de biblioteca em sistema distribuído. Um dataset de 1 TB é dividido em tasks e processado em paralelo por múltiplos workers.
Arquitetura
PermafrostClient
│
│ POST /jobs {"source": "dados.csv", "codec": "lzma2"}
▼
PermafrostMaster (porta 8700)
│ Divide em tasks (1 task por chunk de 50k linhas)
│ Scheduling round-robin para workers idle
│
├──────────────────────┬──────────────────────┐
▼ ▼ ▼
Worker-01 Worker-02 Worker-N
(porta 8801) (porta 8802) (porta 880N)
│ │ │
freeze chunk_0 freeze chunk_1 freeze chunk_2
│ │ │
POST /tasks/{id}/done POST /tasks/{id}/done POST /tasks/{id}/done
└──────────────────────┴──────────────────────┘
│
Master agrega → Job DONE
Quick Start — cluster local
Opção 1: Python puro (desenvolvimento)
# Terminal 1: Master
python -m permafrost.cluster master --port=8700
# Terminal 2 e 3: Workers
python -m permafrost.cluster worker --master=http://localhost:8700 --port=8801
python -m permafrost.cluster worker --master=http://localhost:8700 --port=8802
Opção 2: Docker Compose (recomendado)
# No diretório do projeto
docker-compose up --scale worker=4
# → 1 master + 4 workers prontos em < 30 segundos
Submeter jobs via Python
import permafrost as pf
client = pf.PermafrostClient("http://localhost:8700")
# Verificar saúde do cluster
health = client.health()
print(f"Workers: {health['workers']} | Idle: {health['idle_workers']}")
# Submeter job de freeze
job_id = client.freeze(
"dados.csv",
"dados.permafrost",
codec="lzma2",
chunk_rows=50_000,
)
print(f"Job submetido: {job_id}")
# Aguardar com progress
status = client.wait(job_id, poll_interval=0.5)
print(f"Status: {status['status']}")
print(f"Linhas processadas: {status['total_rows']:,}")
print(f"Tasks: {len(status['tasks'])}")
Monitoramento
# Listar todos os jobs
jobs = client.list_jobs()
for job in jobs:
print(f"{job['job_id']}: {job['status']} ({job['progress']*100:.0f}%)")
# Listar workers
workers = client.list_workers()
for w in workers:
print(f"{w['worker_id']}: {w['status']} | jobs_done={w['jobs_done']}")
# Cancelar job
client.cancel(job_id)
Múltiplos jobs paralelos
O Master suporta múltiplos jobs simultâneos. Workers idle são automaticamente alocados para tasks de qualquer job na fila.
import permafrost as pf
client = pf.PermafrostClient("http://localhost:8700")
# Submeter 3 jobs ao mesmo tempo
job_ids = [
client.freeze("vendas_2022.csv", "vendas_2022.permafrost"),
client.freeze("vendas_2023.csv", "vendas_2023.permafrost"),
client.freeze("clientes.csv", "clientes.permafrost"),
]
# Aguardar todos
results = [client.wait(jid) for jid in job_ids]
for r in results:
print(f"{r['status']} — {r['total_rows']:,} linhas")
Fault tolerance
O Master implementa retry automático até 3 vezes por task:
Task falha no Worker-01
→ Master re-enfileira a task
→ Atribui ao Worker-02 (ou Worker-01 quando idle)
→ Se falhar 3 vezes → Job marcado como FAILED
Heartbeat: workers enviam ping a cada 5s. Workers que pararem de responder
são marcados como offline e suas tasks são redistribuídas.
Configuração avançada
import permafrost as pf
# Master com configurações customizadas
master = pf.PermafrostMaster(
host="0.0.0.0",
port=8700,
)
master.MAX_RETRIES = 5 # tentativas por task
master.HEARTBEAT_S = 10 # intervalo de heartbeat
master.DEFAULT_CHUNK = 50_000 # linhas padrão por task
# Worker em host remoto
worker = pf.PermafrostWorker(
master_url="http://master-host:8700",
host="0.0.0.0",
port=8801,
worker_id="worker-prod-01",
)
worker.register()
Docker Hub — imagens prontas
As imagens oficiais estão publicadas no Docker Hub e funcionam sem build local.
Iniciar o cluster (1 comando)
Isso sobe:
- 1 Master em http://localhost:8700
- 4 Workers conectados automaticamente ao Master
Imagens disponíveis
| Imagem | Tag | Descrição |
|---|---|---|
caua-ferreira/permafrost-master |
latest |
Nó coordenador |
caua-ferreira/permafrost-master |
0.5.1 |
Versão específica |
caua-ferreira/permafrost-worker |
latest |
Nó de processamento (inclui zpaq) |
caua-ferreira/permafrost-worker |
0.5.1 |
Versão específica |
Ambas disponíveis para linux/amd64 e linux/arm64 (Apple M1/M2).
Desenvolvimento local (build da imagem)
# Build local sem Docker Hub
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up --scale worker=2
Variáveis de ambiente
Master:
| Variável | Padrão | Descrição |
|---|---|---|
PERMAFROST_MASTER_HOST |
0.0.0.0 |
Interface de escuta |
PERMAFROST_MASTER_PORT |
8700 |
Porta da API REST |
PERMAFROST_MAX_RETRIES |
3 |
Tentativas por task |
PERMAFROST_DEFAULT_CHUNK |
50000 |
Linhas padrão por task |
Worker:
| Variável | Padrão | Descrição |
|---|---|---|
PERMAFROST_MASTER_URL |
http://master:8700 |
URL do Master |
PERMAFROST_WORKER_HOST |
0.0.0.0 |
Interface de escuta |
PERMAFROST_WORKER_PORT |
8801 |
Porta do worker |
Publicar suas próprias imagens
Se você fizer fork do projeto, as imagens são publicadas automaticamente
ao criar uma tag no GitHub (via .github/workflows/docker.yml):
git tag v0.5.2 && git push --tags
# → GitHub Actions: build + push para Docker Hub
# → docker.io/caua-ferreira/permafrost-master:0.5.2
# → docker.io/caua-ferreira/permafrost-worker:0.5.2
Configure os secrets no GitHub:
- DOCKERHUB_USERNAME — seu usuário Docker Hub
- DOCKERHUB_TOKEN — token de acesso (não a senha)