1. O Problema que o Docker Resolve
"Na minha máquina funciona." Poucas frases resumem tão bem um problema que atormentou (e ainda atormenta) times de desenvolvimento: um código que roda perfeitamente no notebook do desenvolvedor, mas quebra no servidor de produção — porque a versão do Python é outra, uma biblioteca do sistema está faltando, uma variável de ambiente não foi configurada, ou o sistema operacional simplesmente se comporta de forma diferente.
O Docker resolve exatamente isso: ele empacota uma aplicação junto com tudo que ela precisa para rodar — código, dependências, bibliotecas do sistema, variáveis de ambiente, configuração — em uma unidade isolada e portátil chamada container. Essa unidade roda da mesma forma em qualquer lugar que tenha o Docker instalado: no notebook do desenvolvedor, no servidor de CI, ou em produção. Esta é a primeira parte de uma série que vai do zero ao avançado em Docker: hoje o foco é entender o problema que ele resolve, os conceitos fundamentais e como eles se encaixam.
2. Containers vs Máquinas Virtuais
A comparação mais comum ao explicar Docker é com máquinas virtuais (VMs), porque ambos resolvem um problema parecido — isolar e empacotar aplicações — mas de formas muito diferentes.
Uma máquina virtual virtualiza o hardware inteiro: cada VM roda seu próprio sistema operacional completo (kernel incluso), gerenciado por um hypervisor. Isso garante isolamento forte, mas tem um custo alto: cada VM consome centenas de MBs a alguns GBs de disco e memória só para o SO, e leva de dezenas de segundos a minutos para inicializar.
Um container, por outro lado, virtualiza no nível do sistema operacional: todos os containers em uma máquina compartilham o mesmo kernel do host, mas cada um enxerga seu próprio sistema de arquivos, processos e rede isolados — usando recursos do kernel Linux como namespaces (isolamento de visão) e cgroups (limites de CPU/memória). O resultado é que containers são muito mais leves: alguns MBs a poucas centenas de MBs, com inicialização em milissegundos a poucos segundos.
┌─────────────────────────┐ ┌─────────────────────────┐
│ VM 1 │ VM 2 │ │ Container 1│Container 2│
│ ┌─────┐ │ ┌─────┐ │ │ ┌───────┐ │ ┌───────┐ │
│ │ App │ │ │ App │ │ │ │ App │ │ │ App │ │
│ ├─────┤ │ ├─────┤ │ │ ├───────┤ │ ├───────┤ │
│ │ SO │ │ │ SO │ │ │ │ Libs │ │ │ Libs │ │
│ └─────┘ │ └─────┘ │ │ └───────┘ │ └───────┘ │
├───────────┴──────────────┤ ├─────────────┴───────────┤
│ Hypervisor │ │ Docker Engine │
├───────────────────────────┤ ├───────────────────────────┤
│ SO Host + Hardware │ │ SO Host + Hardware │
└───────────────────────────┘ └───────────────────────────┘
Isso não significa que containers substituem VMs em todo cenário — VMs continuam sendo a escolha certa quando o isolamento precisa ser total (por exemplo, rodar cargas de múltiplos clientes não confiáveis na mesma máquina física) ou quando se precisa de um kernel diferente do host. Mas para o caso mais comum — empacotar e distribuir aplicações de forma consistente — containers ganham em leveza, velocidade de inicialização e densidade (quantas cargas cabem na mesma máquina).
3. Os Três Conceitos Fundamentais: Imagens, Containers e Registries
Todo o modelo mental do Docker gira em torno de três peças:
-
Imagem (image): um pacote read-only com tudo que uma aplicação precisa para rodar — sistema de arquivos, binários, bibliotecas, código da aplicação e metadados (como qual comando executar ao iniciar). É construída a partir de um
Dockerfile(assunto do Artigo 3 desta série) e organizada em camadas (layers) empilhadas, o que permite reaproveitar partes já construídas entre builds diferentes. - Container: uma instância em execução de uma imagem. Se a imagem é a "planta" (como uma classe em programação orientada a objetos), o container é o "objeto" instanciado a partir dela — com um processo rodando, um sistema de arquivos gravável em cima da imagem read-only, e seu próprio espaço de rede isolado. É possível rodar múltiplos containers a partir da mesma imagem, cada um independente dos outros.
-
Registry: um repositório para armazenar e distribuir imagens. O Docker Hub é o registry público padrão (onde vivem imagens oficiais como
python,postgres,nginx), mas existem registries privados (AWS ECR, Google Artifact Registry, GitHub Container Registry) para imagens internas de uma empresa.
O fluxo típico conecta essas três peças: escreve-se um Dockerfile, constrói-se uma imagem a partir dele, essa imagem é enviada (push) para um registry, e em qualquer máquina com Docker instalado é possível baixá-la (pull) e rodar um ou mais containers a partir dela.
Dockerfile → (build) → Imagem → (push) → Registry
│
(pull)
│
▼
Imagem local → (run) → Container
4. Instalando o Docker
O Docker está disponível para Linux, macOS e Windows. Em distribuições Linux, o pacote oficial (não o docker.io genérico de alguns repositórios, que costuma ficar desatualizado) é instalado assim:
# Debian/Ubuntu — script oficial de conveniência
curl -fsSL https://get.docker.com | sh
# Depois, para rodar docker sem sudo (requer novo login/logout):
sudo usermod -aG docker $USER
Em macOS e Windows, a forma mais comum é instalar o Docker Desktop, que empacota o engine, uma VM Linux leve (necessária porque o Docker depende de recursos do kernel Linux) e uma interface gráfica.
Depois de instalado, confirme que está funcionando:
docker --version
docker run hello-world
O segundo comando baixa uma imagem mínima do Docker Hub, roda um container a partir dela (que imprime uma mensagem de confirmação e termina) — é o "hello world" oficial do ecossistema Docker, útil para validar que o engine está rodando e tem permissão para baixar imagens.
5. Um Primeiro Container na Prática
Para sair da teoria, um exemplo real: rodar um servidor web Nginx sem instalar nada além do Docker na máquina.
docker run -d -p 8080:80 --name meu-nginx nginx
Decompondo o comando:
-
docker run— cria e inicia um container a partir de uma imagem. -
-d(detached) — roda o container em segundo plano, devolvendo o terminal imediatamente. -
-p 8080:80— mapeia a porta 8080 da máquina host para a porta 80 dentro do container (onde o Nginx escuta por padrão). -
--name meu-nginx— dá um nome fácil de referenciar ao container, em vez de um id gerado automaticamente. -
nginx— a imagem a usar. Como não existe localmente ainda, o Docker automaticamente fazpulldela do Docker Hub antes de rodar.
Acessar http://localhost:8080 no navegador já mostra a página padrão do Nginx. Para conferir que o container está rodando e depois removê-lo:
docker ps # lista containers em execução
docker stop meu-nginx # para o container
docker rm meu-nginx # remove o container (já parado)
Note que docker stop e docker rm são passos separados — parar um container não o remove, apenas encerra o processo dentro dele. Isso é proposital: permite inspecionar o estado final de um container que falhou antes de descartá-lo. Os comandos do dia a dia como esses (run, exec, logs, ps, build) são o assunto completo do próximo artigo.
6. Conclusão e Próximos Passos
Nesta primeira parte, vimos o problema real que o Docker resolve ("funciona na minha máquina"), como containers se diferenciam de máquinas virtuais em leveza e velocidade, os três conceitos que sustentam todo o ecossistema — imagens, containers e registries — e rodamos o primeiro container de ponta a ponta. No próximo artigo, o foco vai para os comandos que realmente viram hábito no dia a dia: run, exec, logs, ps e build, com exemplos de containers reais além do "hello world".
Imagem de capa: Logo oficial do Docker — Wikimedia Commons, fonte: docker.com/company/newsroom/media-resources
Referências:
Top comments (0)