Máquinas virtuais vs. contêineres: qual usar

1. Fundamentos: O que são Máquinas Virtuais e Contêineres?

Uma máquina virtual (VM) é uma abstração completa de um computador físico, executada sobre um hipervisor (como VMware ESXi, Hyper-V ou KVM). Cada VM contém seu próprio sistema operacional convidado, drivers virtuais e aplicações. O hipervisor gerencia o acesso ao hardware real, garantindo isolamento total entre as VMs.

Arquitetura de uma Máquina Virtual:
+------------------+  +------------------+
| Aplicação        |  | Aplicação        |
| SO Convidado     |  | SO Convidado     |
| (Linux)          |  | (Windows)        |
+------------------+  +------------------+
|   Hipervisor (Hypervisor Type 1 ou 2)   |
+----------------------------------------+
|            Hardware Físico              |
+----------------------------------------+

Um contêiner, por outro lado, compartilha o kernel do sistema operacional host. Utiliza recursos como namespaces (para isolar processos) e cgroups (para limitar recursos). O engine de contêineres (Docker, Podman, containerd) gerencia a execução, mas não virtualiza hardware — apenas isola processos no nível do sistema operacional.

Arquitetura de um Contêiner:
+------------------+  +------------------+
| Aplicação        |  | Aplicação        |
| Bibliotecas      |  | Bibliotecas      |
+------------------+  +------------------+
|      Engine de Contêineres (Docker)     |
+----------------------------------------+
|         Kernel Compartilhado            |
|              (Linux)                    |
+----------------------------------------+
|            Hardware Físico              |
+----------------------------------------+

A diferença fundamental: VMs oferecem isolamento total (cada VM roda seu próprio kernel), enquanto contêineres oferecem isolamento leve (compartilham o kernel do host).

2. Comparação de Desempenho e Eficiência de Recursos

O overhead de uma VM é significativo: cada instância consome memória para o sistema operacional convidado (tipicamente 512 MB a 2 GB), além do hipervisor. Um contêiner, por compartilhar o kernel, consome apenas a memória das aplicações e bibliotecas.

Exemplo de consumo de recursos para 10 instâncias:
VM (cada): 1 vCPU, 1 GB RAM, 10 GB de disco = 10 vCPUs, 10 GB RAM, 100 GB
Contêiner (cada): 0.5 vCPU, 128 MB RAM = 5 vCPUs, 1.28 GB RAM

Tempo de inicialização: uma VM precisa realizar boot completo do SO (30 segundos a 2 minutos), enquanto um contêiner inicia em milissegundos. Isso torna contêineres ideais para escalabilidade horizontal rápida.

Densidade de implantação: no mesmo hardware, é possível executar de 5 a 10 vezes mais contêineres do que VMs, dependendo da carga de trabalho.

3. Nível de Isolamento e Segurança

VMs oferecem isolamento de hardware: um ataque no kernel de uma VM não afeta outras VMs ou o host. O hipervisor atua como barreira de segurança.

Contêineres compartilham o kernel: se uma vulnerabilidade de escalonamento de privilégios for explorada, um atacante pode comprometer o host e todos os contêineres. Medidas de mitigação incluem:

Medidas de segurança para contêineres:
- Executar contêineres como usuário não-root
- Usar namespaces de usuário (user namespaces)
- Aplicar seccomp e AppArmor/SELinux
- Utilizar runtime seguro (gVisor, Kata Containers)

Para ambientes multi-inquilinos (SaaS público), VMs são mais seguras. Para aplicações internas com equipe confiável, contêineres são aceitáveis.

4. Gerenciamento e Orquestração

Gerenciamento de VMs envolve ferramentas como VMware vSphere, Microsoft SCVMM, OpenStack. Cada VM precisa de patches de SO, atualizações de kernel e configuração de rede individual.

Orquestração de contêineres usa Kubernetes, Docker Swarm ou Amazon ECS. O gerenciamento é mais simples: imagens imutáveis, atualizações via rolling updates, auto-scaling.

Exemplo de manifesto Kubernetes:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web
        image: nginx:1.25
        ports:
        - containerPort: 80

Complexidade operacional: VMs exigem administradores de sistema especializados em SOs e hipervisores. Contêineres exigem conhecimento de Dockerfiles, Kubernetes e redes de contêineres.

5. Portabilidade e Ciclo de Desenvolvimento

VMs usam formatos OVF/OVA, mas a portabilidade depende do hipervisor de destino. Migrar uma VM de VMware para Hyper-V pode exigir conversão.

Contêineres são altamente portáveis: uma imagem Docker criada localmente pode ser executada em qualquer ambiente que suporte Docker — desenvolvimento, teste, produção, nuvem pública.

Exemplo de Dockerfile para aplicação Node.js:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Integração com CI/CD: contêineres são artefatos imutáveis ideais para pipelines. Cada build gera uma imagem versionada. VMs exigem snapshots ou scripts de provisionamento (Packer, Ansible).

6. Custos e Licenciamento

Cada VM requer licenciamento de SO (Windows Server, Red Hat Enterprise Linux). Para 50 VMs, os custos de licenciamento podem ser proibitivos.

Contêineres compartilham o kernel do host: apenas o SO host precisa de licença. Para ambientes Linux, isso reduz custos drasticamente.

Estimativa de custos mensais (10 servidores):
VMs: 10 licenças Windows Server (~$1.000/mês) + hipervisor (~$500) = $1.500
Contêineres: 1 licença Linux host (~$0) + plataforma Kubernetes ($0 self-hosted) = $0

Em nuvem, instâncias dedicadas (EC2, VMs) são mais caras que serviços gerenciados de contêineres (ECS, GKE, AKS), que otimizam a utilização de recursos.

7. Critérios de Decisão: Quando Usar Cada Um?

Use VMs quando:
- Aplicações legadas exigem SOs específicos (Windows Server 2008, Solaris)
- Requisitos de segurança rigorosos (dados sensíveis, conformidade PCI-DSS, HIPAA)
- Necessidade de múltiplos SOs diferentes no mesmo hardware
- Ambientes multi-inquilinos com isolamento total

Use contêineres quando:
- Arquitetura de microsserviços
- Necessidade de escalabilidade rápida e auto-scaling
- Equipe DevOps e pipelines CI/CD maduros
- Aplicações stateless e de curta duração (jobs, workers)
- Redução de custos de infraestrutura

Estratégia híbrida (recomendada para produção):

Exemplo de arquitetura híbrida:
+----------------------------------------+
|        Cluster Kubernetes               |
|  +----------+  +----------+            |
|  | Contêiner |  | Contêiner |           |
|  | (Node.js) |  | (Python)  |           |
|  +----------+  +----------+            |
+----------------------------------------+
|        VM (Worker Node)                 |
|        Ubuntu Server 22.04              |
+----------------------------------------+
|        Hipervisor (VMware/KVM)          |
+----------------------------------------+
|            Hardware Físico              |
+----------------------------------------+

Nessa abordagem, VMs fornecem isolamento e flexibilidade de SO, enquanto contêineres rodam dentro das VMs para orquestração e escalabilidade. É o padrão em grandes empresas como Google, Netflix e Spotify.


Referências