🧠FFVAcademy
📦

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

ComponentePapelExemplos
OCI Image SpecFormato de imagem padronizadodocker build, buildkit
OCI Runtime SpecComo executar um containerrunc, crun, kata-runtime
Container RuntimeGerencia containerscontainerd, CRI-O, podman
Container EngineInterface do usuárioDocker, Podman, nerdctl
Kubernetes CRIInterface K8s → runtimecontainerd, 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