Do Docker Compose ao Kubernetes: quando realmente vale a pena mudar?
“Já tenho vários containers rodando, será que preciso de Kubernetes?” Essa pergunta aparece com frequência em times que começaram com Docker Compose, foram adicionando serviços aos poucos e, em algum momento, olharam para o docker-compose.yml e sentiram que ele estava maior e mais frágil do que gostariam.
O problema é que a resposta mais comum costuma ser “sim, migre para Kubernetes”, sem antes identificar qual problema precisa ser resolvido. Kubernetes passou a ser tratado como o próximo passo natural, quase um rito de passagem para quem “leva infraestrutura a sério”. Esse raciocínio é perigoso.
Não se trata de ensinar a instalar um cluster ou escrever manifestos. A questão é outra: quais critérios técnicos e operacionais justificam a migração de uma aplicação containerizada de Docker Compose para Kubernetes? A pergunta mais útil não é “Kubernetes é melhor?”, mas “quais problemas eu tenho hoje que o Kubernetes resolveria e tenho capacidade de operar tudo o que viria junto com essa solução?”.
O cenário inicial
Um ponto de partida comum é uma aplicação dividida em alguns containers: um serviço web, um banco de dados, talvez um worker de fila e um cache. Tudo definido em um docker-compose.yml e executado em um único host, como uma VM ou um servidor dedicado. Este é um exemplo hipotético, não uma arquitetura real de um ambiente específico. Ainda assim, ele ilustra o cenário que costuma dar origem a essa dúvida.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
services:
web:
build: ./web
ports:
- "8080:8080"
depends_on:
- db
- cache
worker:
build: ./worker
depends_on:
- db
db:
image: postgres:16
volumes:
- db-data:/var/lib/postgresql/data
cache:
image: redis:7
volumes:
db-data:
Nesse estágio, o Compose costuma funcionar muito bem. docker compose up -d sobe o ambiente inteiro, docker compose logs -f mostra o que está acontecendo, um docker compose restart web resolve boa parte dos problemas do dia a dia. É simples de entender, simples de versionar e simples de explicar para alguém que está entrando no time agora.
O ponto de virada não é o número de containers em si. É o que a operação começa a exigir da infraestrutura.
Quando Docker Compose começa a ficar limitado
Segundo a própria documentação oficial do Docker Compose, o Compose foi desenhado para definir e rodar aplicações multi-container em um único host Docker. Essa frase, sozinha, já explica a maior parte das limitações que aparecem depois:
- Um único host é um único ponto de falha. Se a máquina cai, tudo cai junto. O Compose não faz o rescheduling automático dos containers para outro host, porque esse modelo não inclui um segundo host.
- Não há orquestração entre múltiplas máquinas. O Compose não sabe distribuir carga entre servidores diferentes nem mover um container de uma máquina para outra.
- Escalonamento é manual e limitado ao host. O comando
docker compose up --scale web=3sobe réplicas do serviço, mas todas compartilham os recursos, como CPU, memória e rede, da mesma máquina. Não há escalonamento horizontal real entre hosts. - Sem self-healing sofisticado. O Compose reinicia um container que falhou, respeitando a política de
restart, mas não toma decisões como “este host está com problema; mova a carga para outro lugar”. - Service discovery é local. A resolução de nomes entre serviços funciona dentro da rede criada pelo Compose no mesmo host. Não existe um mecanismo nativo de descoberta de serviços distribuído entre máquinas diferentes.
- Deploy costuma ser manual ou feito com scripts próprios. Rolling update, rollback automático e verificação de saúde durante o deploy não são recursos nativos do Compose. É possível simular parte disso com scripts, mas a construção e a manutenção ficam por sua conta.
Nada disso é um “defeito” do Compose. É uma questão de escopo. A ferramenta foi feita para um problema mais restrito e resolve muito bem esse problema.
O que Kubernetes resolve
O Kubernetes foi criado para cenários em que essas limitações passam a pesar: múltiplos hosts, cargas que precisam de resiliência e escala e equipes que precisam gerenciar tudo de forma declarativa. Conforme descrito na documentação oficial do Kubernetes, trata-se de “uma plataforma portável e extensível, de código aberto, para gerenciar workloads e serviços containerizados, que facilita tanto a configuração declarativa quanto a automação”.
Esses objetivos aparecem no dia a dia de algumas formas:
- Scheduling entre múltiplos nós. O kube-scheduler decide em qual nó cada Pod deve rodar, considerando recursos disponíveis, afinidade, taints/tolerations e outros critérios.
- Self-healing real. Se um nó falha, os Pods que estavam nele são recriados em outro nó saudável. Isso é rescheduling de verdade, não apenas o reinício de um container no mesmo lugar.
- Escalonamento horizontal automatizado. Com o Horizontal Pod Autoscaler, é possível aumentar ou reduzir o número de réplicas com base em métricas como o uso de CPU.
- Service discovery nativo. Os Services fornecem um nome estável e um IP virtual para um conjunto de Pods, independentemente do nó em que estejam rodando.
- Rollout e rollback estruturados. Um Deployment faz rolling update de forma controlada e pode reverter para a revisão anterior com um único comando.
- Gerenciamento declarativo do estado desejado. Você descreve o estado desejado, incluindo o número de réplicas, os recursos e os volumes, e o control plane trabalha continuamente para manter o ambiente de acordo com essa descrição.
Esses recursos resolvem problemas operacionais concretos. Eles tratam da orquestração de workloads em escala, mas não tornam, por si só, uma aplicação confiável.
O que Kubernetes não resolve
Kubernetes não resolve problemas de arquitetura da aplicação. Se a aplicação não foi pensada para rodar múltiplas réplicas (por exemplo, guarda estado em memória local sem replicação), colocá-la em um Deployment com replicas: 3 não vai magicamente torná-la distribuída e consistente.
Kubernetes não resolve observabilidade. Ele não vem com logging centralizado nem métricas prontas para uso. Isso continua sendo responsabilidade de ferramentas como Prometheus, Grafana ou uma stack de logs, que precisam ser instaladas, configuradas e mantidas.
Kubernetes não resolve persistência de dados por conta própria. Ele oferece abstrações como PersistentVolumes e PersistentVolumeClaims, mas o armazenamento durável, replicado e com backup ainda depende de uma solução específica, seja de um provedor de nuvem, seja de uma solução on-premises. Essa costuma ser uma das partes mais delicadas de operar Kubernetes fora de um cloud provider gerenciado.
Kubernetes não elimina a complexidade operacional. Ele a desloca. Em troca dos benefícios listados acima, você passa a operar um control plane, com API server, etcd, scheduler e controller manager, além de uma rede de cluster, políticas de RBAC e atualizações da própria plataforma. Também precisa dominar um vocabulário inteiro de objetos, como Pods, Deployments, Services, Ingress, ConfigMaps e Secrets.
Kubernetes não substitui maturidade operacional. Um cluster mal operado, sem monitoramento adequado, política de backup do etcd ou entendimento de requests e limits de recursos, pode ser menos confiável do que um Docker Compose bem administrado em um host estável. A ferramenta não compensa a falta de prática.
Docker Compose vs Kubernetes
A tabela abaixo resume a comparação em alguns aspectos que costumam pesar nessa decisão.
| Aspecto | Docker Compose | Kubernetes |
|---|---|---|
| Complexidade de operação | Baixa: um arquivo YAML e comandos diretos | Alta: control plane, rede de cluster, RBAC e upgrades da própria plataforma |
| Execução em um host | Ponto forte nativo | Suportado (ex.: k3s, minikube), mas é usar um martelo grande para um prego pequeno |
| Múltiplos hosts | Não é o objetivo da ferramenta | Ponto forte nativo, pois esse é justamente o cenário para o qual foi criado |
| Escalabilidade horizontal | Manual e limitada ao host | Nativa, entre múltiplos nós, com autoscaling |
| Alta disponibilidade | Depende inteiramente da disponibilidade do host único | Suportada nativamente via múltiplos nós e réplicas |
| Self-healing | Reinício de container via política de restart | Rescheduling entre nós, verificações de liveness/readiness |
| Deploy (rollout/rollback) | Manual ou via scripts próprios | Nativo, declarativo, com histórico de revisões |
| Service discovery | Local, dentro da rede do host | Nativo, via DNS interno e Services, entre nós |
| Persistência | Volumes locais do Docker | Abstração via PV/PVC, mas depende de storage externo bem configurado |
| Observabilidade | Não nativa: depende de ferramentas externas | Não nativa: depende de ferramentas externas. O ecossistema é maior, mas o esforço de integração continua existindo |
| Custo operacional (pessoas, tempo, curva de aprendizado) | Baixo | Alto, especialmente sem um serviço gerenciado |
Essa tabela não é uma verdade absoluta, e algumas ressalvas são importantes:
- “Complexidade” e “custo operacional” variam bastante conforme a experiência prévia do time. Para quem já opera Kubernetes no dia a dia, subir mais uma aplicação nele pode ser mais simples do que manter um Compose fora do padrão da equipe.
- Serviços gerenciados (EKS, GKE, AKS e afins) reduzem parte da complexidade do control plane, mas não eliminam a complexidade de modelar a aplicação em objetos do Kubernetes, nem o custo financeiro do serviço gerenciado.
- “Observabilidade não nativa” em ambos os lados não significa que as duas opções sejam equivalentes. O ecossistema de operadores e integrações prontas para Kubernetes, como o Prometheus Operator, costuma reduzir o esforço de configuração em comparação com a montagem da mesma stack em um host isolado com Compose.
- Docker Compose também suporta múltiplos ambientes por meio de vários arquivos, como o
docker-compose.override.yml, e integrações com ferramentas externas de orquestração leve. Isso reduz um pouco a rigidez da afirmação de que múltiplos hosts não são o objetivo da ferramenta.
Use a tabela como ponto de partida para a conversa, não como uma pontuação a ser somada.
Quando eu escolheria Docker Compose
Eu continuaria usando Compose nas seguintes situações:
- o número de serviços é pequeno e cabe confortavelmente em um host (ou poucos hosts, sem necessidade de coordenação entre eles);
- a aplicação não tem um requisito de disponibilidade que justifique tolerância à falha de um host. Por exemplo, uma indisponibilidade de alguns minutos durante uma manutenção é aceitável;
- a equipe prioriza a simplicidade operacional, seja por ser pequena, seja porque a infraestrutura não é o foco principal do trabalho;
- não existe, hoje, ninguém na equipe com experiência prática para operar um cluster Kubernetes com segurança, nem tempo ou orçamento para desenvolver essa competência antes de colocar um sistema crítico em produção.
Esse último ponto costuma ser subestimado. Um cluster Kubernetes mal operado não é “Docker Compose com mais recursos”. É um sistema distribuído, sujeito a problemas próprios, como falhas de rede entre nós, pressão de recursos e drift de configuração. Diagnosticar essas situações exige conhecimento específico.
Quando eu escolheria Kubernetes
Por outro lado, a migração começa a fazer sentido quando surgem necessidades concretas, como:
- a aplicação precisa rodar em mais de um host, seja por capacidade, seja por resiliência a falha de uma máquina;
- existe um requisito real de alta disponibilidade, com pouca ou nenhuma tolerância a indisponibilidade por falha de host;
- é necessário escalar réplicas horizontalmente de forma dinâmica, respondendo a variação de carga;
- o processo de deploy precisa de rollout controlado e rollback rápido e confiável, com histórico auditável;
- a equipe já tem, ou está disposta a adquirir, o conhecimento operacional necessário para manter o cluster saudável, incluindo monitoramento, backup do estado do cluster e uma política de atualização de versões.
Se pelo menos alguns desses pontos são reais e mensuráveis, e não apenas hipóteses, Kubernetes passa a ser uma opção justificável, mesmo com o custo operacional adicional.
Uma possível evolução
Entre “um host com Compose” e um “cluster Kubernetes completo”, existem alternativas intermediárias que costumam ser ignoradas quando a equipe decide migrar diretamente:
- Múltiplos hosts com Compose + um balanceador de carga na frente, para ganhar redundância simples sem adotar um orquestrador completo.
- Docker Swarm, que oferece orquestração multi-host com uma curva de aprendizado bem menor que Kubernetes, embora tenha um ecossistema e uma adoção de mercado mais restritos atualmente.
- Nomad (HashiCorp), como orquestrador mais simples que Kubernetes, para quem quer scheduling multi-host sem todo o vocabulário do Kubernetes.
- Um PaaS gerenciado (que abstrai a orquestração por trás de um
git pushou de um build automatizado), quando o objetivo é focar na aplicação e não na infraestrutura. - Kubernetes gerenciado (EKS, GKE, AKS, ou distribuições mais leves como k3s), como ponto de entrada para reduzir a operação do control plane antes de considerar um cluster on-premises completo.
Nenhum desses passos é obrigatório. Cada um resolve um subconjunto dos problemas listados acima, com uma fração da complexidade operacional de um Kubernetes completo. Vale avaliar se alguma dessas opções já resolveria o problema real antes de adotar uma solução mais complexa.
Conclusão
Kubernetes não deveria ser o destino automático de toda aplicação containerizada. Eu começaria pelos problemas operacionais: precisamos de múltiplos hosts, alta disponibilidade, escala dinâmica ou um rollout mais estruturado? Depois, avaliaria se a equipe tem condições de operar a solução escolhida e lidar com a complexidade que ela traz.
Docker Compose não é uma etapa “menor” ou “menos madura” antes do Kubernetes. É a ferramenta certa para alguns cenários, enquanto Kubernetes atende a outros, mais exigentes. A pergunta que vale a pena fazer não é “estamos atrasados por ainda usar Compose?”, mas “quais problemas concretos temos hoje e qual ferramenta os resolve com o menor custo operacional possível para a equipe que vai operar esse ambiente amanhã?”.
