Upgrade do NKP 2.18 → 2.19 na práticaParte 2: o upgrade

Publicado
Leitura
12 min

Read in English

Diagrama das fases do upgrade do NKP 2.18 para 2.19, com as fases 4 e 5 em destaque

Resumo: a janela durou cerca de 1h42 e terminou com os dois clusters em NKP 2.19, Kubernetes 1.36.2 e Rocky Linux 9.8. O management subiu pelo CLI em duas etapas, o workload cluster subiu pela nova interface em 21 minutos, e no caminho apareceram dois incidentes: um que estava escondido desde a criação do cluster e um aviso que se resolveu sozinho. Esta é a parte 2 de uma série de três.

Onde paramos

Na parte 1, passei pelas fases 0 a 3: assessment, criação do workload cluster ainda na 2.18, downloads e backup. Com tudo verde, a janela começou com este cenário:

ItemAntes do upgrade
Management clusterNKP 2.18, Kubernetes 1.35.2, Rocky Linux 9.7, 3 control planes e 5 workers
Workload clusterNKP 2.18, Kubernetes 1.35.2, 3 control planes e 3 workers
CLI nkp na bastion2.19.0
Imagem de nó no Prism CentralRocky Linux 9.8, Kubernetes 1.36.2
FaseO que éPost
0 a 3PreparaçãoParte 1
4Upgrade do management (Kommander, depois o cluster)Parte 2 (este)
5Upgrade do workload cluster pela interfaceParte 2 (este)
6Pós-upgrade: validação, lições e o que a 2.19 trazParte 3

Como acompanhei a janela

Três terminais ficaram abertos o tempo todo, e a janela inteira foi gravada:

  1. Um loop contra o podinfo, a aplicação de teste da parte 1, que roda no workload cluster: uma requisição por segundo, imprimindo o horário e o código HTTP.
  2. Os comandos do upgrade, dentro de uma sessão tmux na bastion. Se a conexão SSH cair, o upgrade continua rodando.
  3. Um watch nos nós ou nas máquinas do Cluster API, para ver a troca acontecendo.
Terminal window
# Terminal 1 (notebook): o termômetro
while true; do printf "%s " "$(date +%T)"; curl -s -m 2 -o /dev/null -w "%{http_code}\n" http://<ip-do-podinfo>/ || echo FALHA; sleep 1; done
# Terminal 2 (bastion): tudo dentro do tmux
ssh -t usuario@<bastion> 'tmux new -A -s upgrade'

Fase 4.1: Kommander

Objetivo: atualizar a camada de gerenciamento do NKP (Kommander e as aplicações da plataforma) para a 2.19.

Vindo da 2.18, essa etapa é pelo CLI. Antes de começar, conferi a versão do CLI e a saúde dos nós, e mandei a saída para um log:

Terminal window
export KUBECONFIG=~/.kube/mgmt-admin.conf
nkp version
kubectl get nodes
nkp upgrade kommander 2>&1 | tee ~/fase4-1-kommander-$(date +%Y%m%d-%H%M).log

O comando faz quatro verificações antes de mexer em qualquer coisa:

✓ Checking for deprecated GitRepository-based catalog apps
✓ Ensuring that new app dependencies introduced by the upgrade are enabled
✓ Ensuring upgrading conditions are met
✓ Checking docker.io/nutanix credentials are present for the fleet
• Upgrading ManagementPlane nkp-platform...

A última é a credencial do registry da Nutanix, aquela que eu achava que estava configurada e não estava (parte 1, Fase 3). Desta vez, passou.

Depois das verificações, o upgrade tem duas etapas, e as duas ficam em silêncio por vários minutos:

EtapaDuração
Upgrading ManagementPlane17min31s
Ensuring platform Kommander applications are upgradedcerca de 12 min
Total da Fase 4.1cerca de 30 min

Silêncio não é travamento

Nas duas etapas, o terminal fica parado na mesma linha por muitos minutos. Se quiser ver o progresso, abra outro terminal e acompanhe as HelmReleases: watch -n 10 'kubectl get helmreleases -A | grep -v True'.

O critério para seguir

Com o comando concluído, conferi se alguma aplicação da plataforma tinha ficado fora do ar:

Terminal window
kubectl get helmreleases -A --no-headers | grep -v "True" || echo "TODAS AS APPS READY"

A resposta foi TODAS AS APPS READY. Só com isso eu passei para a próxima fase.

O login do Kommander cai

Depois da Fase 4.1, o token de autenticação muda e a interface volta para a tela de login. É esperado e está no Upgrade Guide. Kubeconfigs baseados em token também deixam de funcionar; o de admin, por certificado, continua. Se for gravar a janela, pause antes de logar de novo.


Fase 4.2: o management cluster

Objetivo: levar o próprio management cluster para Kubernetes 1.36.2 e Rocky Linux 9.8, sem derrubar nada.

O comando precisa do nome e do namespace do cluster no Cluster API, e da imagem nova. Em vez de digitar os dois primeiros, peguei direto do recurso do NKP:

Terminal window
export MANAGEMENT_CLUSTER_NAME=$(kubectl get -n kommander nkpcluster \
-l 'kommander.d2iq.io/host=true' -o jsonpath='{.items[0].status.capiClusterRef.name}')
export MANAGEMENT_CLUSTER_NAMESPACE=$(kubectl get -n kommander nkpcluster \
-l 'kommander.d2iq.io/host=true' -o jsonpath='{.items[0].status.capiClusterRef.namespace}')
export VM_IMAGE_NAME=nkp-rocky-9.8-release-cis-1.36.2-<build>.qcow2
echo "Cluster: $MANAGEMENT_CLUSTER_NAME | Namespace: $MANAGEMENT_CLUSTER_NAMESPACE | Imagem: $VM_IMAGE_NAME"
nkp upgrade cluster nutanix \
--cluster-name ${MANAGEMENT_CLUSTER_NAME} \
--namespace ${MANAGEMENT_CLUSTER_NAMESPACE} \
--vm-image ${VM_IMAGE_NAME}

O echo antes do upgrade é proposital: se alguma variável sair vazia, dá tempo de cancelar. E o nome da imagem precisa ser exatamente o que está no Prism Central (por isso a regra de não renomear, na parte 1).

✓ Waiting for ClusterClasses to be ready
✓ Running preflight checks
nkpcluster.clusters.nkp.nutanix.com/<management> upgraded
• Upgrading the cluster...

O rolling upgrade, nó por nó

A partir daqui, quem trabalha é o Cluster API. Para cada nó, ele:

  1. cria uma VM nova, com a imagem nova;
  2. espera o nó novo ficar Ready no Kubernetes;
  3. isola o nó antigo (SchedulingDisabled) e drena os pods dele;
  4. só então remove a VM antiga.

Por alguns minutos, o cluster tem um nó a mais (9 em vez de 8). Ele nunca fica com menos capacidade do que tinha. Num terminal com watch -n 15 kubectl get nodes, dá para ver as versões mudando linha por linha:

NAME STATUS ROLES AGE VERSION
<mgmt>-cp-novo-1 Ready control-plane 28s v1.36.2
<mgmt>-cp-antigo-1 Ready,SchedulingDisabled control-plane 29d v1.35.2
<mgmt>-cp-antigo-2 Ready control-plane 29d v1.35.2
...
EtapaDuração
3 control planescerca de 12 min (cerca de 4 min por nó)
5 workerscerca de 14 min
Total da Fase 4.2cerca de 26 min

A folga de CPU fez diferença

Na parte 1, adicionei um quinto worker ao management porque os requests de CPU estavam em 87%. É nessa fase que isso importa: cada worker drenado precisa caber nos outros quatro, além do nó extra temporário.

Fase 4.3: validação do management

Antes de tocar no workload, validei o management e conferi que o workload continuava intacto:

Terminal window
kubectl get nodes -o wide # 8 nós Ready, v1.36.2, Rocky Linux 9.8
kubectl get helmreleases -A --no-headers | grep -v "True" || echo "TODAS AS APPS READY"
kubectl -n kube-system get cm coredns -o yaml | grep -c "lab.exemplo.local" # zona interna preservada
kubectl --kubeconfig ~/.kube/servicos.conf get nodes # workload ainda em v1.35.2

Tudo certo, incluindo um detalhe que eu temia: a zona de DNS interna que adicionei ao CoreDNS (o troubleshooting da Fase 1) sobreviveu ao upgrade nos dois clusters.

Depois de logar de novo, o dashboard mostrava v2.19.0 e Kubernetes v1.36.2, e dois menus novos em Administration: Metal Host Inventory e Upgrades. O segundo é o da Fase 5.

Resultado da Fase 4

Management cluster em NKP 2.19, Kubernetes 1.36.2 e Rocky Linux 9.8, em cerca de 56 minutos e sem nenhum erro. Workload cluster intacto, ainda na 2.18.


Antes da Fase 5: o problema que estava escondido

A interface da 2.19 só libera o upgrade de um workload cluster quando todas as aplicações dele estão saudáveis. E a parte 1 deixou uma pendência: nos pré-checks da Fase 3, o OpenCost do workload aparecia com falha de instalação. Era hora de entender.

opencost False Helm install failed ... timeout waiting for: [Deployment/<namespace>/opencost status: 'InProgress']

Fui puxando o fio:

  1. O OpenCost estava em CrashLoopBackOff, com mais de 200 reinícios. O log era claro: Failed to create Prometheus data source ... connection refused. Ele estava bem; quem não respondia era o Prometheus.
  2. O HelmRelease do Prometheus aparecia como True, mas o pod do Prometheus nem iniciava. Os eventos mostravam o motivo:
    Error: configmap "cluster-info-configmap" not found
  3. Esse ConfigMap existia no management, mas não no workload. Ele tem um único campo, CLUSTER_ID, e o Kommander deveria ter entregue ao vincular o cluster ao workspace.

A minha hipótese: efeito colateral do primeiro dia do cluster, quando o Flux não conseguia alcançar o Kommander por falta de DNS (parte 1, Fase 1). Quase tudo foi entregue depois da correção, mas esse item, não.

A correção, em quatro passos:

Terminal window
# 1. Confirmar o padrão no management: CLUSTER_ID = UID do namespace kube-system
kubectl -n kommander get cm cluster-info-configmap -o jsonpath='{.data.CLUSTER_ID}'
kubectl get ns kube-system -o jsonpath='{.metadata.uid}'
# 2. Criar o ConfigMap no workload, com o UID do kube-system dele
export WK=~/.kube/servicos.conf; export NS=<namespace-do-workspace>
UID_WK=$(kubectl --kubeconfig $WK get ns kube-system -o jsonpath='{.metadata.uid}')
kubectl --kubeconfig $WK -n $NS create configmap cluster-info-configmap --from-literal=CLUSTER_ID="$UID_WK"
# 3. Reiniciar o Prometheus e o OpenCost
kubectl --kubeconfig $WK -n $NS delete pod prometheus-kube-prometheus-stack-prometheus-0
kubectl --kubeconfig $WK -n $NS rollout restart deploy/opencost
# 4. Fazer o Flux tentar de novo (ele desiste depois de esgotar as tentativas)
kubectl --kubeconfig $WK -n $NS patch helmrelease opencost --type merge -p '{"spec":{"suspend":true}}'
kubectl --kubeconfig $WK -n $NS patch helmrelease opencost --type merge -p '{"spec":{"suspend":false}}'
T=$(date +%s)
kubectl --kubeconfig $WK -n $NS annotate helmrelease opencost \
reconcile.fluxcd.io/requestedAt="$T" reconcile.fluxcd.io/forceAt="$T" reconcile.fluxcd.io/resetAt="$T" --overwrite

Em cerca de 8 minutos: Prometheus 3/3 Running, OpenCost 2/2 Running com zero reinícios, HelmRelease True e, no Kommander, o aviso “You have cluster(s) that are ready for an upgrade”.

Duas lições deste incidente

HelmRelease True não garante que a aplicação está rodando: o chart do Prometheus estava instalado, mas o pod nem iniciava. Olhe pods e eventos. E o Flux não tenta de novo depois de esgotar as tentativas: mesmo com a causa corrigida, o status só volta com um reset explícito.


Fase 5: o workload cluster pela interface

Objetivo: testar a grande novidade da 2.19, o upgrade de workload cluster pela interface do Kommander.

Em Administration → Upgrades, a tela mostra os dois clusters lado a lado. O management já aparecia como Newest Version; o workload, como Ready, com o caminho calculado: de v2.18.0 para v2.19.0 e de v1.35.2 para v1.36.2.

O link Upgrade na linha do cluster abre um assistente de dois passos.

Passo 1, Select Version: a versão atual e a recomendada (NKP v2.19.0 & Kubernetes v1.36.2), com as notas da versão embutidas na própria tela.

Assistente de upgrade, passo Select Version: versão atual v2.18.0 e Kubernetes v1.35.2, nova versão NKP v2.19.0 e Kubernetes v1.36.2 recomendada, e as notas da versão 2.19

Passo 2, Upgrade Configuration: a imagem atual e a nova para cada grupo de nós (control plane e cada node pool). Foi aqui que escolhi a mesma imagem Rocky Linux 9.8 usada no management.

Assistente de upgrade, passo Upgrade Configuration: imagem atual Rocky 9.7 e nova imagem Rocky 9.8 para o control plane e para o worker node pool, e o aviso sobre os pre-checks

Repare no aviso dos Pre-Checks: pela interface, as verificações sempre rodam. Para pular alguma, só pelo CLI, e só se você tiver certeza de que a falha é falsa.

O Cluster Upgrade Tracker

Um clique em Upgrade e o link Upgrading na linha do cluster abre o Cluster Upgrade Tracker, outra novidade da 2.19. Ele mostra três etapas, nesta ordem:

  1. Validating Inputs
  2. Upgrading Platform Applications
  3. Upgrading Kubernetes and Cluster Infrastructure

A ordem é o oposto do que fiz no management: aqui, as aplicações sobem primeiro e o Kubernetes depois. Só na terceira etapa as máquinas começam a ser trocadas.

O aviso amarelo

No início da terceira etapa, o Tracker mostrou isto:

Cluster Upgrade Tracker com as duas primeiras etapas concluídas e a terceira em andamento, com o aviso de erro "failed to call extension handlers for hook BeforeClusterUpgrade ... deadline exceeded"

Antes de trocar as máquinas, o Cluster API chama um gancho, o BeforeClusterUpgrade, respondido por uma extensão do NKP que roda no management. A chamada demorou demais.

Não cliquei em nada. A etapa continuava girando, sem marca de falha, e o Cluster API tenta de novo sozinho. Uma causa provável: os pods dessa extensão tinham acabado de mudar de nó no upgrade do management. Em cerca de um minuto, o aviso sumiu e o Tracker passou a mostrar KubeadmControlPlane upgrading to v1.36.2.

Aviso amarelo com a etapa girando não é falha

Espere alguns minutos antes de agir. E não use o botão Change Image para “destravar”: a imagem estava certa, o problema era o gancho.

A troca dos nós, vista de baixo

Os mesmos passos do management, agora nos seis nós do workload. Com três terminais, dá para ver o que a interface resume:

  • Máquinas no Cluster API: kubectl get machines -n <namespace> mostra cada máquina nova passando de Provisioning para Running, e as antigas em Deleting.
  • Nós no Kubernetes: o nó novo aparece NotReady por um ou dois minutos (a rede de pods ainda subindo) e depois Ready em v1.36.2.
  • A proteção do control plane: nas condições do cluster, aparece “Scaling down 4 → 3 blocked, waiting for control plane components to become healthy”. O NKP só remove um control plane antigo depois que o novo está saudável.

Concluído

Cluster Upgrade Tracker com as três etapas concluídas: Validating Inputs em 4 segundos, Upgrading Platform Applications em 6min22s e Upgrading Kubernetes and Cluster Infrastructure em 14min35s, total 21min05s

Etapa do TrackerDuração
Validating Inputs4 s
Upgrading Platform Applications6min22s
Upgrading Kubernetes and Cluster Infrastructure14min35s
Total da Fase 521min05s

Na tela de Upgrades, os dois clusters na mesma versão:

Tela Administration, Upgrades: o management cluster como Newest Version e o workload cluster como Completed, os dois em NKP v2.19.0 e Kubernetes v1.36.2

A validação final no workload repetiu a da Fase 4.3: seis nós Ready em v1.36.2 com Rocky Linux 9.8, todas as aplicações prontas, a zona de DNS interna preservada e o cluster-info-configmap que eu tinha criado continuava lá.

Resultado da Fase 5

Workload cluster em NKP 2.19 pela interface, em 21 minutos, com um aviso transitório e nenhuma intervenção durante o upgrade.


E o podinfo?

O termômetro respondeu 200 em todos os momentos que registrei. Durante a Fase 4, isso era esperado: o podinfo roda no workload, e o upgrade do management não deveria afetá-lo. A Fase 5 era o teste de verdade, porque os nós trocados eram os dele. Nos prints e na gravação, ele seguiu respondendo; com 3 réplicas e um PodDisruptionBudget, o drain de um worker nunca derruba todas. Eu não fiz uma contagem formal de falhas segundo a segundo, então não vou cravar “zero downtime absoluto”: fica como próximo teste.

Resumo da janela

EtapaHorárioDuraçãoResultado
4.1 Kommander (CLI)17:56 → 18:26cerca de 30 min✓
4.2 Management cluster, 8 nós (CLI)18:28 → 18:54cerca de 26 min✓
4.3 Validação18:55—✓
Correção do OpenCost no workload19:00 → 19:08cerca de 8 min✓
5 Workload cluster, 6 nós (interface)19:15 → 19:3721 min✓
Janela inteira17:56 → 19:38cerca de 1h422 clusters em NKP 2.19
IncidenteCausaO que resolveu
OpenCost em CrashLoopBackOff no workloadcluster-info-configmap nunca entregue, desde o primeiro dia sem DNSCriar o ConfigMap, reiniciar Prometheus e OpenCost, resetar o HelmRelease no Flux
Aviso deadline exceeded no gancho BeforeClusterUpgradeExtensão do NKP demorando a responder logo após o upgrade do managementNada: o Cluster API tentou de novo e seguiu em cerca de um minuto

Em breve: Parte 3

A Parte 3 fecha a série com a Fase 6: o pós-upgrade, as lições aprendidas consolidadas e o que a 2.19 traz de novo além do upgrade pela interface.

Checklist para a janela

  • Sessão tmux na bastion para os comandos longos
  • Um termômetro rodando contra uma aplicação real
  • nkp upgrade kommander e validação das HelmReleases antes de seguir
  • Plano para o login do Kommander que cai depois da Fase 4.1
  • Variáveis do nkp upgrade cluster conferidas com um echo antes
  • Nome exato da imagem do Prism Central
  • Folga de CPU para o nó extra do rolling upgrade
  • Customizações (CoreDNS) conferidas depois do upgrade
  • Todas as aplicações do workload saudáveis antes de usar a interface
  • Paciência com avisos amarelos no Tracker

Referências