Aumente a confiabilidade do Kubernetes com sondas de integridade

KubernetesBeginner
Pratique Agora

Introdução

Running significa que existe um processo de contêiner; isso não comprova que a aplicação esteja pronta para receber tráfego ou seja capaz de se recuperar. As sondas do Kubernetes transformam verificações de integridade específicas da aplicação em comportamentos do controlador e do Service.

Você adicionará os três tipos de sondas, observará a remoção de um Pod dos endpoints do Service quando ele deixar de estar pronto, corrigirá a situação e adicionará um mecanismo de desligamento controlado.

Mantenha as responsabilidades separadas: uma sonda de inicialização protege aplicações com inicialização lenta, uma sonda de prontidão controla se um Pod recebe tráfego do Service e uma sonda de atividade determina quando o kubelet deve reiniciar um contêiner travado. Usar uma única sonda para todas as finalidades costuma causar reinicializações evitáveis ou o envio prematuro de tráfego.

Implantar uma aplicação com sondas

Nesta etapa, implante o NGINX com verificações de inicialização, prontidão e atividade contra / na porta nomeada http.

O manifesto é mais extenso porque as três sondas ficam dentro da especificação do contêiner. Antes de aplicá-lo, leia-o nesta ordem: Deployment e rótulos, contêiner e porta nomeada, depois inicialização, prontidão e atividade. As três verificações usam o mesmo endpoint HTTP nesta demonstração, mas as consequências de uma falha são diferentes.

Alguns mapeamentos e listas de uma única linha usam o “estilo compacto” do YAML, como {app: healthy-web}. As chaves representam um mapeamento e os colchetes representam uma lista; eles equivalem às formas YAML com várias linhas. Use esse formato aqui apenas quando o valor completo continuar fácil de ler.

O limite da sonda de inicialização permite até 24 segundos (12 × 2) antes que o Kubernetes considere a inicialização malsucedida. Depois que a inicialização for concluída, a prontidão será verificada a cada três segundos e a atividade, a cada cinco segundos.

cd /home/labex/project/application-health
cat <<'EOF' > healthy-web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: healthy-web
  namespace: practice-health
spec:
  replicas: 2
  selector:
    matchLabels: {app: healthy-web}
  template:
    metadata:
      labels: {app: healthy-web}
    spec:
      containers:
        - name: web
          image: nginx:1.27-alpine
          imagePullPolicy: IfNotPresent
          ports:
            - {name: http, containerPort: 80, protocol: TCP}
          startupProbe:
            httpGet: {path: /, port: http}
            failureThreshold: 12
            periodSeconds: 2
          readinessProbe:
            httpGet: {path: /, port: http}
            periodSeconds: 3
          livenessProbe:
            httpGet: {path: /, port: http}
            periodSeconds: 5
            failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
  name: healthy-web
  namespace: practice-health
spec:
  selector: {app: healthy-web}
  ports:
    - {name: http, port: 80, targetPort: http, protocol: TCP}
EOF

Crie o namespace e aplique o manifesto. rollout status aguarda que as duas réplicas atendam aos requisitos de prontidão, não apenas que seus processos sejam iniciados:

kubectl create namespace practice-health
kubectl apply -f healthy-web.yaml
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s

A sonda de inicialização protege uma inicialização lenta; a sonda de prontidão controla a elegibilidade para receber tráfego; e a sonda de atividade permite reiniciar o contêiner após uma falha irrecuperável.

Se o rollout for concluído, você já saberá mais do que apenas “os contêineres estão em execução”: os dois Pods responderam corretamente à solicitação de prontidão.

A linha final do rollout é a primeira evidência de integridade:

Waiting for deployment "healthy-web" rollout to finish: 0 of 2 updated replicas are available...
Waiting for deployment "healthy-web" rollout to finish: 1 of 2 updated replicas are available...
deployment "healthy-web" successfully rolled out

As linhas intermediárias são normais. O Kubernetes informa o progresso à medida que cada Pod passa pelas verificações de inicialização e prontidão; somente a última linha significa que as duas réplicas estão disponíveis.

Conectar a prontidão aos endpoints do Service

Nesta etapa, inspecione a condição de prontidão e os EndpointSlices.

Comece com a tabela familiar de Pods e depois restrinja a saída à condição de prontidão e à contagem de reinicializações. A tabela personalizada facilita distinguir “não está pronto” de “está reiniciando”:

kubectl get pods -n practice-health -l app=healthy-web
kubectl get pods -n practice-health -l app=healthy-web -o custom-columns='POD:.metadata.name,READY_CONDITION:.status.conditions[?(@.type=="Ready")].status,RESTARTS:.status.containerStatuses[0].restartCount'
kubectl get endpointslices -n practice-health -l kubernetes.io/service-name=healthy-web -o jsonpath='{range .items[].endpoints[*]}{.addresses[0]} ready={.conditions.ready}{"\n"}{end}'

Os dois endereços devem exibir ready=true. Uma falha de prontidão mantém o contêiner em execução, mas remove seu endereço do roteamento normal do Service.

Os EndpointSlices fazem a ligação entre o seletor de um Service e os endereços reais dos Pods. O Service continua existindo quando um Pod não está pronto; é a condição do endpoint que impede o envio de tráfego normal para esse Pod.

Uma evidência típica de funcionamento correto é:

POD                            READY_CONDITION   RESTARTS
healthy-web-...                True              0
healthy-web-...                True              0
10.244.104.11 ready=true
10.244.104.12 ready=true

Os nomes dos Pods e os endereços IP serão diferentes. O que importa é a relação: dois Pods com Ready=True correspondem a dois endpoints com ready=true, e nenhum dos contêineres foi reiniciado.

Observar uma falha de prontidão

Nesta etapa, altere deliberadamente apenas o caminho da sonda de prontidão para uma URL inexistente.

Esta é uma falha controlada. O JSON Patch substitui um único campo aninhado e, portanto, mantém inalteradas as sondas de inicialização e atividade. O NGINX retorna HTTP 404 para /not-ready, e a sonda de prontidão interpreta isso como uma falha.

kubectl patch deployment healthy-web -n practice-health --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/not-ready"}]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=20s || true
kubectl get pods -n practice-health -l app=healthy-web
kubectl get endpointslices -n practice-health -l kubernetes.io/service-name=healthy-web -o jsonpath='{range .items[].endpoints[*]}{.addresses[0]} ready={.conditions.ready}{"\n"}{end}'

|| true marca o tempo limite do rollout como esperado, permitindo que o shell continue. Os novos contêineres são executados, mas a verificação de prontidão falha e o Deployment não consegue ficar disponível.

O rollout testado produziu um estado semelhante a este:

error: timed out waiting for the condition
NAME                           READY   STATUS    RESTARTS
healthy-web-old-...            1/1     Running   0
healthy-web-old-...            1/1     Running   0
healthy-web-new-...            0/1     Running   0
...
10.244.104.17 ready=false

O novo Pod está Running, mas não está pronto, e não foi reiniciado. Os dois Pods antigos e prontos permanecem porque as atualizações graduais do Deployment protegem a disponibilidade: o Kubernetes não substituirá mais réplicas saudáveis enquanto a nova versão não conseguir ficar pronta. Portanto, o tempo limite do rollout comprova tanto o comportamento da prontidão quanto o mecanismo de segurança do controlador. Na saída do EndpointSlice, o novo endereço aparece como ready=false, então o roteamento normal do Service continua usando apenas os endpoints saudáveis.

Corrigir a prontidão e verificar o tráfego

Nesta etapa, restaure o caminho da sonda de prontidão e aguarde o retorno dos endpoints do Service.

Use o patch inverso ao anterior para que o experimento altere apenas uma variável. Agora, o rollout status será concluído com sucesso porque os Pods substitutos respondem a / com HTTP 200.

kubectl run health-client cria um Pod temporário independente para fazer a solicitação. --image escolhe o BusyBox, --image-pull-policy=IfNotPresent reutiliza a imagem armazenada em cache e --restart=Never solicita um Pod, em vez de um controlador. --rm -i mantém o comando conectado e exclui o Pod quando ele termina; o segundo -- separa as opções do kubectl do comando wget executado dentro do contêiner.

Em wget -qO-, -q oculta as mensagens de progresso e -O- grava o corpo da resposta na saída padrão; o pipe envia esse corpo para head, que exibe apenas as primeiras linhas. A URL usa o nome do Service, healthy-web, que o DNS do cluster resolve dentro do namespace.

kubectl patch deployment healthy-web -n practice-health --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/"}]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
kubectl run health-client -n practice-health --image=busybox:1.36 --image-pull-policy=IfNotPresent --restart=Never --rm -i -- wget -qO- http://healthy-web | head

O cliente temporário usa o DNS do cluster. Limitar o HTML exibido mantém a evidência legível, enquanto uma resposta bem-sucedida comprova que a prontidão e o roteamento do Service estão alinhados.

A resposta começa com um HTML reconhecível do NGINX:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

Ver HTML após um rollout bem-sucedido comprova o caminho completo: o DNS resolveu o Service, o Service selecionou um endpoint pronto e o NGINX processou a solicitação.

Excluir o Pod temporário depois evita deixar clientes de diagnóstico abandonados no namespace.

Adicionar uma finalização controlada

Nesta etapa, adicione um hook preStop e um período de tolerância para encerramento. O hook atrasa brevemente o desligamento, permitindo que a remoção do endpoint se propague antes que o processo seja encerrado.

Leia separadamente as duas operações do JSON Patch. A primeira adiciona um período de 20 segundos para a finalização do Pod. A segunda adiciona um hook de espera de cinco segundos ao contêiner de índice 0. O Kubernetes inicia o hook quando a finalização começa, enquanto o período de tolerância restante continua sendo contado.

kubectl patch deployment healthy-web -n practice-health --type=json -p='[
  {"op":"add","path":"/spec/template/spec/terminationGracePeriodSeconds","value":20},
  {"op":"add","path":"/spec/template/spec/containers/0/lifecycle","value":{"preStop":{"sleep":{"seconds":5}}}}
]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
kubectl get deployment healthy-web -n practice-health -o jsonpath='grace={.spec.template.spec.terminationGracePeriodSeconds}s preStop={.spec.template.spec.containers[0].lifecycle.preStop.sleep.seconds}s{"\n"}'

O patch JSON altera apenas os dois campos nomeados, portanto as sondas e as demais configurações do contêiner permanecem intactas. A finalização controlada complementa a prontidão: uma controla a admissão de tráfego, enquanto a outra dá tempo para que o Kubernetes remova o Pod do tráfego antes que o encerramento seja concluído.

A saída final do JSONPath deve ser grace=20s preStop=5s. O hook de cinco segundos cabe dentro do período de 20 segundos, deixando tempo para que o NGINX seja encerrado normalmente.

O rollout pode pausar com mensagens como 1 old replicas are pending termination, pois o hook preStop adiciona deliberadamente cinco segundos ao encerramento. A saída final continua sendo:

deployment "healthy-web" successfully rolled out
grace=20s preStop=5s

Resumo

Você conectou as verificações de inicialização, prontidão e atividade ao comportamento do Kubernetes, observou a remoção de endpoints quando um Pod perdeu a prontidão sem interromper os contêineres, restaurou o tráfego e adicionou uma finalização controlada. Esses mecanismos tornam a integridade acionável, em vez de apenas visível.