Antes dos containers, o problema mais comum no desenvolvimento de software era: funciona na minha máquina, mas não funciona no servidor. Containers resolveram isso empacotando a aplicação junto com tudo que ela precisa para rodar — e mudaram para sempre a forma como software é desenvolvido e entregue.
O problema que containers resolveram
Na prática, o problema era simples de enunciar e difícil de resolver: um desenvolvedor escrevia código no laptop com Python 3.9, Redis 6.2 e uma versão específica de uma biblioteca. O servidor de produção tinha Python 3.7, Redis 5.0 e uma versão diferente da biblioteca. O comportamento era diferente. Os bugs eram diferentes. O tempo perdido era real.
A solução anterior era a VM — uma máquina virtual completa com sistema operacional próprio. Funcionava para isolar ambientes, mas era pesada: uma VM Windows Server gasta gigabytes de RAM só para o SO antes de rodar qualquer aplicação.
O container veio com uma proposta diferente: em vez de virtualizar o hardware inteiro (como a VM faz), o container virtualiza apenas o ambiente da aplicação, compartilhando o kernel do sistema operacional do host.
Como um container funciona tecnicamente
Por baixo dos panos, containers usam dois recursos do kernel Linux que existem desde antes do Docker:
Namespaces: criam isolamento. Cada container tem seu próprio “espaço de nomes” para processos, rede, sistema de arquivos e usuários. Um processo dentro do container não vê processos de outros containers, não acessa a rede do host diretamente, enxerga um sistema de arquivos isolado.
cgroups (control groups): controlam e limitam recursos. Com cgroups, você define que um container pode usar no máximo 2 CPU cores e 512 MB de RAM. O container não pode ultrapassar esse limite, o que evita que uma aplicação mal comportada derrube outras no mesmo host.
Quando você roda um container, o Docker (ou outro runtime) configura namespaces e cgroups automaticamente, sem que você precise entender os detalhes. O resultado é um processo isolado que acredita que está rodando em uma máquina própria, mas na prática compartilha o kernel do host.
Imagem Docker vs container em execução
Essa distinção é fundamental e frequentemente confundida:
Imagem Docker: é o template — um snapshot imutável e empacotado que contém o sistema operacional base, as bibliotecas, as dependências e o código da aplicação. A imagem é construída uma vez (com o comando docker build) e pode ser usada para criar quantos containers você quiser. É como uma classe em orientação a objetos.
Container: é uma instância da imagem em execução. A imagem fica estática; o container roda, processa requisições, escreve logs, e pode ser parado e destruído. Você pode ter 10 containers rodando a partir da mesma imagem. É como um objeto instanciado da classe.
Quando um container é destruído, qualquer dado escrito dentro dele desaparece — a menos que você use volumes (armazenamento persistente externo ao container). Essa é uma das características mais importantes para entender: containers são efêmeros por design.
Docker vs containerd vs Podman
O ecossistema evoluiu. Docker foi o pioneiro e popularizou os containers, mas hoje não é o único player:
Docker: ainda o mais usado em desenvolvimento local. Inclui a CLI (docker), o daemon (dockerd), o runtime de containers e ferramentas de build. Para uso em produção, a tendência é usar apenas o runtime sem o daemon completo do Docker.
containerd: o runtime de containers que o Kubernetes usa por padrão (desde a versão 1.24, quando o Docker foi descontinuado como runtime do K8s). É mais leve, focado em rodar containers sem toda a camada extra do Docker. Você provavelmente não interage com containerd diretamente — ele opera por baixo do Kubernetes.
Podman: alternativa ao Docker sem daemon (daemonless), compatível com os mesmos comandos Docker. Roda containers sem precisar de um processo root em background, o que tem vantagens de segurança. Preferido por alguns times que priorizam segurança ou trabalham em ambientes Red Hat/RHEL.
Para desenvolvimento do dia a dia, Docker ainda é o padrão. Para produção em Kubernetes, o runtime é containerd e você raramente precisa se preocupar com isso diretamente.
Por que containers são a base do Kubernetes
Kubernetes orquestra containers — não VMs, não processos bare metal, não aplicações instaladas diretamente no SO. A escolha de containers como unidade básica não é acidental:
- Portabilidade: uma imagem de container roda igual em qualquer ambiente que suporte o runtime
- Imutabilidade: uma nova versão é uma nova imagem — nunca se atualiza um container em execução, substitui-se por um novo
- Velocidade: um container inicia em segundos; uma VM leva minutos
- Densidade: centenas de containers cabem em um servidor onde caberiam dezenas de VMs
Quando o Kubernetes precisa rodar mais réplicas de uma aplicação, ele instrui o runtime (containerd) a criar novos containers a partir da imagem. Quando precisa atualizar, substitui containers antigos por containers novos da nova versão da imagem — o rolling update.
Container vs VM: quando usar cada um
| Critério | Container | VM |
|---|---|---|
| Isolamento | Nível de processo (compartilha kernel do host) | Nível de SO (kernel próprio) |
| Tempo de inicialização | Segundos | Minutos |
| Uso de recursos | Leve — sem SO completo | Pesado — SO completo por VM |
| Portabilidade | Alta — roda em qualquer runtime compatível | Média — depende do hypervisor |
| Segurança de isolamento | Menor (compartilha kernel) | Maior (kernel isolado) |
| Aplicações Windows | Limitado (containers Windows existem, mas têm restrições) | Ideal |
| Workloads legados | Difícil de containerizar | Funciona bem |
A resposta prática: containers para aplicações modernas, APIs, microserviços e tudo que você está desenvolvendo hoje. VMs para workloads legados, aplicações Windows, sistemas que precisam de isolamento de kernel completo (compliance, segurança regulada).
Precisa de ambiente containerizado em cloud privada no Brasil? A Adentro oferece infraestrutura para containers e Kubernetes com suporte em português.