25 de maio de 202613 min de leitura

Migração com zero downtime de Ingress NGINX para Envoy Gateway: o que nenhum guia conta

Andrew Katsikas, Pelotech

Cloud Native Computing Foundation

Times que rodam Ingress NGINX em produção estão, cada vez mais, avaliando caminhos de migração à medida que o networking do Kubernetes evolui para o Gateway API.

Para muitas organizações, o desafio não é apenas selecionar uma implementação de Gateway API, mas projetar uma estratégia de migração que minimize o risco operacional durante o corte.

A maioria dos times de engenharia sabe que precisa migrar, mas falta tempo para fazer uma avaliação adequada, não tem certeza de qual controller Gateway API é a melhor aposta de longo prazo e sabe que um corte apressado vai derrubar tráfego de produção no momento em que os TTLs de DNS discordarem da realidade.

Este artigo é um case study de uma migração de Ingress NGINX que rodamos recentemente para um cliente na AWS. Ele percorre como escolhemos o Envoy Gateway, como testamos a migração primeiro em nossa própria infraestrutura, por que nosso primeiro corte bem-sucedido não foi bom o suficiente e a abordagem de weighted DNS que finalmente nos deu um resultado limpo de zero downtime. Há também um breve FAQ no final cobrindo as perguntas que mais recebemos sobre essa transição.

Se você está passando por essa migração, o objetivo aqui é poupar o trabalho de descoberta que já fizemos.

Por que migrar do Ingress NGINX para o Gateway API?

O Ingress NGINX controller é um dos controllers mais amplamente implantados no ecossistema Kubernetes, e muitos clusters de produção ainda o utilizam.

Com nenhum patch de segurança, nenhuma nova funcionalidade e uma API Ingress congelada, o Gateway API é o próximo passo lógico. É uma especificação mais expressiva que substitui anotações por recursos dedicados para as mesmas preocupações.

Em um engagement recente com um cliente na AWS, avaliamos várias implementações de Gateway API e abordagens de migração antes de optar pelo Envoy Gateway.

Como utilizamos o Ingress NGINX no Foundation, nossa plataforma GitOps opinativa para Kubernetes, essa migração era tão relevante para nós quanto para nosso cliente.

Praticamente todos os guias de migração que lemos durante a pesquisa param em "tráfego movido". Isso é um resultado aceitável para uma demo. Em produção, conseguir mover o tráfego sem derrubar requisições em andamento é um problema separado — e é sobre isso que o resto deste artigo trata.

Migrando um cliente do Ingress NGINX para o Envoy Gateway

Escolhendo o controller Gateway API certo

O primeiro passo foi colocar a mão na massa em um ambiente de baixo risco. Usamos Kind — Kubernetes IN Docker — que permite criar um cluster Kubernetes local dentro de contêineres Docker. É uma ótima maneira de prototipar e experimentar sem tocar em infraestrutura real, e usamos isso durante toda a avaliação.

Começamos avaliando opções com um projeto chamado ing-switch. É uma ferramenta de migração que varre um cluster em busca de recursos Ingress em todos os principais controllers, mapeia anotações com classificações de impacto e gera manifests de migração, seja via CLI ou interface visual. Isso nos deu um ponto de partida útil para entender com o que estávamos lidando e o que uma migração realmente envolveria.

Uma coisa que queríamos explorar era se um único controller poderia atender tanto ao Ingress quanto ao Gateway API simultaneamente. A ideia era que um controller de modo duplo pudesse servir como ponte, permitindo executar ambos lado a lado durante a transição, em vez de fazer um corte abrupto.

Lançamos uma rede ampla inicialmente, incluindo projetos fora do ecossistema CNCF. Conforme a avaliação avançava, o alinhamento com a CNCF emergiu como um filtro significativo. A Cloud Native Computing Foundation hospeda os projetos mais críticos do ecossistema Kubernetes, incluindo ArgoCD, cert-manager, ExternalDNS e muitos outros que formam a espinha dorsal do Foundation.

Ao tomar uma decisão de infraestrutura de longo prazo para um cliente, focamos em projetos que conquistaram consenso da comunidade e atendem ao padrão da CNCF para prontidão de produção.

Além do status CNCF, nos importávamos com algumas coisas específicas:

  • Paridade de anotações com o que já estávamos usando
  • Suporte a requisitos reais de produção, como mTLS e buffer de requisições
  • Um controller que honrasse o modelo de recursos dedicados do Gateway API, em vez de uma proliferação de anotações

Aqui está uma comparação entre os diferentes controllers Gateway API que consideramos em relação a esses critérios:

Controller Status CNCF Notas da nossa avaliação
Envoy Gateway Projeto CNCF Rodado pela CNCF em sua própria infraestrutura. Atendeu nossos requisitos de mTLS e buffer de requisições. Selecionado.
Traefik Fora da CNCF Não se alinhou tão fortemente com nossa direção de longo prazo.
NGINX Gateway Fabric Fora da CNCF Fora do filtro CNCF que estabelecemos.
Istio Projeto CNCF Conjunto abrangente de funcionalidades.
Higress Projeto CNCF Não listado nos resultados oficiais de conformidade do Kubernetes Gateway API.

Nota sobre dados de contribuidores do Envoy Gateway: o projeto Envoy mais amplo tem uma ampla contribuição; o próprio Envoy Gateway atualmente tem 4 contribuidores, com uma organização responsável por 51% ou mais.

O Envoy Gateway saiu na frente. É um projeto CNCF, a especificação é sólida, e a CNCF o executa em sua própria infraestrutura. Atender aos critérios de graduação da CNCF nos dá confiança nas decisões de ferramentas, e saber que as pessoas por trás desses critérios fizeram a mesma seleção internamente é um sinal ainda mais forte.

Testando o Envoy Gateway primeiro em nossa própria infraestrutura

Com o Envoy Gateway selecionado, a próxima questão era onde testá-lo.

Antes de levar qualquer coisa para nosso cliente, queríamos validar em nossa própria infraestrutura primeiro. Construímos um componente Envoy Gateway conectado a um AWS Network Load Balancer e contamos com dois outros componentes do Foundation nos quais já confiávamos: ExternalDNS para gerenciamento automático de registros DNS e cert-manager para TLS. Os pontos de integração eram familiares, o que nos permitiu focar na migração em si.

Como campo de prova, escolhemos uma instância interna do Goldilocks. O Goldilocks é uma ferramenta para gerar recomendações de resource requests e limits para workloads Kubernetes. Seu Helm chart suporta tanto Ingress quanto Gateway API, o que significava que poderíamos executar ambos simultaneamente sem nenhuma alteração no nível da aplicação. É também uma aplicação bastante simples de implantar, o que a torna um candidato ideal para trabalhar a mecânica de uma migração sem variáveis desnecessárias.

Para monitorar o corte, escrevemos um script shell simples que consultava nosso endpoint e direcionava a saída tanto para o terminal quanto para um arquivo de log usando tee. Isso nos deu um registro claro e revisável dos códigos de status HTTP durante todo o processo.

Uma migração Gateway API bem-sucedida, mas não boa o suficiente

O primeiro corte ocorreu sem problemas. Levantamos o HTTPRoute do Envoy Gateway junto com o Ingress existente, observamos nosso script de polling e fizemos a troca. Houve uma etapa manual que vale notar: como gerenciamos nossos clusters com ArgoCD e não usamos a opção prune, o recurso Ingress antigo não foi limpo automaticamente. Tivemos que excluí-lo manualmente através do Argo. Não é grande coisa, apenas algo a considerar em um plano de migração real.

A migração funcionou. No entanto, ao revisar o arquivo de log, identificamos uma janela de downtime durante o corte. O culpado foi o DNS. Ao trocar um registro A, você fica à mercê do TTL definido no registro antigo. Até que esse TTL expire, os clientes ainda estão resolvendo para o endereço antigo. Se o Ingress antigo não existe mais, essas requisições não vão para lugar nenhum. Conseguimos fazer a migração, mas não de forma limpa.

Isso não era bom o suficiente para nós. A maioria dos guias de migração termina aqui. O nosso não, porque mover tráfego é uma coisa; movê-lo sem derrubar uma única requisição é outra. Voltamos à prancheta.

Alcançando zero downtime com weighted DNS

O problema com nossa primeira tentativa foi o sequenciamento. Removemos o Ingress antigo antes do DNS ter chance de se atualizar. O tráfego precisava de um destino estável durante a janela de transição.

Optamos por registros DNS ponderados via ExternalDNS e AWS Route 53. Em vez de um corte abrupto, poderíamos executar o Ingress e o HTTPRoute simultaneamente, cada um registrado com o mesmo hostname, mas com pesos diferentes. Definindo o peso do Ingress como alto e o peso do HTTPRoute como zero, o HTTPRoute ficaria ativo mas sem receber tráfego, efetivamente inerte. Assim que tivéssemos certeza de que tudo estava configurado corretamente, poderíamos trocar os pesos e mover o tráfego sem criar ou excluir nenhum registro DNS.

O ExternalDNS observa uma anotação tanto nos recursos Ingress quanto HTTPRoute e gerencia os registros ponderados no Route 53 automaticamente. A anotação do Ingress ficou assim:

external-dns.alpha.kubernetes.io/aws-weight: "100"

E o HTTPRoute começou com:

external-dns.alpha.kubernetes.io/aws-weight: "0"

Ambos os recursos apontavam para o mesmo hostname, ambos registrados no Route 53, com todo o tráfego fluindo através do Ingress. Quando estávamos prontos para o corte, trocamos os pesos:

# Ingress
external-dns.alpha.kubernetes.io/aws-weight: "0"
# HTTPRoute
external-dns.alpha.kubernetes.io/aws-weight: "100"

O script de polling continuou rodando durante o corte e confirmou o resultado: nenhuma requisição falhou. Também verificamos que as requisições agora estavam passando pelo novo load balancer da AWS e roteando através do HTTPRoute conforme esperado.

Vale notar: essa abordagem também torna o rollback trivial. Se algo parecer errado após o corte, basta inverter os pesos. Não há registro DNS a recriar, nenhum recurso a reimplantar, e o rollback propaga da mesma forma que o corte. O padrão de weighted DNS também é independente de controller. Usamos para o Envoy Gateway, mas a mesma abordagem funciona com qualquer implementação de Gateway API que o ExternalDNS possa gerenciar.

O que a produção realmente revelou

O teste com Goldilocks nos deu confiança na mecânica. A produção, então, introduziu requisitos que não haviam surgido em nossos testes internos.

Alguns desses requisitos já havíamos resolvido no Kind. Nosso cliente precisava de mTLS com encaminhamento de certificado, além de limitador de buffer de requisições. Resolvemos ambos de forma limpa durante nossa fase de avaliação, o que nos deu confiança real para o engagement com o cliente. Ter trabalhado neles no Kind significou que não os descobrimos durante a migração.

O que não antecipamos totalmente foi o modelo multi-namespace. Nosso cliente executa vários namespaces por cluster para representar diferentes ambientes, que é um padrão comum e razoável. O desafio é que, antes do Gateway API 1.5, os hostnames precisam ser definidos tanto no nível do Gateway quanto no nível do HTTPRoute.

O Gateway é um recurso de nível de cluster, tipicamente de propriedade do time de plataforma ou infraestrutura. O HTTPRoute é um recurso de nível de aplicação, de propriedade dos desenvolvedores.

Flow chart image of the Gateway

Fonte: Gateway API

Quando a configuração de hostname vive no nível do Gateway, você está puxando uma preocupação de aplicação para a infraestrutura. Isso quebra exatamente a separação de responsabilidades que o Gateway API deveria entregar.

Em escala de namespace único é tolerável. Em múltiplos namespaces representando ambientes distintos, torna-se um problema real. O Gateway API 1.5 é uma versão estável que aborda isso diretamente. O problema é que os controllers ainda não alcançaram a especificação.

Para onde o Gateway API vai daqui?

O Gateway API 1.5 lançou o ListenerSet, um novo tipo de recurso projetado para permitir essa separação de responsabilidades. O ListenerSet permite que os times de aplicação definam seus próprios listeners sem tocar no recurso Gateway em si. Preocupações de infraestrutura ficam no nível de infraestrutura, e preocupações de aplicação ficam no nível de aplicação.

O Envoy Gateway implementou o ListenerSet, atualmente em release candidate, e a versão estável é esperada em poucos dias. Já estamos testando o RC atrás de uma feature flag e prontos para avançar rapidamente assim que lançar. Temos confiança no time do Envoy Gateway e estamos ansiosos para implementar isso assim que a versão estável chegar, pois melhorará significativamente como separamos preocupações de infraestrutura e aplicação entre namespaces.

Recap: Fazendo sua migração de Ingress NGINX do jeito certo

A pressão para migrar do Ingress NGINX é real, e cada mês que um time permanece em um controller EOL construído sobre uma API congelada é um mês mais longe de para onde o Kubernetes está indo. A urgência é válida, mas não pode ser desculpa para cortar caminhos ou implementar uma solução subótima.

O que nos propusemos a fazer foi migrar um cliente com zero downtime, sobre uma base sólida de longo prazo, sem atalhos. A abordagem de weighted DNS nos deu o corte limpo que queríamos. O Envoy Gateway nos deu um controller no qual podemos confiar. A lição mais difícil é que a diferença entre uma migração que move tráfego e uma que não derruba nenhuma requisição se resume aos passos que a maioria dos guias pula.

Se o Ingress NGINX está no seu stack, agora é um bom momento para começar a planejar. As decisões de infraestrutura que você toma hoje moldam o quão limpo você conseguirá adotar o que vem a seguir.

Perguntas Frequentes

  • Por que migrar do Ingress NGINX para Gateway API agora?
    O Ingress NGINX controller está em fim de vida: não receberá mais patches de segurança nem novas funcionalidades. A API Ingress está congelada. O Gateway API é o substituto oficial, mais expressivo e com suporte da comunidade CNCF. Quanto antes planejar a migração, menor o risco de ficar exposto a vulnerabilidades não corrigidas.

  • O weighted DNS funciona com qualquer provedor de nuvem?
    Sim, o padrão weighted DNS é independente de controller. No case foi usado AWS Route 53 com ExternalDNS, mas a mesma lógica se aplica a outros provedores que suportam pesos em registros DNS (como Azure DNS ou Google Cloud DNS). O importante é ter um DNS gerenciado que permita pesos e um ExternalDNS configurado para gerenciar os registros automaticamente.

  • Qual o principal desafio com múltiplos namespaces no Gateway API?
    Antes da versão 1.5, os hostnames precisavam ser definidos tanto no Gateway (nível de infraestrutura) quanto no HTTPRoute (nível de aplicação), quebrando a separação de responsabilidades. Em clusters com múltiplos namespaces por ambiente, isso se torna um problema operacional. O ListenerSet (Gateway API 1.5) resolve, mas os controllers precisam implementá-lo — o Envoy Gateway já tem em release candidate.

  • É possível fazer rollback rápido com weighted DNS?
    Sim, essa é uma das maiores vantagens. Basta inverter os pesos entre o Ingress antigo e o novo HTTPRoute. Como não há criação ou remoção de registros DNS, o rollback é imediato e segue o mesmo mecanismo de propagação da migração. Isso reduz drasticamente o risco operacional em comparação com um hard cutover.

  • O Envoy Gateway é a única opção viável para Gateway API?
    Não, mas foi a escolha do case após avaliação criteriosa. Outros controllers como Istio (CNCF) ou Traefik (fora da CNCF) também são opções. O Envoy Gateway se destacou por ser um projeto CNCF, rodar na própria infraestrutura da fundação, atender requisitos de mTLS e buffering, e ter boa paridade de anotações com o Ingress NGINX. A recomendação é avaliar com base nas suas necessidades específicas.


Artigo originalmente publicado por Andrew Katsikas, Pelotech em Cloud Native Computing Foundation.

Gostou? Compartilhe:
Precisa de ajuda?Fale com nossos especialistas 👋
Avatar Walcew - Headset