Como o Docker limita, conecta e mantém containers vivos?
Nosso processo já enxerga outro mundo, mas ainda pode consumir toda a memória do host — e não possui uma rede própria para conversar de forma controlada.
namespaces · cgroups · mounts · signalsVOCABULÁRIO VISUAL
As peças do kernel que entram em cena.
Namespaces controlam visibilidade. Cgroups agrupam processos para contabilizar e limitar recursos. O escalonador continua sendo o mesmo descrito em Como uma CPU funciona por dentro; o kernel apenas passa a aplicar uma política ao grupo.
| Recurso | Arquivo cgroup v2 | Efeito |
|---|---|---|
| Memória | memory.max | limita bytes; pode acionar OOM no grupo |
| CPU | cpu.max | quota por período, não “meio núcleo” físico |
| Processos | pids.max | novos forks falham no limite |
let group = Cgroup::create("/sys/fs/cgroup/casulo", id)?;
group.write("memory.max", "268435456")?;
group.write("cpu.max", "50000 100000")?;
group.write("pids.max", "64")?;
group.attach(child_pid)?;O filho nasce bloqueado num pipe. O pai prepara limites, anexa o PID e só então libera a execução. Ao falhar, encerra o filho e remove o grupo vazio. Métricas de memory.current, cpu.stat e pids.current alimentam inspect.
host └─ cgroup casulo/01J… ├─ memory.max = 256 MiB ├─ cpu.max = 50 ms / 100 ms ├─ pids.max = 64 └─ cgroup.procs = [4822, 4830]
O runtime precisa de privilégios para criar mounts e rede, mas a aplicação não. O Casulo configura UID/GID, remove capabilities não necessárias e ativa no_new_privs, que impede ganhar novos privilégios por execve. Um perfil opcional de seccomp bloqueia chamadas claramente fora do escopo.
Isso reduz superfície, mas não transforma Casulo numa sandbox para código hostil. Não implementamos todos os perfis, LSMs, rootless completo, atualização contra vulnerabilidades ou auditoria de runtimes maduros.
Ao criar network namespace, o processo recebe apenas loopback. Criamos um par veth: tudo que entra numa ponta sai pela outra. Uma ponta vai para o namespace; a outra liga-se à bridge do Casulo no host.
container namespace host rede externa
eth0 10.88.0.2 ─ veth ─ veth01 ─ bridge casulo0 ─ rota/NAT ─▶ internet
│ 10.88.0.1
└ gateway 10.88.0.1Nomes de interface são derivados de IDs validados e registrados. Se a regra de NAT falha, removemos regra, porta da bridge, veth e namespace em ordem inversa. O Casulo usa API netlink quando disponível; uma fronteira pequena e documentada chama nft para regras transacionais.
cliente → host:8080
→ regra DNAT: destino vira 10.88.0.2:80
→ bridge casulo0 → veth01 → eth0
→ processo escutando :80
resposta → conntrack reconhece fluxo → tradução inversa → cliente$ sudo casulo run web:1 -p 8080:80 --memory 256m --cpus 0.5
[casulo] 01J… running · 10.88.0.2 · host:8080 → 80CREATED → RUNNING → STOPPING → EXITED → REMOVED
│ │ │ │
recursos logs + PID SIGTERM cleanup apaga estado
prazo/SIGKILLO supervisor captura stdout e stderr em arquivos rotacionados, grava PID externo e horário, encaminha sinais e registra o código de saída. stop envia SIGTERM e espera; --time 0 pede encerramento imediato. rm recusa container em execução sem --force.
$ casulo ps
ID IMAGE STATE PID CPU MEMORY
01J… web:1 running 4822 37/50 ms 82/256 MiB
$ casulo inspect 01J…
state=running ip=10.88.0.2 cgroup=/casulo/01J…
$ casulo logs -f 01J…
servidor ouvindo em :80
$ casulo stop 01J… --time 10
$ casulo rm 01J…O registro local não é um daemon. Cada comando lê estado bloqueado por arquivo e confirma a realidade no kernel. PIDs são combinados com o instante de início para evitar atingir um processo diferente após reutilização do número.
| Ecossistema maduro | Papel | No Casulo |
|---|---|---|
| cliente Docker | interface do usuário | CLI |
| daemon Docker | API, imagens, rede e estado | não há daemon |
| containerd | lifecycle de alto nível | módulos locais |
| runc | cria container conforme bundle OCI | runtime interno didático |
A Open Container Initiative define formatos para imagem, runtime e distribuição. Para interoperar, precisaríamos ler manifests OCI, preparar bundles com config.json, obedecer hooks e implementar toda a semântica exigida. Similaridade de peças não significa conformidade.
casulo run web:1 → resolve imagem e verifica hashes → monta lower + upper + work = merged → cria namespaces e pivot_root → cria cgroup e anexa processo bloqueado → cria veth, bridge, rota e porta publicada → reduz privilégios e libera exec → captura logs e observa processo principal → recebe sinal, espera, força se necessário → limpa rede, cgroup e mounts em ordem inversa
Testes verificam limites, falha de fork, OOM observável, conectividade entre dois containers, porta publicada, encaminhamento de sinais, logs, transições e rollback injetado em cada etapa. Todos usam prefixos exclusivos e comprovam ausência de mounts, interfaces, regras e cgroups órfãos.
O Casulo 1.0 é funcional para laboratório Linux controlado. Não é seguro para imagens desconhecidas ou produção. Sua arquitetura torna visíveis processos, namespaces, rootfs, camadas, cgroups, rede e lifecycle.
Fontes primárias: cgroup v2 do kernel, capabilities(7), network_namespaces(7), veth do kernel e especificações OCI.
Um container Linux é um conjunto de processos comuns cuja visão, recursos, privilégios, filesystem e rede são organizados por mecanismos do kernel do host. A ferramenta coordena essas peças e precisa desfazê-las corretamente.
PROJETO COMPLETO · ESTADO EXATO DESTE CAPÍTULO
Baixe o Casulo 1.0.
Pacote exclusivo deste artigo, com código Rust, exemplos, testes condicionais para Linux e roteiro de limpeza. Não execute em produção nem use imagens desconhecidas.