Prática guiada — Instalar, verificar e consertar seu cluster

Prática guiada17 min
Duração
45 a 60 min
Módulo
1/8
Você vai construir
o kit do curso instalado e validado por labo.ps1 prerequis, a API whoami da biblioteca em cinco réplicas atrás de um Service LoadBalancer em http://localhost:8080, um Pod propositalmente quebrado (tag de imagem inexistente) que você diagnostica e depois repara, um -n esquecido que você reconhece pela mensagem, e um frontend nginx exposto em NodePort
Entregável
a saída de labo.ps1 etat (ou labo.sh etat) mostrando premiers-pas 7/7 pods Running e os dois Services, além de suas respostas às três perguntas da etapa 9

Resumo: os comandos do laboratório

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)

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 curso

Em um segundo terminal (a manter visível durante todo o laboratório):

powershell
kubectl get pods -n premiers-pas -w

Os arquivos deste laboratório estão em 01-installer-kubernetes-avec-docker-desktop\pratique\. Depois, verifique no navegador: http://localhost:8080 (API whoami).

powershell
.\labo.ps1 etat          # entregável: premiers-pas 7/7 pods Running e dois Services
.\labo.ps1 nettoyer      # ao final, remove os namespaces do curso

Se 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

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 curso

Em um segundo terminal (a manter visível durante todo o laboratório):

bash
kubectl get pods -n premiers-pas -w

Os arquivos deste laboratório estão em 01-installer-kubernetes-avec-docker-desktop/pratique/. Depois, verifique no navegador: http://localhost:8080 (API whoami).

bash
./labo.sh etat           # entregável: premiers-pas 7/7 pods Running e dois Services
./labo.sh nettoyer       # ao final, remove os namespaces do curso

Objetivo

Você 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 prerequisetat 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.

Antes de começar

  • Ter lido as quatro lições do módulo: 01, 02, 03 e 04.
  • Docker Desktop iniciado, Kubernetes ativado em modo kubeadm (lição 02). O namespace premiers-pas não deve mais existir: kubectl get namespace premiers-pas responde NotFound (senão kubectl delete namespace premiers-pas primeiro).
  • O kit do curso clonado: 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í.
  • Dois terminais lado a lado: o primeiro para agir, o segundo para observar (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 …).
  • No Windows, se a lição 03 mostrou um aviso de versão, comece cada terminal com $env:Path = "C:\Program Files\Docker\Docker\resources\bin;" + $env:Path.

Etapa 1 — Instalar o kit e executar prerequis

O 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.

powershell
.\labo.ps1 prerequis

Ponto 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):

text
== 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 etat

Leia 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.

Etapa 2 — Ler etat em um cluster vazio

Antes de criar qualquer coisa, observe o que o kit exibe quando não há nada. Você vai comparar com a etapa 9.

powershell
.\labo.ps1 etat

Ponto 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):

text
== 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.

Etapa 3 — Implantar a API e expô-la, observando

No segundo terminal, inicie o monitoramento e não o encerre até a limpeza:

bash
kubectl get pods -n premiers-pas -w

Nada acontece enquanto o namespace não existir, isso é normal. No primeiro terminal:

bash
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-pas

Ponto de verificação: três confirmações e depois um Service com EXTERNAL-IP localhost.

text
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   5s

Enquanto isso, o segundo terminal exibiu o nascimento do primeiro Pod, linha por linha:

text
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          2s

Pending (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:

bash
curl -s http://localhost:8080
powershell
curl.exe -s http://localhost:8080
text
Hostname: api-9bfb55fc6-mh24c
IP: 10.1.2.255
RemoteAddr: 192.168.65.3:49552
Host: localhost:8080

Trecho: 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.

Etapa 4 — Cinco réplicas, dez requisições

Observe bem o segundo terminal no momento em que você digitar isto:

bash
kubectl scale deployment api --replicas=5 -n premiers-pas
kubectl get pods -n premiers-pas -o wide

Ponto de verificação: cinco Pods Running, cada um com seu próprio IP 10.1.x.x, todos no nó docker-desktop.

text
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):

text
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          3s

O kit contém um pequeno script que envia dez requisições e conta os Pods distintos que responderam:

powershell
.\01-installer-kubernetes-avec-docker-desktop\pratique\repartition.ps1
bash
./01-installer-kubernetes-avec-docker-desktop/pratique/repartition.sh

Saída real (PowerShell; o script exibe primeiro os dez Hostname recebidos, um por linha, depois esta contagem):

text
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  x2

O 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.

Etapa 5 — Quebrar: pedir uma imagem que não existe

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.

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: 80
bash
kubectl apply -f 01-installer-kubernetes-avec-docker-desktop/pratique/01-pod-image-inexistante.yaml
kubectl get pods -n premiers-pas

Ponto de verificação: o Pod existe mas não está Running, e os outros cinco não são afetados.

text
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):

text
api-cassee            0/1     ErrImagePull        0          2s
api-cassee            0/1     ImagePullBackOff    0          17s
api-cassee            0/1     ErrImagePull        0          29s

get te diz que está falhando; só describe te diz por quê. Vá direto à seção Events, no final:

bash
kubectl describe pod api-cassee -n premiers-pas

Saída real (só o final é mostrado):

text
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: ImagePullBackOff

Tudo 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:

bash
kubectl logs api-cassee -n premiers-pas
text
Error from server (BadRequest): container "whoami" in pod "api-cassee" is waiting to start: image can't be pulled

RESTARTS permanece em 0 pelo mesmo motivo. Escreva em uma frase a causa da falha: você vai precisar dela na etapa 9.

Etapa 6 — Quebrar: esquecer o -n

Você quer verificar o Pod quebrado, e digita o comando sem o namespace:

bash
kubectl get pod api-cassee
kubectl logs api-cassee

Ponto de verificação: dois erros NotFound, embora o Pod exista de fato.

text
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:

bash
kubectl get pod api-cassee -n premiers-pas
text
NAME         READY   STATUS         RESTARTS   AGE
api-cassee   0/1     ErrImagePull   0          36s

Guarde 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.

Etapa 7 — Reparar com kubectl set image

Um 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:

bash
kubectl set image pod/api-cassee whoami=traefik/whoami:v1.10 -n premiers-pas
kubectl get pod api-cassee -n premiers-pas

whoami é o nome do contêiner no YAML (containers[0].name), não o da imagem.

Ponto de verificação:

text
pod/api-cassee image updated
NAME         READY   STATUS    RESTARTS   AGE
api-cassee   1/1     Running   0          44s

O segundo terminal mostrou a transição ImagePullBackOffRunning 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.

Etapa 8 — Mão na massa: expor o frontend em NodePort

A 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.

Correção

bash
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-pas
text
deployment.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   10s

80: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.

bash
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.)

text
<title>Welcome to nginx!</title>
HTTP/1.1 200 OK
Server: nginx/1.27.5
Date: Fri, 11 Sep 2026 03:47:01 GMT

Server: 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).

Etapa 9 — O entregável: etat e três perguntas

Execute de novo o script do kit e compare com a etapa 2.

powershell
.\labo.ps1 etat

Saída real (mesmos cortes da etapa 2):

text
== 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                 17s

Sete Pods: cinco api, api-cassee reparado, um frontend. Copie essa saída para seu entregável, depois responda em uma ou duas frases cada:

  1. Por que kubectl logs api-cassee -n premiers-pas não conseguiu exibir nada na etapa 5, e onde você encontrou a causa?
  2. Na etapa 6, a mensagem dizia not found in namespace "default": o Pod tinha desaparecido? O que você deveria ter digitado?
  3. 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?

Verificação final

  • .\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.
  • Você consegue dizer, sem reler, o que significam ErrImagePull, ImagePullBackOff e not found in namespace "default".

Limpeza

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:

powershell
.\labo.ps1 nettoyer
text
== 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 -oui

Para este módulo, remova apenas premiers-pas, manualmente, e mantenha o segundo terminal aberto para ver os sete Pods passarem para Terminating:

bash
kubectl delete namespace premiers-pas
kubectl get namespace premiers-pas
text
namespace "premiers-pas" deleted
Error from server (NotFound): namespaces "premiers-pas" not found

Ctrl+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.

Se algo travar

  • labo.ps1 recusa iniciar: l'exécution de scripts est désactivée sur ce systèmeSet-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 directorychmod +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.

O que você aprendeu

  • 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.
  • Sob 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.

Desafio bônus (opcional)

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.