Introdução ao Docker: containers vs máquinas virtuais

1. O Problema da Implantação de Aplicações

1.1. O cenário clássico: dependências, versões e o "funciona na minha máquina"

Todo profissional de DevOps já ouviu a frase: "Funciona na minha máquina". Esse problema clássico surge quando uma aplicação desenvolvida em um ambiente falha ao ser implantada em outro — seja por diferenças de versões de bibliotecas, sistema operacional, configurações de rede ou variáveis de ambiente. Em projetos complexos, gerenciar manualmente dependências como Python 3.8 vs 3.10, Node.js 14 vs 18, ou pacotes específicos do sistema operacional torna-se um pesadelo operacional.

1.2. A evolução das soluções: de servidores físicos para virtualização

Historicamente, a primeira tentativa de resolver esse problema foi a virtualização. Com hipervisores como VMware e VirtualBox, tornou-se possível encapsular uma aplicação com seu sistema operacional completo dentro de uma máquina virtual (VM). Isso resolveu o isolamento, mas trouxe alto overhead: cada VM consome gigabytes de RAM, um kernel completo e minutos para inicializar.

1.3. A promessa da conteinerização: isolamento leve e portabilidade

O Docker surgiu como resposta a essas limitações. Containers compartilham o kernel do host, eliminam a necessidade de um SO convidado e inicializam em segundos. A promessa é simples: "empacote sua aplicação e todas as dependências em uma imagem; execute-a em qualquer lugar".

2. O que é um Container?

2.1. Definição técnica: processos isolados com sistema de arquivos próprio

Um container é um processo Linux isolado que possui seu próprio sistema de arquivos, rede e árvore de processos. Diferente de uma VM, não há um kernel separado — o container usa o kernel do host, mas com visibilidade limitada.

2.2. Como o Docker utiliza namespaces e cgroups do Linux

O Docker utiliza dois mecanismos fundamentais do kernel Linux:

  • Namespaces: isolam recursos como PID (processos), rede, montagem de arquivos e usuários. Cada container enxerga apenas seus próprios processos e interfaces de rede.
  • Cgroups (control groups): limitam e monitoram o uso de recursos como CPU, memória e I/O de disco. Impedem que um container consuma todos os recursos do host.

2.3. Imagens como templates imutáveis para criar containers

Imagens Docker são templates imutáveis que contêm tudo que um container precisa: código, runtime, bibliotecas e configurações. São construídas em camadas (layers) e armazenadas em registries como Docker Hub. Cada docker run cria um container a partir de uma imagem.

# Exemplo: estrutura de uma imagem Docker
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app/
CMD ["python3", "/app/app.py"]

3. O que é uma Máquina Virtual?

3.1. Camadas de abstração: hypervisor, kernel convidado e sistema operacional completo

Uma VM utiliza um hypervisor (tipo 1 como KVM ou tipo 2 como VirtualBox) que virtualiza hardware completo. Cada VM executa seu próprio kernel e sistema operacional convidado. Isso significa que uma VM Linux rodando em um host Windows carrega todo o kernel Linux, drivers, serviços de init e bibliotecas.

3.2. Exemplos clássicos: VirtualBox, VMware, KVM

  • VirtualBox: hypervisor tipo 2, popular para desenvolvimento desktop.
  • VMware Workstation: hypervisor tipo 2, foco em empresas.
  • KVM: hypervisor tipo 1, nativo do Linux, usado em datacenters e nuvens.

3.3. Overhead de recursos: CPU, memória e armazenamento

Cada VM reserva recursos fixos: tipicamente 1-4 GB de RAM, 10-20 GB de disco e um núcleo de CPU. Em um host com 16 GB de RAM, cabem no máximo 4-5 VMs com 4 GB cada. O overhead do kernel convidado consome cerca de 200-500 MB adicionais por VM.

4. Comparação Detalhada: Containers vs Máquinas Virtuais

4.1. Isolamento: nível de processo (container) vs nível de sistema (VM)

Característica Container Máquina Virtual
Isolamento Nível de processo (namespace) Nível de sistema (kernel separado)
Kernel Compartilhado com host Próprio kernel
Segurança Moderada (escape de container é possível) Alta (hypervisor isola completamente)

4.2. Consumo de recursos: compartilhamento do kernel vs kernel dedicado

Um container ocupa apenas o espaço da aplicação (dezenas a centenas de MB). Uma VM ocupa o espaço do sistema operacional convidado (1-5 GB) mais o da aplicação. Em termos de memória, containers compartilham páginas de kernel, enquanto VMs duplicam.

4.3. Tempo de inicialização: segundos (container) vs minutos (VM)

# Comparação de tempo de inicialização
Container Nginx:  ~0.5 segundos
VM Ubuntu com Nginx: ~30-60 segundos (incluindo boot do kernel)

5. Quando Usar Containers (Docker) e Quando Usar Máquinas Virtuais

5.1. Casos ideais para containers: microsserviços, CI/CD, desenvolvimento e deploy rápido

  • Microsserviços: cada serviço em seu container, escalando independentemente.
  • CI/CD: pipelines que constroem e testam em ambientes idênticos.
  • Desenvolvimento: ambientes reproduzíveis com Docker Compose.
  • Deploy rápido: escala horizontal em segundos com Kubernetes.

5.2. Casos ideais para VMs: sistemas legados, múltiplos sistemas operacionais, segurança rigorosa

  • Sistemas legados: aplicações que exigem versões específicas de SO.
  • Múltiplos SOs: rodar Windows e Linux no mesmo host.
  • Segurança rigorosa: ambientes multitenant onde o isolamento do hypervisor é exigido.

5.3. Cenários híbridos: usando VMs como hosts para Docker (ex: na nuvem)

Na prática, provedores de nuvem como AWS, GCP e Azure executam containers dentro de VMs. Uma instância EC2 (VM) hospeda múltiplos containers Docker. Isso combina o isolamento da VM com a eficiência dos containers.

6. Docker na Prática: Primeiros Passos com Containers

6.1. Comandos essenciais: docker run, docker ps, docker stop

# Baixar e executar um container Nginx
docker run -d --name meu-nginx nginx:latest

# Listar containers em execução
docker ps

# Parar um container
docker stop meu-nginx

# Remover um container
docker rm meu-nginx

6.2. Mapeamento de portas e volumes: expondo serviços e persistindo dados

# Mapear porta 8080 do host para porta 80 do container
docker run -d -p 8080:80 --name web nginx:latest

# Montar volume para persistir logs
docker run -d -p 8080:80 -v /meus-logs:/var/log/nginx nginx:latest

6.3. Exemplo rápido: rodando um servidor web Nginx dentro de um container

# Passo 1: Executar Nginx em segundo plano
docker run -d -p 80:80 --name meu-site nginx:alpine

# Passo 2: Verificar se está rodando
docker ps

# Passo 3: Testar no navegador (http://localhost) ou via curl
curl http://localhost

# Passo 4: Parar e remover
docker stop meu-site && docker rm meu-site

7. Integração com Kubernetes e o Ecossistema DevOps

7.1. Por que containers são a base do Kubernetes: orquestração em escala

Kubernetes (K8s) é um orquestrador de containers. Ele gerencia automaticamente a implantação, escalonamento e operação de containers em clusters de máquinas. Sem containers, o Kubernetes não teria uma unidade padrão de deploy. O Pod do Kubernetes é essencialmente um grupo de containers que compartilham recursos.

7.2. Diferença entre Docker (container runtime) e Kubernetes (orquestrador)

  • Docker: cria e gerencia containers individualmente. Foco em desenvolvimento e execução local.
  • Kubernetes: orquestra containers em escala. Gerencia dezenas a milhares de containers, com balanceamento de carga, descoberta de serviços, atualizações rolling e auto-scaling.

7.3. Como a conteinerização viabiliza pipelines DevOps: build, teste e deploy consistentes

# Exemplo de pipeline DevOps com containers
# 1. Build: criar imagem a partir do código
docker build -t minha-app:v1.0 .

# 2. Teste: executar testes dentro do container
docker run minha-app:v1.0 pytest

# 3. Publicar: enviar imagem para registry
docker push minha-app:v1.0

# 4. Deploy: no Kubernetes
kubectl set image deployment/app minha-app:v1.0

A conteinerização garante que o mesmo artefato (imagem) seja testado e implantado em todos os ambientes — desenvolvimento, staging e produção — eliminando o "funciona na minha máquina".

Conclusão

Containers Docker e máquinas virtuais não são concorrentes, mas ferramentas complementares. As VMs oferecem isolamento mais forte e suporte a múltiplos sistemas operacionais. Os containers oferecem eficiência, velocidade e consistência ideais para microsserviços, pipelines DevOps e orquestração com Kubernetes. Para o profissional DevOps moderno, dominar ambos — e saber quando usar cada um — é essencial.

Referências