Skip to main content

Desligamento gracioso

Quando o Kubernetes tira um pod do ar, ele manda SIGTERM e espera. O que o seu serviço faz entre o sinal e o fim do processo decide se uma requisição em curso é concluída ou se o cliente vê um erro.

O orçamento é do chart:

terminationGracePeriodSeconds: 30

Trinta segundos. Se o processo não terminar até lá, o kubelet mata.

A sequência​

  1. /ready passa a negar. O pod sai do balanceamento antes de fechar qualquer coisa — nenhuma requisição nova chega.
  2. Espera de drenagem: 3 s em produção, 0 em desenvolvimento. Nesse tempo o serviço continua atendendo normalmente: é a janela para o balanceador perceber que o pod saiu.
  3. O listener fecha e as requisições em curso terminam.
  4. O trabalho de fundo é cancelado e esperado.
  5. Os beans são destruídos, o pool por último.
  6. O processo termina com 0.

Os prazos, e o que acontece quando estouram​

etapaprazo
espera de drenagem3 s (produção)
listeners + workersum orçamento único de 20 s, contado do fim da espera
fechamento do contexto depois do prazo+3 s
terminationGracePeriodSeconds30 s

Listeners e workers dividem os 20 s. Se o orçamento estourar, o registro é nns: desligamento incompleto (com nns: workers não terminaram no prazo em error) e o código de saída é 1 — sinal para quem observa que aquele pod não saiu limpo, mesmo tendo saído.

Se o contexto não acabar de fechar até 3 s depois do prazo, o processo é encerrado com 1. Todo o caminho cabe nos 30 s do terminationGracePeriodSeconds.

O que isso vale na prática​

Medido no laboratório: uma requisição presa em curso terminou com 201, o pod saiu em 15,8 s e houve 0 erro em 528 mil requisições durante o desligamento. É o comportamento que você deve esperar de um NNS que não inventa o próprio desligamento.

As duas armadilhas​

Não use @Scheduled para trabalho periódico. Ele não conhece o papel da release e para de disparar no início do desligamento — mas uma rodada em curso não é tratada como parte do desligamento. O caminho do SDK é a tarefa periódica da release ativa, que o runtime cancela e espera junto com o resto:

@NnsPeriodic(name = "items-sync", every = "1h")
void run(Ctx ctx) {
// só roda na release ATIVA; o runtime cuida do agendamento e do desligamento
}

Um bean que encerra algo por conta própria precisa se declarar. Se o seu serviço fecha um recurso próprio (uma conexão de longa duração, um cliente externo) e não consegue terminar dentro do orçamento, marque:

NnsShutdown.incomplete("cliente externo não fechou no prazo");

Sem isso, o desligamento é declarado limpo mesmo tendo deixado algo pela metade — e o código de saída mente para o operador.

O que o seu handler pode contar​

Um handler em curso recebe um Ctx com o prazo da rota. O desligamento não cancela esse prazo antes da hora: a requisição já aceita termina. O que muda é o /ready, que a partir do SIGTERM responde negando para que nada novo entre.

Operação longa já respondida como 202 não depende do desligamento: ela é executada pelo runner da Action, e a release ativa adota e fecha o que ficou pendente.

Como conferir​

./nnconsolew chart check --values nns/helm/testdata/injected-values.yaml --namespace <ns> \
--runtime jvm --runtime-user 1002 nns/helm/chart

O chart check confere o terminationGracePeriodSeconds do manifesto. Uma execução local que exercite o desligamento é papel dos testes do serviço (./mvnw verify), que sobem a aplicação e mandam o sinal.