Kit do curso: https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr
Você clona o kit em uma pasta lab2, verifica que o Docker Desktop e o Kubernetes estão prontos, lê o estado de um cluster vazio, depois implanta a API whoami da biblioteca em cinco réplicas atrás de um Service LoadBalancer em http://localhost:8080. Em seguida, você quebra duas coisas de propósito (uma imagem que não existe, um -n esquecido), repara, e expõe o frontend em NodePort. Mantenha um segundo terminal aberto com kubectl get pods -n premiers-pas -w para observar os Pods se movendo. O detalhe de cada etapa vem a seguir; comece executando este bloco.
Windows (PowerShell)
git clone https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr.git lab2
cd lab2
ls # explorar o conteúdo: labo.ps1, labo.sh, uma pasta por módulo (01-… a 08-…)
$env:Path = "C:\Program Files\Docker\Docker\resources\bin;" + $env:Path # kubectl do Docker Desktop com prioridade
.\labo.ps1 prerequis
.\labo.ps1 etat # cluster vazio: esperado docker-desktop Ready, nenhum namespace do cursoEm um segundo terminal (a manter visível durante todo o laboratório):
kubectl get pods -n premiers-pas -wOs arquivos deste laboratório estão em 01-installer-kubernetes-avec-docker-desktop\pratique\. Depois, verifique no navegador: http://localhost:8080 (API whoami).
.\labo.ps1 etat # entregável: premiers-pas 7/7 pods Running e dois Services
.\labo.ps1 nettoyer # ao final, remove os namespaces do cursoSe o PowerShell recusar .\labo.ps1 (« a execução de scripts está desativada »): Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, responda S, execute de novo.
Linux, macOS, WSL 2, Git Bash
git clone https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr.git lab2
cd lab2
ls # explorar o conteúdo: labo.sh, labo.ps1, uma pasta por módulo (01-… a 08-…)
./labo.sh prerequis
./labo.sh etat # cluster vazio: esperado docker-desktop Ready, nenhum namespace do cursoEm um segundo terminal (a manter visível durante todo o laboratório):
kubectl get pods -n premiers-pas -wOs arquivos deste laboratório estão em 01-installer-kubernetes-avec-docker-desktop/pratique/. Depois, verifique no navegador: http://localhost:8080 (API whoami).
./labo.sh etat # entregável: premiers-pas 7/7 pods Running e dois Services
./labo.sh nettoyer # ao final, remove os namespaces do cursoVocê entra na equipe que constrói o catálogo da biblioteca da plataforma de cursos. Sua chefe entrega um notebook: « Amanhã de manhã, quero que o Kubernetes esteja rodando na sua máquina, que você me prove com um único comando que tudo está em ordem, e que você não me chame na primeira vez que um Pod se recusar a iniciar. » Você vai então instalar o kit do curso, executar sua lista de verificação, implantar a API e observá-la se multiplicar em tempo real, depois quebrar duas coisas de propósito: pedir uma imagem que não existe, e esquecer o -n. Nos dois casos você vai ler a mensagem, nomear a causa e reparar. Saber distinguir « o cluster recusa o que estou pedindo » de « estou olhando no lugar errado » é o que evita perder uma hora. O percurso: kit e prerequis → etat vazio → api atrás de um LoadBalancer, sob -w → cinco réplicas e dez requisições → duas falhas provocadas → reparo → frontend em NodePort → entregável.
premiers-pas não deve mais existir: kubectl get namespace premiers-pas responde NotFound (senão kubectl delete namespace premiers-pas primeiro).git clone https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr.git, depois posicione-se na sua raiz (aquela que contém labo.ps1 e labo.sh). Todos os comandos abaixo são executados a partir daí.kubectl get pods -n premiers-pas -w). PowerShell ou bash (Git Bash, WSL 2, macOS, Linux): os comandos kubectl são idênticos, e cada comando do kit existe nas duas variantes (.\labo.ps1 … / ./labo.sh …).$env:Path = "C:\Program Files\Docker\Docker\resources\bin;" + $env:Path.prerequisO script do kit faz em um único comando as verificações das lições 02 e 03: docker, daemon, Kubernetes do Docker Desktop, contexto, versões cliente/servidor, helm, memória, portas. Em toda esta prática, a variante bash de um comando do kit se obtém substituindo .\labo.ps1 por ./labo.sh.
.\labo.ps1 prerequisPonto de verificação: um ✔ por linha e a frase final Tout est prêt. Saída real (a versão bash exibe as mesmas linhas, com o caminho /c/Program Files/Docker/Docker/resources/bin/kubectl):
== Prérequis ==
✔ docker : Docker version 29.3.1, build c2be9cc
✔ le démon Docker répond (Docker Engine 29.3.1)
✔ Kubernetes Docker Desktop : running (mode kubeadm, 1 nœud, v1.34.1)
✔ contexte kubectl courant : docker-desktop
✔ kubectl client v1.34.1 (C:\Program Files\Docker\Docker\resources\bin\kubectl.exe)
✔ serveur Kubernetes v1.34.1 (même version mineure que le client)
✔ helm : v4.2.4+g3900f43
✔ mémoire disponible pour Docker : 31 Go
✔ port 8080 : libre
✔ port 8443 : libre
✔ port 30080 : libre
Tout est prêt. Étape suivante : .\labo.ps1 etatLeia a linha kubectl client: ela diz qual binário está respondendo, com seu caminho. É a resposta em uma linha para o problema da lição 03. O Helm só é usado no módulo 7: se estiver faltando, a linha passa para ! (aviso), não para ✘.
Se você vir outra coisa: ✘ kubectl client v1.30.0 trop ancien (minimum 1.33) — mettez C:\Program Files\Docker\Docker\resources\bin en tête du PATH ; where.exe kubectl montre l'ordre actuel (saída real obtida com um kubectl antigo no início do PATH) → lição 03, etapa 3; ✘ le démon Docker ne répond pas → o Docker Desktop não foi iniciado; o PowerShell recusa executar o script → Set-ExecutionPolicy -Scope CurrentUser RemoteSigned uma vez por todas.
etat em um cluster vazioAntes de criar qualquer coisa, observe o que o kit exibe quando não há nada. Você vai comparar com a etapa 9.
.\labo.ps1 etatPonto de verificação: o nó Ready, premiers-pas marcado como absent, nenhum Service exposto. Saída real (as linhas dos outros namespaces do curso, que aparecerão módulo após módulo, foram cortadas):
== Nœuds ==
NAME STATUS ROLES AGE VERSION
docker-desktop Ready control-plane 18d v1.34.1
== Namespaces du cours ==
— premiers-pas absent
== Services exposés (LoadBalancer / NodePort) dans les namespaces du cours ==
— aucun Service exposé pour l'instant (les leçons en créent avec kubectl expose)Três símbolos para conhecer: ✔ tudo funcionando, ! algo merece atenção (Pods que não estão Running, um aviso), — informação neutra (ausente, nada a exibir). O script só observa os namespaces do curso: o que vive em default ou kube-system não lhe interessa.
No segundo terminal, inicie o monitoramento e não o encerre até a limpeza:
kubectl get pods -n premiers-pas -wNada acontece enquanto o namespace não existir, isso é normal. No primeiro terminal:
kubectl create namespace premiers-pas
kubectl create deployment api --image=traefik/whoami:v1.10 --port=80 -n premiers-pas
kubectl expose deployment api --type=LoadBalancer --port=8080 --target-port=80 -n premiers-pas
kubectl get svc -n premiers-pasPonto de verificação: três confirmações e depois um Service com EXTERNAL-IP localhost.
namespace/premiers-pas created
deployment.apps/api created
service/api exposed
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
api LoadBalancer 10.96.92.182 localhost 8080:31427/TCP 5sEnquanto isso, o segundo terminal exibiu o nascimento do primeiro Pod, linha por linha:
NAME READY STATUS RESTARTS AGE
api-9bfb55fc6-mh24c 0/1 Pending 0 0s
api-9bfb55fc6-mh24c 0/1 Pending 0 0s
api-9bfb55fc6-mh24c 0/1 ContainerCreating 0 0s
api-9bfb55fc6-mh24c 1/1 Running 0 2sPending (o scheduler procura um nó), ContainerCreating (o kubelet pede o contêiner ao runtime), Running: dois segundos, porque a imagem já está na máquina desde a lição 04. Verifique se está respondendo:
curl -s http://localhost:8080curl.exe -s http://localhost:8080Hostname: api-9bfb55fc6-mh24c
IP: 10.1.2.255
RemoteAddr: 192.168.65.3:49552
Host: localhost:8080Trecho: Hostname é o nome do Pod que respondeu, IP seu endereço dentro do cluster; RemoteAddr: 192.168.65.3 é o endereço do nó, pelo qual o Docker Desktop faz sua requisição entrar. O mesmo Deployment e o mesmo Service, escritos em YAML completo, estão no kit: 01-installer-kubernetes-avec-docker-desktop/04-api-declaratif.yaml (módulo 2 para lê-los linha a linha).
Se você vir outra coisa: EXTERNAL-IP <pending> que não muda → outro Service LoadBalancer já publica a porta 8080 (kubectl get svc -A), remova-o ou mude de porta; AlreadyExists → o namespace ficou pendente da lição 04, kubectl delete namespace premiers-pas e recomece.
Observe bem o segundo terminal no momento em que você digitar isto:
kubectl scale deployment api --replicas=5 -n premiers-pas
kubectl get pods -n premiers-pas -o widePonto de verificação: cinco Pods Running, cada um com seu próprio IP 10.1.x.x, todos no nó docker-desktop.
deployment.apps/api scaled
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
api-9bfb55fc6-7b7br 1/1 Running 0 15s 10.1.3.1 docker-desktop <none> <none>
api-9bfb55fc6-hp76n 1/1 Running 0 15s 10.1.3.3 docker-desktop <none> <none>
api-9bfb55fc6-mh24c 1/1 Running 0 27s 10.1.2.255 docker-desktop <none> <none>
api-9bfb55fc6-r4d5w 1/1 Running 0 15s 10.1.3.0 docker-desktop <none> <none>
api-9bfb55fc6-rd95r 1/1 Running 0 15s 10.1.3.2 docker-desktop <none> <none>O primeiro Pod (mh24c, 27 s) tem doze segundos mais que os outros quatro: é o da etapa 3.
O segundo terminal mostrou os quatro novos Pods passando juntos por Pending, ContainerCreating e depois Running em três segundos (trecho real):
api-9bfb55fc6-r4d5w 0/1 Pending 0 0s
api-9bfb55fc6-r4d5w 0/1 ContainerCreating 0 0s
api-9bfb55fc6-hp76n 1/1 Running 0 2s
api-9bfb55fc6-r4d5w 1/1 Running 0 3sO kit contém um pequeno script que envia dez requisições e conta os Pods distintos que responderam:
.\01-installer-kubernetes-avec-docker-desktop\pratique\repartition.ps1./01-installer-kubernetes-avec-docker-desktop/pratique/repartition.shSaída real (PowerShell; o script exibe primeiro os dez Hostname recebidos, um por linha, depois esta contagem):
10 requêtes, 5 Pods distincts ont répondu :
api-9bfb55fc6-7b7br x2
api-9bfb55fc6-hp76n x2
api-9bfb55fc6-mh24c x2
api-9bfb55fc6-r4d5w x2
api-9bfb55fc6-rd95r x2O mesmo script executado em bash logo depois deu 10 requêtes, 4 Pods distincts com um Pod atendido quatro vezes: a distribuição é aleatória, não um rodízio. Em dez requisições, ver quatro ou cinco Pods é normal; ver apenas um não é (veja « Se algo travar »). O script não faz nada de mágico: um loop de curl que mantém a linha Hostname:. Abra-o para lê-lo.
O kit contém um Pod cujo tag de imagem está errado. Leia-o antes de aplicá-lo: 01-installer-kubernetes-avec-docker-desktop/pratique/01-pod-image-inexistante.yaml.
apiVersion: v1
kind: Pod
metadata:
name: api-cassee
namespace: premiers-pas
labels:
app: api-cassee
partie: bibliotheque
spec:
containers:
- name: whoami
image: traefik/whoami:v9.99
ports:
- containerPort: 80kubectl apply -f 01-installer-kubernetes-avec-docker-desktop/pratique/01-pod-image-inexistante.yaml
kubectl get pods -n premiers-pasPonto de verificação: o Pod existe mas não está Running, e os outros cinco não são afetados.
pod/api-cassee created
NAME READY STATUS RESTARTS AGE
api-9bfb55fc6-7b7br 1/1 Running 0 27s
api-9bfb55fc6-mh24c 1/1 Running 0 39s
api-cassee 0/1 ErrImagePull 0 4s(Três linhas api-9bfb55fc6-… idênticas cortadas.)
No segundo terminal, o estado oscila: o kubelet tenta de novo, falha, espera cada vez mais (trecho real):
api-cassee 0/1 ErrImagePull 0 2s
api-cassee 0/1 ImagePullBackOff 0 17s
api-cassee 0/1 ErrImagePull 0 29sget te diz que está falhando; só describe te diz por quê. Vá direto à seção Events, no final:
kubectl describe pod api-cassee -n premiers-pasSaída real (só o final é mostrado):
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 34s default-scheduler Successfully assigned premiers-pas/api-cassee to docker-desktop
Normal Pulling 18s (x2 over 34s) kubelet Pulling image "traefik/whoami:v9.99"
Warning Failed 18s (x2 over 34s) kubelet Failed to pull image "traefik/whoami:v9.99": Error response from daemon: failed to resolve reference "docker.io/traefik/whoami:v9.99": docker.io/traefik/whoami:v9.99: not found
Warning Failed 18s (x2 over 34s) kubelet Error: ErrImagePull
Normal BackOff 6s (x2 over 33s) kubelet Back-off pulling image "traefik/whoami:v9.99"
Warning Failed 6s (x2 over 33s) kubelet Error: ImagePullBackOffTudo está aí: quem (kubelet), o quê (Failed to pull image), por quê (docker.io/traefik/whoami:v9.99: not found), e o ritmo (x2 over 34s). Um reflexo para abandonar imediatamente: procurar a causa nos logs. Não há nenhum, o contêiner nunca chegou a iniciar:
kubectl logs api-cassee -n premiers-pasError from server (BadRequest): container "whoami" in pod "api-cassee" is waiting to start: image can't be pulledRESTARTS permanece em 0 pelo mesmo motivo. Escreva em uma frase a causa da falha: você vai precisar dela na etapa 9.
-nVocê quer verificar o Pod quebrado, e digita o comando sem o namespace:
kubectl get pod api-cassee
kubectl logs api-casseePonto de verificação: dois erros NotFound, embora o Pod exista de fato.
Error from server (NotFound): pods "api-cassee" not found
error: error from server (NotFound): pods "api-cassee" not found in namespace "default"A segunda linha diz tudo: in namespace "default". Sem -n, o kubectl procura no namespace do contexto, default, onde não há nenhum api-cassee. O Pod não desapareceu, você está olhando na gaveta errada. Prova:
kubectl get pod api-cassee -n premiers-pasNAME READY STATUS RESTARTS AGE
api-cassee 0/1 ErrImagePull 0 36sGuarde a regra: um NotFound em um objeto que você acabou de criar é quase sempre um -n esquecido. Este curso nunca muda o namespace padrão do contexto, justamente para que você adquira o hábito de informá-lo em cada comando.
kubectl set imageUm Pod cuja imagem não baixa não se repara esperando. Dois remédios: excluí-lo e reaplicar um YAML corrigido, ou trocar a imagem no lugar. Faça o segundo, o mais rápido, observando o segundo terminal:
kubectl set image pod/api-cassee whoami=traefik/whoami:v1.10 -n premiers-pas
kubectl get pod api-cassee -n premiers-paswhoami é o nome do contêiner no YAML (containers[0].name), não o da imagem.
Ponto de verificação:
pod/api-cassee image updated
NAME READY STATUS RESTARTS AGE
api-cassee 1/1 Running 0 44sO segundo terminal mostrou a transição ImagePullBackOff → Running entre o 36º e o 37º segundo de vida do Pod: um segundo, porque traefik/whoami:v1.10 já está na máquina. Note que RESTARTS continua em 0: o Kubernetes não reiniciou um contêiner, ele iniciou um pela primeira vez com a imagem correta.
frontend em NodePortA API está rodando. Adicione agora o segundo componente da biblioteca: um Deployment frontend a partir da imagem nginx:1.27-alpine (porta 80), exposto por um Service do tipo NodePort (não LoadBalancer). Depois, descubra em qual porta da sua máquina ele responde e exiba o título de sua página inicial com curl.
Dicas: três comandos da lição 04 são suficientes, trocando apenas o tipo do Service; a porta escolhida pelo Kubernetes se lê na coluna PORT(S) de kubectl get svc, depois dos dois-pontos; no Docker Desktop, um NodePort responde em localhost:<porta>. O kit contém o YAML equivalente se você preferir: 01-installer-kubernetes-avec-docker-desktop/pratique/02-frontend-nginx-nodeport.yaml.
kubectl create deployment frontend --image=nginx:1.27-alpine --port=80 -n premiers-pas
kubectl expose deployment frontend --type=NodePort --port=80 -n premiers-pas
kubectl get svc frontend -n premiers-pasdeployment.apps/frontend created
service/frontend exposed
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend NodePort 10.111.158.77 <none> 80:32740/TCP 10s80:32740: 80 é a porta do Service dentro do cluster, 32740 a porta do nó sorteada entre 30000 e 32767. Na sua máquina será diferente: leia-a, não copie esta.
curl -s http://localhost:32740 | grep "<title>"
curl -sI http://localhost:32740 | head -3(PowerShell: curl.exe -s … | Select-String "<title>" e curl.exe -sI … | Select-Object -First 3.)
<title>Welcome to nginx!</title>
HTTP/1.1 200 OK
Server: nginx/1.27.5
Date: Fri, 11 Sep 2026 03:47:01 GMTServer: nginx/1.27.5 confirma o tag 1.27-alpine. EXTERNAL-IP <none> é normal para um NodePort: ninguém atribui um endereço a ele, ele abre uma porta no nó, e o Docker Desktop encaminha essa porta para localhost. O módulo 4 compara os três tipos de Service em detalhe. Com o YAML do kit: kubectl apply -f 01-installer-kubernetes-avec-docker-desktop/pratique/02-frontend-nginx-nodeport.yaml cria os dois mesmos objetos (verificado: deployment.apps/frontend created, service/frontend created).
etat e três perguntasExecute de novo o script do kit e compare com a etapa 2.
.\labo.ps1 etatSaída real (mesmos cortes da etapa 2):
== Nœuds ==
NAME STATUS ROLES AGE VERSION
docker-desktop Ready control-plane 18d v1.34.1
== Namespaces du cours ==
✔ premiers-pas 7/7 pods Running
== Services exposés (LoadBalancer / NodePort) dans les namespaces du cours ==
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
premiers-pas api LoadBalancer 10.96.92.182 localhost 8080:31427/TCP 91s
premiers-pas frontend NodePort 10.111.158.77 <none> 80:32740/TCP 17sSete Pods: cinco api, api-cassee reparado, um frontend. Copie essa saída para seu entregável, depois responda em uma ou duas frases cada:
kubectl logs api-cassee -n premiers-pas não conseguiu exibir nada na etapa 5, e onde você encontrou a causa?not found in namespace "default": o Pod tinha desaparecido? O que você deveria ter digitado?api exibe EXTERNAL-IP localhost e frontend <none>: qual tipo de Service cada um tem, e por qual porta da sua máquina você responde a cada um?.\labo.ps1 prerequis (ou ./labo.sh prerequis) termina com Tout est prêt.kubectl get all -n premiers-pas mostra deployment.apps/api 5/5 5 5, deployment.apps/frontend 1/1 1 1, pod/api-cassee 1/1 Running, um replicaset.apps/api-9bfb55fc6 5 5 5 e os dois Services (saída real: sete Pods, dois Services, dois Deployments, dois ReplicaSets).curl -s http://localhost:8080 | grep Hostname (PowerShell: curl.exe -s http://localhost:8080 | Select-String Hostname) retorna um Hostname: api-… que muda de uma chamada para outra.curl -sI http://localhost:<porta do frontend> retorna HTTP/1.1 200 OK e Server: nginx/1.27.5.ErrImagePull, ImagePullBackOff e not found in namespace "default".O kit sabe remover todos os namespaces do curso de uma vez, mas se recusa a fazer isso sem confirmação. Veja primeiro o que ele faria:
.\labo.ps1 nettoyer== Nettoyage des namespaces du cours ==
Namespaces qui seront supprimés (avec tout ce qu'ils contiennent) : premiers-pas
Jamais touchés : default, kube-system, kube-public, kube-node-lease.
! rien n'a été supprimé. Pour confirmer : .\labo.ps1 nettoyer -ouiPara este módulo, remova apenas premiers-pas, manualmente, e mantenha o segundo terminal aberto para ver os sete Pods passarem para Terminating:
kubectl delete namespace premiers-pas
kubectl get namespace premiers-pasnamespace "premiers-pas" deleted
Error from server (NotFound): namespaces "premiers-pas" not foundCtrl+C no segundo terminal. http://localhost:8080 não responde mais (curl falha com o código 7, conexão recusada, ou 52, resposta vazia). .\labo.ps1 nettoyer -oui / ./labo.sh nettoyer --oui vai servir quando você quiser zerar todo o curso: ele nunca toca em default nem em kube-system.
labo.ps1 recusa iniciar: l'exécution de scripts est désactivée sur ce système → Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, feche e reabra o PowerShell. O script está em UTF-8 com BOM e funciona tanto no PowerShell 5.1 quanto no PowerShell 7.
./labo.sh: Permission denied ou /usr/bin/env: 'bash\r': No such file or directory → chmod +x labo.sh para o primeiro; para o segundo, o Git converteu as quebras de linha para CRLF na extração: sed -i 's/\r$//' labo.sh (ou git config core.autocrlf false e clone de novo).
prerequis exibe ✘ port 8080 déjà occupé par <programme> (PID …) — arrêtez-le ou choisissez un autre port dans les leçons → uma aplicação local já está escutando (outro servidor de desenvolvimento, um port-forward esquecido); o script nomeia o processo. Encerre-o, ou escolha --port=8081 na etapa 3 e adapte as URLs. Se a porta estiver ocupada por um Service Kubernetes do curso, a linha permanece ✔: utilisé par un Service du cours (LoadBalancer ou NodePort).
EXTERNAL-IP <pending> na etapa 3 → no Docker Desktop, a porta pedida já está publicada por outro Service LoadBalancer (verificado: um segundo Service na porta 8080 permanece <pending> enquanto o primeiro existir). kubectl get svc -A mostra qual. No kind ou minikube, <pending> é o estado normal: use kubectl port-forward.
repartition retorna dez vezes o mesmo Hostname → ou o scale ainda não terminou (kubectl get pods -n premiers-pas: espere cinco Running), ou o Service tem apenas um único endpoint: kubectl get endpointslice -n premiers-pas deve listar cinco IPs (10.1.2.255,10.1.3.3,10.1.3.0 + 2 more... na máquina do curso).
Depois de set image, api-cassee continua em ImagePullBackOff → você digitou um nome de contêiner ou um tag errado: kubectl describe pod api-cassee -n premiers-pas mostra a imagem realmente pedida em Containers: whoami: Image:. O nome à esquerda do = é o do contêiner (whoami), não o da imagem.
labo.ps1 prerequis / labo.sh prerequis verifica em um único comando o que as lições 02 e 03 faziam manualmente, e nomeia o binário kubectl que responde.labo.ps1 etat resume nó, Pods por namespace do curso e Services expostos: é a foto a anexar quando você pedir ajuda.kubectl get pods -w, um Pod passa por Pending, ContainerCreating, Running; cinco réplicas nascem em três segundos quando a imagem já está presente.ErrImagePull e depois ImagePullBackOff = a imagem não baixa; a causa está em kubectl describe, seção Events, nunca em kubectl logs (o contêiner não iniciou).not found in namespace "default" = -n esquecido; o objeto ainda está lá, em outro namespace.kubectl set image pod/<nome> <contêiner>=<imagem> corrige uma imagem no lugar; um Service NodePort responde em localhost:<30000-32767> com o Docker Desktop, um LoadBalancer na porta que você escolheu.Sem remover o namespace, provoque uma terceira falha e repare-a: mude o Deployment api para um tag inexistente com kubectl set image deployment/api whoami=traefik/whoami:v9.99 -n premiers-pas, depois observe no segundo terminal que o Kubernetes não destrói os Pods que funcionam: na máquina do curso, ele criou três Pods api-78d7f59657-… em ErrImagePull, removeu apenas um antigo, manteve quatro api-9bfb55fc6-… em Running, e parou aí (kubectl rollout status deployment/api -n premiers-pas permanece em Waiting for deployment "api" rollout to finish: 3 out of 5 new replicas have been updated...). http://localhost:8080 continua respondendo. Repare com kubectl rollout undo deployment/api -n premiers-pas (deployment.apps/api rolled back) e verifique que os Pods quebrados desaparecem e que cinco api-9bfb55fc6-… estão rodando. Você acabou de ver o rolling update, suas travas de segurança (maxSurge, maxUnavailable) e o rollback, temas do módulo 3.