Migrar de Kubernetes pra stack mais simples: case real de redução de complexidade

Quando a empresa adota K8s cedo demais, todo mundo paga. O caminho inverso — sair do K8s pra orquestração mais simples — é viável e mais comum do que parece. O que validar antes, durante e depois.

Equipe HeroCtl··15 min

A narrativa pública dos últimos seis anos foi sempre na mesma direção: todo mundo migra para Kubernetes. Conferências, posts patrocinados, vagas de SRE, cases de fornecedor — o vetor é único. Saiu de máquina virtual nua e subiu pra K8s. Saiu de Heroku e subiu pra K8s. Saiu de Docker Compose e subiu pra K8s. A direção é uma seta só, e quem não está na seta deve estar fazendo errado.

A realidade silenciosa que ninguém publica em conferência é o vetor inverso: centenas de times migram fora do Kubernetes depois de descobrir que pagaram caro pela complexidade que não precisavam. Não é manchete, mas acontece todo mês. Empresa de quinze devs com cluster EKS de seis nós percebe que o time de plataforma virou metade do orçamento de engenharia. Startup que adotou K8s no segundo dia descobre que três anos depois ainda gasta uma sexta-feira inteira por mês só atualizando versão de Helm chart de operadores. Time de produto que deveria estar shippando feature está debugando webhook de admission controller.

Esse post é o playbook pra essa migração inversa, com as armadilhas reais que a gente viu acontecer. Não é pitch — é manual operacional. Se você lê isso e decide ficar em Kubernetes, ótimo. A decisão informada de ficar é tão valiosa quanto a decisão informada de sair.

A pergunta de qualificação: você devia sequer considerar isso?

Antes de qualquer coisa, descarte o caso. Migração inversa só faz sentido pra um perfil específico — e a maioria dos times que pensa em sair de K8s não está nesse perfil. Os outros estão pesquisando alternativa quando deveriam estar contratando mais um SRE ou simplificando o uso atual do K8s.

O perfil em que a migração faz sentido tem seis sinais simultâneos:

Sinal 1: a empresa roda Kubernetes em produção há um ano ou mais. Migração não é experimento. Se o time está em K8s há três meses e já está reclamando, o problema é onboarding, não plataforma. Espere o ciclo de aprendizado completar antes de declarar bancarrota.

Sinal 2: o time de plataforma tem entre uma e três pessoas. Empresas com cinco ou mais engenheiros dedicados à plataforma têm escala de operação que justifica K8s. Abaixo disso, a ferramenta tende a consumir o time inteiro em manutenção.

Sinal 3: o cluster tem menos de cinquenta servidores em produção. Acima desse número, o ecossistema de K8s te dá ferramentas de tooling — escalonamento horizontal de nós, balanceamento entre regiões, federação multi-cluster — que outra stack não te dá. Abaixo, você está pagando overhead pra capacidade que não usa.

Sinal 4: os apps são típicos. Web HTTP, banco relacional, cache em memória, jobs assíncronos, alguma fila. Se a stack inclui operador exótico de banco distribuído, malha de serviço com políticas L7 sofisticadas, ou agendamento de GPU pra treinamento de modelo, a migração inversa fica complicada.

Sinal 5: 80% do tempo de plataforma é manutenção, não feature nova. Se o time de plataforma passa a maioria das semanas atualizando Helm chart, debugando upgrade de versão de cluster, ou arrumando webhook que falhou, é sintoma claro. A plataforma virou produto interno que consome ela mesma.

Sinal 6: o salário total de plataforma representa mais de 5% do MRR. Não é regra absoluta, mas é métrica útil. Quando o time que opera a infra custa mais que um vigésimo da receita recorrente mensal, a infra está cara demais pra escala atual da empresa.

Se a sua empresa marca os seis, vale ler o resto do post. Se marca três ou quatro, leia mas decida com cautela. Se marca um ou dois, provavelmente o problema é outro.

Quem definitivamente não deve migrar

Mesma honestidade no inverso. Existe perfil em que sair de K8s é decisão errada, e quem se enquadra deveria fechar essa aba.

Time forte de plataforma com processo maduro. Se você tem cinco engenheiros de plataforma que dominam K8s, pipeline de CI/CD estável, runbook escrito, observabilidade configurada — sair de tudo isso pra começar do zero numa stack mais simples é jogar fora investimento real. A simplicidade do destino não compensa o reset.

Stack que depende de operadores críticos. Banco relacional com replicação automática gerenciada por operador, fila distribuída com balanceamento gerenciado por operador, banco colunar com bootstrap automático. Esses operadores são valor real. Trocar pra "humano cuida disso" é regressão operacional, não simplificação.

Compliance que exige Kubernetes nominalmente. Alguns frameworks de auditoria — FedRAMP em determinados níveis, certos contratos de governo, alguns selos de segurança setoriais — listam ferramentas pré-aprovadas. Se o seu compliance officer precisa apontar pra um certificado existente, K8s é a resposta. Migrar pra ferramenta que não está na lista cria fricção que custa mais do que economia.

Federação multi-cluster em produção. Se você roda workloads que se movem entre clusters em regiões diferentes, com replicação de estado coordenada por ferramenta tipo Argo ou FluxCD em modo multi-cluster, o ecossistema do K8s tem primitivas que outras stacks não têm. Migrar disso é projeto de seis meses no mínimo.

Workloads de ML/AI com agendamento de GPU complexo. Treinamento distribuído, particionamento de GPU, agendamento que entende afinidade de hardware específico. K8s tem operadores e plugins maduros pra isso. Stack mais simples não tem.

Se você se enquadra em qualquer um desses cinco, a recomendação honesta é ficar onde está e otimizar o uso atual do K8s.

Pré-flight assessment: um a dois dias antes de comprometer

Migração inversa começa com inventário. Antes de marcar reunião de "vamos sair do K8s", o time precisa medir o que tem hoje. Sem números, a decisão é vibe — e vibe não sobrevive ao primeiro problema imprevisto durante cutover.

Inventário de manifests. Rode kubectl get all -A --output yaml > all.yaml e conte. Quantos arquivos no repositório de manifestos? Quantas linhas no agregado? Quantos namespaces? A nossa medição informal em times pequenos: empresa com dez apps típicos costuma ter entre 1.500 e 4.000 linhas de YAML, espalhadas entre Deployment, Service, Ingress, ConfigMap, Secret, HorizontalPodAutoscaler, e algum NetworkPolicy. Cada uma dessas linhas é trabalho de migração.

Inventário de releases Helm. helm list -A mostra cada chart instalado. Cada um é uma decisão. Chart de operador de banco — vai virar job comum no destino, com replicação manual? Chart de ingress — vai virar config de roteador integrado? Chart de monitoring — vai virar agente externo? Quanto mais charts, mais tempo de migração.

Inventário de operadores. kubectl get crds lista cada Custom Resource Definition. Cada CRD é uma dependência crítica que provavelmente não tem equivalente direto no destino. Se a saída tem três ou quatro CRDs (cert-manager, ingress-nginx, prometheus-operator, sealed-secrets), está dentro do esperado pra time pequeno. Se tem trinta CRDs, a migração não é trivial — você construiu plataforma sobre plataforma.

Inventário de RBAC e políticas complexas. NetworkPolicy declarando isolamento entre namespaces, PodSecurityPolicy ou PodSecurityStandards configurados, RoleBinding de granularidade fina. Tudo isso precisa de equivalente no destino, e o equivalente raramente é 1:1.

Volume de tráfego. Requisições por segundo no horário de pico, conexões simultâneas no banco, throughput agregado de saída. O destino precisa absorver tudo isso. Se você nunca mediu, mede agora — antes de comprometer cronograma de migração.

Mapeamento de Service para Ingress. Cada Service exposto vira ponto de entrada no destino. Lista de domínios, certificados associados, sticky sessions configuradas, regras de path-based routing. Sem essa lista, a migração quebra exatamente no momento de cutover.

Esse assessment leva um a dois dias pra um dev competente. É barato. Pular essa etapa é a maior fonte de migração que estoura cronograma.

Decisão de stack alvo

Quatro opções principais hoje pra time pequeno. Cada uma com tradeoff explícito.

Opção A: Docker Swarm. Compatibilidade direta com formato Compose, multi-server simples, baixa curva de aprendizado. Bom pra time de um dev que já conhece Compose. Limitação séria: Swarm está em modo manutenção há tempos, sem desenvolvimento ativo de feature nova. Roda e funciona, mas você está apostando em ferramenta que não evolui.

Opção B: Nomad. Similar a K8s em modelo declarativo, mas mais simples e com binário único. Bom pra quem gosta de modelo declarativo robusto e quer HA real. Limitação: a licença mudou pra modelo restrito desde 2023, e a empresa por trás foi adquirida em 2025. Pra adoção nova hoje, é caminho com asterisco.

Opção C: HeroCtl. Orquestrador independente com plano de controle replicado, binário único, configuração curta. Bom pra quem quer simplicidade operacional e alta disponibilidade real desde o primeiro dia. Limitação honesta: ecossistema menor que K8s, sem biblioteca profunda de operadores prontos.

Opção D: painel self-hosted (Coolify, Dokploy, similares). Painel web que orquestra Docker numa máquina ou pequeno conjunto. Bom pra time muito pequeno sem requisitos formais de HA. Limitação: arquitetura que não suporta consenso distribuído real entre vários servidores — cresceu, virou ponto único de falha.

A escolha depende do perfil. Time de um dev sem requisito de SLA = Opção D. Time pequeno com requisito de HA real = Opção C. Time que prefere modelo declarativo robusto e aceita licença restrita = Opção B. Time já investido em Compose = Opção A.

Os cinco passos da migração

A partir daqui o playbook assume que a stack alvo é HeroCtl, mas o esqueleto se aplica a qualquer destino. Ajuste os mapeamentos conceituais pra Swarm/Nomad/Coolify conforme escolha.

Passo 1 — Setup do destino em paralelo (uma semana)

Regra dura: nunca migrar in-place. O cluster K8s atual continua rodando intocado durante toda a migração. O destino é provisionado em paralelo, em servidores novos, com domínio temporário ou subdomínio de teste.

Três a cinco servidores Linux novos. Instalar a stack alvo. Validar que a rede entre os servidores funciona, que o storage persiste após reboot, que certificados são emitidos automaticamente, que secrets podem ser injetados em apps. Conectar o destino com o mesmo registry de imagens que o K8s atual usa — assim a mesma imagem que roda em produção vai pro destino sem rebuild.

Esse passo é deliberadamente leve. A intenção é provar que o destino funciona com app sintético antes de comprometer migração de app real.

Passo 2 — Migração de manifests para spec do destino (uma a duas semanas)

A maior parte do esforço está aqui. Cada workload do K8s precisa ser re-expressa no formato do destino. O mapeamento conceitual de K8s pra HeroCtl serve de referência:

  • Deployment + ReplicaSet → job com replicas: N. O conceito é o mesmo: N cópias do mesmo workload, balanceadas entre servidores.
  • Service ClusterIP → service interno. No HeroCtl não precisa criar — qualquer task tem nome resolvível dentro do cluster por padrão.
  • Service LoadBalancer ou Ingress → ingress integrado. Sem operador externo, sem cert-manager separado, sem ingress-nginx — tudo embutido no orquestrador.
  • Pod → task. Conceito 1:1.
  • PersistentVolume → volume nomeado. Pode exigir cópia de dados, dependendo do storage backend usado em K8s.
  • ConfigMap → bloco de env ou arquivo no spec. Não há objeto separado.
  • Secret → secret integrado do orquestrador. Encriptado em repouso no plano de controle.
  • HorizontalPodAutoscaler → política de scaling no spec do job. Disparada por uso de CPU, RAM, ou métrica custom.
  • DaemonSet → job com restrição de placement "1 por nó".
  • CronJob → job tipo periodic com expressão cron.
  • Helm chart → spec custom. Não converte 1:1 — re-escrever na mão.

Em linhas brutas, a redução é dramática. Um app web típico em K8s tem 30 a 50 linhas de Deployment, mais 20 de Service, mais 50 a 100 de Ingress + cert-manager + annotations. Total 100 a 170 linhas. O equivalente em HeroCtl fica entre 30 e 50 linhas, agregando tudo num único arquivo.

Tempo médio de migração por app: um a três dias pra dev competente. Dez apps em três semanas é ritmo realista. Se chegar muito mais lento, há operador escondido ou complexidade não detectada no assessment — pare e re-meça.

Passo 3 — Migração de banco e storage (um a três dias)

Duas estratégias. Se o banco é gerenciado (RDS, Cloud SQL, equivalente), o destino só aponta a nova string de conexão e pronto — o banco fica onde estava, agnóstico de plataforma. Se o banco é self-hosted no K8s, é dump-and-restore manual: pg_dump no banco antigo, pg_restore no novo, com janela de manutenção curta no momento de cutover.

Volumes persistentes do K8s viram volumes nomeados no destino. Pode exigir cópia de dados via rsync ou snapshot — dependendo do storage backend, isso é janela adicional.

Secrets são extraídos do K8s e re-inseridos no destino. Use canal seguro (kubectl get secret -o yaml é só meio de leitura; nunca commitar arquivo intermediário). No HeroCtl, secrets são submetidos via API com TLS e ficam encriptados no plano de controle.

Passo 4 — Cutover (uma a três horas, normalmente noturno)

O passo crítico. Pre-checks antes de qualquer mudança de DNS: smoke test no destino — login funciona, página principal carrega, banco está conectado, latência é aceitável, fila processa job, métricas chegam no monitoring. Se algum dos cinco falha, aborta o cutover.

DNS preparado: TTL reduzido pra 60 segundos vinte e quatro horas antes da janela. Sem isso, propagação leva horas e rollback é doloroso.

Cutover propriamente dito: muda o registro DNS pra apontar pros IPs do destino. Monitora 5xx e latência em janela de cinco minutos. Se algo quebra significativamente nos primeiros trinta minutos, switch DNS de volta pro K8s — rollback completo em sessenta segundos de propagação adicional.

Mantém o cluster K8s rodando como standby por trinta dias. Não desliga. O custo extra é justificado: se algum bug latente aparece na semana três do destino, você ainda tem onde voltar.

Passo 5 — Decommission do K8s (uma a duas horas, após trinta dias)

Trinta dias depois do cutover, sem incidente significativo, hora de desligar. kubectl delete cluster no caso self-hosted, ou aws eks delete-cluster (ou equivalente em outras nuvens) no caso gerenciado. Cancelar managed addons separadamente — a fatura tem itens que não somem só com delete-cluster.

Reembolso prorrata do mês corrente do plano gerenciado, se o provedor oferece. Cancelamento das instâncias de worker. Backup final do estado do cluster antes do delete, em caso de auditoria futura.

As seis armadilhas do caminho

Assessment técnico cobre o que você consegue medir. As armadilhas abaixo são o que escapa do assessment e quebra o cronograma. Cada uma já causou migração que estourou prazo em algum time real.

Armadilha 1: dependência oculta em operador. Você acha que não tem operador complexo, mas cert-manager + ingress-nginx + sealed-secrets já é stack de três operadores. E provavelmente tem mais — kube-state-metrics pra monitoring, external-dns pra atualizar DNS automaticamente, reloader pra reiniciar pods quando ConfigMap muda. Mapear tudo. Cada operador é trabalho de migração que o assessment superficial perde.

Armadilha 2: assumir que Helm chart é re-escrevível em um dia. Chart simples com cinco templates é re-escrita de poucas horas. Chart complexo com trinta templates, valores aninhados, hooks de pre-install/post-install, e dependências de subcharts pode levar uma semana só pra mapear pra spec equivalente. Calibre a estimativa pelo chart mais complexo, não pelo mais simples.

Armadilha 3: sticky sessions sem documentar. O ingress-nginx em K8s suporta sessão persistente por configuração de annotation. Se o app depende disso (carrinho de compras, sessão de admin, websocket persistente) e ninguém documentou, a migração quebra exatamente no cutover quando um usuário começa a alternar entre dois servidores backend e perde estado de sessão. Auditar configuração de ingress antes — não confiar só no que o time lembra.

Armadilha 4: limites de recurso diferentes. K8s usa limit/request com semântica precisa: request é garantia, limit é teto. O destino pode ter modelo declarativo diferente (limite hard, ou cota agregada por job, ou semântica de soft-limit). Erro de tuning aqui quebra autoscaling — o app fica subdimensionado em produção e não escala quando deveria, ou superdimensionado e desperdiça capacidade. Re-medir consumo real após cutover, ajustar limites na primeira semana.

Armadilha 5: formato de log. Alguns ingress em K8s emitem log em JSON por default — parser downstream (Loki, Datadog, ELK) está configurado pra esse formato. Destino pode emitir log em texto plano ou formato diferente. Parsing downstream quebra silenciosamente — alertas param de disparar porque o pattern não bate mais. Verificar formato de log do roteador integrado do destino antes do cutover.

Armadilha 6: pipeline de CI/CD acoplado. GitOps com ArgoCD ou FluxCD apontando pra K8s precisa ser re-trabalhado. Se o pipeline aplica manifesto declarativo com kubectl apply ou helm upgrade, isso não funciona no destino. Adapter scripts no estágio de deploy são necessários — receber o manifesto velho, traduzir pra spec novo, submeter via API. Estimar uma a duas semanas só pra pipeline de CI/CD, separado do tempo de migração de manifests.

Cronograma realista

Calibração honesta de expectativa, em três faixas de tamanho.

Time de um a dois devs, cinco a dez apps: quatro a seis semanas total. Decomposição: uma semana de setup do destino, duas a três semanas de migração de manifests e ajuste, um a três dias de cutover, trinta dias de operação paralela, um dia de decommission. Atenção: o trabalho de migração rouba foco do desenvolvimento de produto durante esse período. Considere janela de feature freeze.

Time de três a cinco devs, vinte a cinquenta apps: oito a doze semanas. A multiplicação não é linear — apps adicionais aumentam matriz de testes de cutover. Vale dedicar uma pessoa em tempo integral à migração e manter o resto do time em produto.

Empresa com cem ou mais apps: projeto de quatro a seis meses, com uma a duas pessoas dedicadas. Nesse tamanho, a migração vira fase com gerente de projeto, marcos quinzenais, e relatórios de status. Não é sprint.

Resultados típicos pós-migração

Faixas observadas em times que completaram a migração. Não são garantias — são pontos de referência.

  • Redução de RAM total: 30% a 50%. O overhead de Kubernetes é real, e some quando você sai. Cluster que usava 32 GB de RAM agregado vira algo entre 16 e 22 GB pro mesmo workload.
  • Redução de custo cloud: 40% a 70%. Vem de três frentes: sem managed control plane (US$73/mês por cluster sai do orçamento), sem NAT gateway por subnet (alguns provedores cobram por GB), instâncias menores possíveis (overhead de plataforma sai do consumo).
  • Tempo de deploy: similar ou ligeiramente melhor. Não é onde está o ganho — K8s é razoavelmente rápido em deploy quando configurado bem.
  • Tempo de aprendizado pra dev novo: uma semana, contra quatro a seis em K8s. O modelo mental é mais simples — menos abstrações intermediárias entre "quero rodar isso" e "está rodando".
  • Tempo de operação mensal: uma a três horas-dev de manutenção, contra vinte a quarenta em K8s. O ganho maior. É aqui que o ROI se materializa.

Pra calibrar a última métrica: cluster público de demonstração da gente roda em quatro servidores totalizando cinco vCPUs e dez gigabytes de RAM, com plano de controle ocupando entre 200 e 400 MB por servidor. Eleição de novo coordenador, em caso de falha do atual, leva cerca de sete segundos. Spec de aplicação típica em HeroCtl tem cerca de cinquenta linhas — comparado a trezentas linhas ou mais de YAML em Kubernetes pra equivalente "hello world" com TLS e ingress.

A pergunta inevitável: vai voltar pra Kubernetes eventualmente?

Honestidade. Depende de escala.

Time que cresce pra trinta ou mais devs, com cem ou mais servidores em produção, multi-região, com requisito de federação entre clusters, eventualmente bate no teto de stack mais simples. Nessa escala, K8s vira escolha racional — o ecossistema te dá ferramentas que outras stacks não têm. A migração de volta é projeto de meses, não dias, mas é caminho viável.

Pra startups que se mantêm sub-cinquenta servidores ao longo de cinco anos — a maioria absoluta delas — raramente faz sentido voltar. O ganho operacional da stack mais simples se mantém durante toda a vida útil do produto.

Migração reversa (HeroCtl → K8s) também é projeto de semanas, não dias. Não é decisão one-way. Se a empresa cresce muito mais rápido do que esperava, o caminho de volta existe — mais caro do que ficar, mas existe. A decisão de migrar agora não te prende pra sempre.

Perguntas que a gente recebe

Em quanto tempo paga o ROI? Pra time de um a dois devs com cluster pequeno, a migração paga em três a seis meses — o salário-equivalente de tempo recuperado em manutenção excede o custo do projeto de migração. Pra times maiores, depende do quanto o time de plataforma consumia em manutenção; tipicamente seis a doze meses.

Posso manter Kubernetes pra um workload específico e migrar o resto? Sim, e em alguns casos é a estratégia correta. Workload com operador crítico (banco distribuído, fila com balanceamento gerenciado) fica no K8s. O resto vai pra stack mais simples. Os dois clusters convivem com domínios separados ou path-based routing num roteador upstream. Custa um pouco mais que consolidar, mas evita re-escrever o que ainda funciona bem.

Helm charts complexos: vale re-escrever? Caso a caso. Chart de operador de terceiro com cinquenta arquivos: provavelmente não vale, mantém em K8s ou troca a tecnologia. Chart próprio com vinte templates: vale, é re-escrita de poucos dias e elimina dependência de Helm.

ArgoCD funciona com HeroCtl? Não diretamente — ArgoCD foi feito pra aplicar manifesto K8s. Mas o conceito de GitOps funciona: pipeline observa o repositório, traduz spec do destino pra payload de API, submete via curl autenticado. Plugin nativo está em consideração; por enquanto é adapter script de cinquenta linhas.

O time que aprendeu Kubernetes — vão ficar resentidos? Pergunta legítima. Curva de aprendizado de K8s é investimento real, e ninguém gosta de ver investimento descartado. Conversa direta: a habilidade não some. K8s continua sendo padrão de mercado pra escala grande, e dev que já dominou continua empregável e valioso. A migração é decisão de produto pra escala atual, não veredicto sobre o conhecimento individual.

Cloud agnostic é mais ou menos viável depois? Mais viável, na prática. Stack mais simples roda em qualquer servidor Linux com Docker — bare metal, VPS de qualquer provedor, instância de qualquer nuvem. K8s gerenciado amarra você ao provedor (EKS na AWS, GKE na Google, AKS na Azure) — cada um com sabor próprio. Sair amplia opções.

Tem case público de empresa que fez essa migração? Vários, mas a maioria não publica em conferência (o vetor narrativo continua sendo K8s pra todos os lados). Em fóruns e em conversas informais, é fácil encontrar relato. Se você quer conversar com alguém que fez a migração, escreve pra gente — fazemos a ponte.

Fechamento

A decisão de sair de Kubernetes pra stack mais simples não é confissão de derrota — é reconhecimento de que a ferramenta certa depende da escala atual da empresa, e que a escala atual da empresa não é a do livro de marketing do colosso. Time pequeno, cluster pequeno, apps típicas, plataforma consumindo metade do orçamento de engenharia: é exatamente o cenário em que a migração inversa paga.

Não é decisão de uma tarde. É projeto de quatro a seis semanas pra time pequeno, com inventário, mapeamento, cutover noturno, trinta dias de operação paralela, e decommission cuidadoso. Mas é projeto cujo ROI se mede em horas-dev recuperadas todo mês — todo mês, pelos próximos anos da empresa.

Se quiser experimentar HeroCtl como destino candidato:

curl -sSL get.heroctl.com/install.sh | sh

Roda em qualquer servidor Linux com Docker. Três servidores formam plano de controle replicado com alta disponibilidade real. Spec de aplicação fica entre trinta e cinquenta linhas, agregando todo o necessário (replicação, ingress, certificado automático, secrets). Plano Community gratuito permanente cobre toda a stack descrita aqui — só Business e Enterprise adicionam SSO, RBAC granular, auditoria detalhada, escrow de código e suporte com SLA, voltados pra empresas com requisitos formais de plataforma.

Pra contexto adicional, k3s vs HeroCtl: quando cada um faz sentido trata da escolha quando o time já decidiu sair do K8s vanilla mas hesita entre distribuição leve de K8s e orquestrador independente. E Kubernetes é overkill: quando você não precisa é o argumento de fundo pra quem ainda não está convencido de que a complexidade é desnecessária na escala atual.

Migração inversa não é manchete de conferência. Mas é a decisão certa pra mais times do que a narrativa pública admite.

Faz parte do tema
#kubernetes#migracao#simplificacao#case#caso-de-uso