📦
Containers por baixo: namespaces e cgroups no Linux
⏱ 16 min de leitura·+80 XP
Docker popularizou containers, mas a tecnologia são primitivos Linux: namespaces para isolamento de visão e cgroups para limite de recursos. Entender isso explica por que containers iniciam em milissegundos, o que "root no container" significa, e como Kubernetes gerencia recursos.
Os primitivos: namespaces e cgroups
# Criar namespace de PID sem Docker: # unshare --pid --fork --mount-proc bash # Dentro: ps aux mostra apenas os processos do novo namespace # No host: pstree -p mostra o processo real com PID diferente # Verificar namespaces de um container em execução: # docker inspect container_id | grep Pid # ls -la /proc/PID/ns/ # lrwxrwxrwx pid → pid:[4026531836] ← namespace ID # lrwxrwxrwx net → net:[4026532008] ← namespace ID (diferente do host) # lrwxrwxrwx mnt → mnt:[4026532009] # Verificar cgroups de um container: # docker stats container_id # cat /sys/fs/cgroup/docker/CONTAINER_ID/memory.current ← uso atual # cat /sys/fs/cgroup/docker/CONTAINER_ID/memory.max ← limite # Criar cgroup manualmente (sem Docker): # mkdir /sys/fs/cgroup/meu_grupo # echo 512M > /sys/fs/cgroup/meu_grupo/memory.max # echo $$ > /sys/fs/cgroup/meu_grupo/cgroup.procs # adiciona shell atual # Agora o shell e seus filhos não podem usar mais de 512MB # Namespace de rede — cada container tem suas interfaces: # docker exec container ip addr show # eth0 → 172.17.0.2/16 (rede Docker bridge) # Isso é uma veth pair: um lado no container, outro no host como veth0 import subprocess # Ver configuração de rede dos containers: # ip link show ← veth pairs visíveis no host # brctl show ← bridge docker0
Union filesystem: layers de imagem Docker
# Uma imagem Docker é uma pilha de layers:
# Layer 5 (topo, read-write): container layer
# Layer 4: COPY requirements.txt + RUN pip install
# Layer 3: COPY . /app
# Layer 2: FROM python:3.12-slim ← layers da imagem base
# Layer 1 (base): filesystem mínimo do Debian
# Union filesystem (OverlayFS no Linux moderno) monta todas as layers
# como um único filesystem — copy-on-write:
# - Leitura: busca da camada mais alta para baixo
# - Escrita: copia arquivo para o container layer (CoW)
# - Deleção: cria "whiteout" na camada superior
# Ver layers de uma imagem:
# docker image inspect python:3.12-slim | jq '.[0].RootFS.Layers'
# docker history python:3.12-slim
# OverlayFS montagem:
# mount -t overlay overlay -o lowerdir=layer1:layer2:layer3,upperdir=container,workdir=work /merged
# lowerdir = layers de imagem (read-only)
# upperdir = container layer (read-write)
# merged = visão unificada do container
# Inspecionar overlay do container:
# docker inspect container_id | jq '.[0].GraphDriver'
# {
# "Name": "overlay2",
# "Data": {
# "LowerDir": "/var/lib/docker/overlay2/.../diff:...",
# "MergedDir": "/var/lib/docker/overlay2/.../merged",
# "UpperDir": "/var/lib/docker/overlay2/.../diff",
# "WorkDir": "/var/lib/docker/overlay2/.../work"
# }
# }Container runtime: OCI e o que Docker realmente faz
| Componente | Papel | Exemplos |
|---|---|---|
| OCI Image Spec | Formato de imagem padronizado | docker build, buildkit |
| OCI Runtime Spec | Como executar um container | runc, crun, kata-runtime |
| Container Runtime | Gerencia containers | containerd, CRI-O, podman |
| Container Engine | Interface do usuário | Docker, Podman, nerdctl |
| Kubernetes CRI | Interface K8s → runtime | containerd, CRI-O |
# runc: executa containers OCI diretamente sem Docker # runc é o que Docker usa internamente # Criar um container OCI manualmente: # 1. Extrair filesystem base # mkdir rootfs && docker export $(docker create busybox) | tar -xC rootfs/ # 2. Gerar config.json (spec OCI) # runc spec # 3. Executar # runc run meu-container # Dockerfile → imagem → container: o caminho completo # docker build → buildkit → OCI image (layers + config.json) # docker run → containerd → runc → namespaces + cgroups + overlayfs # Resultado: processo isolado no host # Security: seccomp filtra syscalls permitidas no container # docker run --security-opt seccomp=perfil.json # Default: 44 syscalls bloqueadas (inclui clone, ptrace, kexec) # Privileged: --privileged remove todos os limites (root real no host!)
✅
Modelo mental: container = processo com namespaces (visão isolada) + cgroups (recursos limitados) + overlayfs (filesystem em layers). Não é uma VM — compartilha o kernel do host. Root num container é root no namespace do container, mapeado para um UID não-privilegiado no host (com user namespaces). Containers não adicionam overhead de CPU/memória para o processo — overhead é de I/O do overlay e latência de rede virtual.
💡
Próximo: Serialização e endianness — os bytes que viajam pela rede e o que UTF-8 realmente é.
🧩
Quiz rápido
3 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito
Continue lendo