Como Realizar Atualizações Rolling com Zero Downtime em Deployments Kubernetes

Configure atualizações rolling no Kubernetes com probes de prontidão, maxSurge, maxUnavailable e desligamento gracioso.

Como Realizar Atualizações Rolling com Zero Downtime em Deployments Kubernetes

As atualizações rolling do Kubernetes podem substituir Pods sem uma interrupção visível, mas apenas se seu Deployment e sua aplicação concordarem sobre quando um Pod está pronto e como ele deve ser desligado. A estratégia padrão ajuda, mas não o salva de probes de prontidão ruins, versões incompatíveis ou requisições em andamento perdidas.

Alcançar verdadeiro zero downtime, no entanto, requer mais do que apenas a configuração padrão do Kubernetes. Exige coordenação cuidadosa entre o manifesto do Deployment, os endpoints de saúde da aplicação e o processo de terminação graciosa. Este guia fornece uma abordagem abrangente e passo a passo para configurar Deployments Kubernetes para garantir que as atualizações de aplicação sejam contínuas e invisíveis para o usuário final.

Este guia aborda probes de prontidão, maxSurge, maxUnavailable e terminação graciosa para que seu serviço mantenha a capacidade durante uma implantação.

Pré-requisitos para Zero Downtime

Antes de configurar o manifesto Kubernetes, a aplicação subjacente deve aderir a certos princípios para suportar implantações com zero downtime:

  1. Compatibilidade Retroativa da Aplicação: Durante o curto período em que as versões antiga e nova da aplicação estão rodando simultaneamente, elas devem ser compatíveis com recursos compartilhados (bancos de dados, filas, caches).
  2. Idempotência: Operações que podem ser tratadas por ambas as versões devem ser repetíveis sem efeitos colaterais negativos.
  3. Terminação Graciosa: A aplicação deve ser programada para reconhecer o sinal SIGTERM enviado pelo Kubernetes e parar graciosamente de aceitar novas conexões enquanto finaliza as requisições em andamento antes de sair.

Entendendo a Estratégia de Atualização Rolling do Kubernetes

Os Deployments do Kubernetes usam por padrão a estratégia RollingUpdate. Este método garante que a versão antiga da aplicação não seja completamente removida antes que a nova versão esteja operacional, gerenciando a transição usando dois parâmetros principais:

Parâmetro Descrição Impacto no Zero Downtime
maxSurge Define o número máximo de Pods que podem ser criados acima do número desejado de réplicas. Pode ser um número absoluto ou uma porcentagem (padrão: 25%). Controla a velocidade da implantação e garante que a capacidade aumente temporariamente.
maxUnavailable Define o número máximo de Pods que podem ficar indisponíveis durante a atualização. Pode ser um número absoluto ou uma porcentagem (padrão: 25%). Crucial para zero downtime. Definir isso como 0% significa que nenhum Pod em serviço é terminado até que os novos Pods estejam totalmente Prontos.

Estratégia Recomendada para Zero Downtime

Para a mais alta disponibilidade, a melhor configuração é frequentemente garantir zero perda de capacidade durante o downtime:

  • maxUnavailable: 0 (Garante que a capacidade nunca caia).
  • maxSurge: 1 ou 25% (Permite que a capacidade exceda brevemente o alvo, garantindo que um novo Pod esteja pronto antes que um antigo seja morto).

Passo 1: Implementando Probes de Prontidão

A Probe de Prontidão é o mecanismo mais importante para garantir atualizações com zero downtime. O Kubernetes depende desta probe para determinar se um novo Pod está pronto para receber tráfego de usuário e se um Pod antigo ainda está ativamente servindo tráfego.

Probes de Vitalidade vs. Prontidão

  • Probe de Vitalidade: Diz ao Kubernetes se o contêiner está saudável e funcional. Se falhar, o contêiner é reiniciado.
  • Probe de Prontidão: Diz ao Kubernetes se o contêiner está pronto para servir requisições. Se falhar, o Pod é removido dos endpoints do Service associado, desviando o tráfego dele até que se torne pronto.

Para atualizações rolling, a Probe de Prontidão é usada para controlar a transição. O Kubernetes não prosseguirá para terminar um Pod antigo até que um Pod recém-criado passe com sucesso em sua verificação de prontidão.

# trecho de deployment.yaml
spec:
  containers:
  - name: my-app
    image: myregistry/my-app:v2.0
    ports:
    - containerPort: 8080
    
    # --- Configuração da Probe de Prontidão ---
    readinessProbe:
      httpGet:
        path: /health/ready
        port: 8080
      initialDelaySeconds: 15  # Tempo de espera antes da primeira tentativa de probe
      periodSeconds: 5         # Com que frequência realizar a verificação
      timeoutSeconds: 3
      failureThreshold: 3      # Número de falhas consecutivas para marcar o Pod como não pronto

    # --- Configuração da Probe de Vitalidade (Verificação de Saúde Padrão) ---
    livenessProbe:
      httpGet:
        path: /health/live
        port: 8080
      initialDelaySeconds: 60
      periodSeconds: 10

Dica: Certifique-se de que o endpoint /health/ready da sua aplicação retorne um código de sucesso (HTTP 200-299) apenas quando a inicialização, conexões de banco de dados e outras dependências externas estiverem totalmente operacionais.

Passo 2: Configurando a Estratégia do Deployment

Para exigir verdadeiro zero downtime, configuramos explicitamente a estratégia de atualização rolling para evitar qualquer queda no número de réplicas disponíveis.

Nesta configuração, o Kubernetes primeiro criará um novo Pod (maxSurge: 1). Assim que o novo Pod passar em sua probe de prontidão, só então o Kubernetes terminará um Pod antigo. Como maxUnavailable é 0, a capacidade do serviço nunca cai abaixo da contagem alvo de réplicas.

# trecho de deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-web-deployment
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      # Garante que a capacidade nunca caia abaixo da contagem desejada de réplicas (4)
      maxUnavailable: 0 
      # Permite que um Pod extra seja criado durante a implantação
      maxSurge: 1 
  template:
    # ... especificação do contêiner ...

Passo 3: Garantindo a Terminação Graciosa

Mesmo com probes de prontidão robustas, se a aplicação desligar instantaneamente ao receber o sinal de terminação, corre o risco de perder requisições em andamento.

O Kubernetes segue uma sequência de terminação específica:

  1. O Pod é marcado como Terminando.
  2. O Pod é removido dos endpoints do Service (o tráfego para de ser roteado para ele).
  3. O hook pré-parada (se definido) é executado.
  4. O contêiner recebe o sinal SIGTERM.
  5. O Kubernetes aguarda pela duração definida em terminationGracePeriodSeconds (padrão: 30 segundos).
  6. Se o contêiner ainda estiver rodando, ele recebe um SIGKILL inegociável.

Para garantir um desligamento gracioso, a aplicação deve lidar com SIGTERM, e o terminationGracePeriodSeconds deve ser longo o suficiente para a aplicação finalizar as requisições existentes.

# trecho de deployment.yaml, dentro do spec do template do Pod
spec:
  terminationGracePeriodSeconds: 45 # Configuração a nível de Pod
  containers:
  - name: my-app
    image: myregistry/my-app:v2.0
    lifecycle:
      preStop:
        exec:
          # Dá tempo para atualizações de endpoint e balanceadores de carga externos drenarem.
          command: ["/bin/sh", "-c", "sleep 10"]

Melhor Prática: Sua aplicação deve parar de aceitar novo trabalho quando receber SIGTERM, depois finalizar as requisições em andamento antes de sair. Um terminationGracePeriodSeconds ligeiramente mais longo, como 45 ou 60 segundos, ajuda a prevenir mortes forçadas para requisições mais lentas.

Passo 4: Realizando e Monitorando a Atualização

Assim que seu manifesto do Deployment incluir a estratégia otimizada e probes robustas, realizar a atualização é direto.

  1. Atualize a Tag da Imagem: Modifique seu manifesto de deployment para refletir a nova versão da imagem (ex: v2.0 para v2.1).

  2. Aplique a Configuração:

    kubectl apply -f deployment.yaml
    

    Alternativamente, você pode corrigir a imagem diretamente:

    kubectl set image deployment/my-web-deployment my-app=myregistry/my-app:v2.1
    
  3. Monitore o Status da Implantação: Observe o Kubernetes progredir através dos estágios, verificando se o número de Pods prontos nunca cai abaixo da contagem desejada.

    kubectl rollout status deployment/my-web-deployment
    
  4. Verifique a Disponibilidade dos Pods: Observe o status dos Pods para confirmar que os Pods antigos (v2.0) são terminados graciosamente apenas após os novos Pods (v2.1) estarem totalmente prontos.

    kubectl get pods -l app=my-web-deployment -w
    

Considerações Avançadas

Usando Orçamentos de Interrupção de Pods (PDBs)

Enquanto uma estratégia de deployment gerencia implantações, um Orçamento de Interrupção de Pods (PDB) limita interrupções voluntárias, como drenagem de nós e algumas operações de manutenção do cluster. Ele não impede todas as falhas não planejadas, mas dá ao Kubernetes e ferramentas de automação um alvo mínimo de disponibilidade a ser respeitado.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-app-pdb
spec:
  minAvailable: 75%  # Garante que pelo menos 75% das réplicas estejam sempre disponíveis
  selector:
    matchLabels:
      app: my-web-deployment

A Importância do Atraso Inicial

Se sua aplicação leva tempo para aquecer, ajuste initialDelaySeconds, periodSeconds e failureThreshold para que a prontidão reflita o comportamento real de inicialização. Uma probe de prontidão com falha mantém o Pod fora dos endpoints do Service; uma probe de vitalidade com falha é o que pode reiniciar o contêiner e criar um loop de falhas.

Implante com Segurança

Alcançar verdadeiras atualizações rolling com zero downtime no Kubernetes é uma combinação de configuração robusta da plataforma e desenvolvimento disciplinado de aplicações. Ao aproveitar corretamente as Probes de Prontidão para sinalizar o status operacional, ajustar a estratégia do Deployment (maxUnavailable: 0) para manter a capacidade e implementar manipuladores de terminação graciosa, você pode garantir que as atualizações de aplicação sejam realizadas de forma confiável sem interromper o serviço aos seus usuários. Sempre teste seu processo de atualização minuciosamente em um ambiente de staging para validar o período de graça de terminação e a lógica da probe.