Pratique guidée — Installer, vérifier et réparer son cluster

Pratique guidée16 min
Durée
45 à 60 min
Module
1/8
Tu vas construire
le kit du cours installé et validé par labo.ps1 prerequis, l'API whoami de la bibliothèque en cinq réplicas derrière un Service LoadBalancer sur http://localhost:8080, un Pod volontairement cassé (tag d'image inexistant) que tu diagnostiques puis répares, un -n oublié que tu reconnais au message, et un frontend nginx exposé en NodePort
Livrable
la sortie de labo.ps1 etat (ou labo.sh etat) montrant premiers-pas 7/7 pods Running et les deux Services, plus tes réponses aux trois questions de l'étape 9

En bref : les commandes du lab

Kit du cours : https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr

Tu clones le kit dans un dossier lab2, tu vérifies que Docker Desktop et Kubernetes sont prêts, tu lis l'état d'un cluster vide, puis tu déploies l'API whoami de la bibliothèque en cinq réplicas derrière un Service LoadBalancer sur http://localhost:8080. Ensuite tu casses deux choses exprès (une image qui n'existe pas, un -n oublié), tu répares, et tu exposes le frontend en NodePort. Garde un second terminal ouvert avec kubectl get pods -n premiers-pas -w pour regarder les Pods bouger. Le détail de chaque étape suit ; commence par exécuter ce bloc.

Windows (PowerShell)

powershell
git clone https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr.git lab2
cd lab2
ls                       # explorer le contenu : labo.ps1, labo.sh, un dossier par module (01-… à 08-…)
$env:Path = "C:\Program Files\Docker\Docker\resources\bin;" + $env:Path   # kubectl de Docker Desktop en priorité
.\labo.ps1 prerequis
.\labo.ps1 etat          # cluster vide : attendu docker-desktop Ready, aucun namespace du cours

Dans un second terminal (à garder visible tout le lab) :

powershell
kubectl get pods -n premiers-pas -w

Les fichiers de ce lab sont dans 01-installer-kubernetes-avec-docker-desktop\pratique\. Vérifier ensuite dans le navigateur : http://localhost:8080 (API whoami).

powershell
.\labo.ps1 etat          # livrable : premiers-pas 7/7 pods Running et deux Services
.\labo.ps1 nettoyer      # à la fin, supprime les namespaces du cours

Si PowerShell refuse .\labo.ps1 (« l'exécution de scripts est désactivée ») : Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, réponds O, relance.

Linux, macOS, WSL 2, Git Bash

bash
git clone https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr.git lab2
cd lab2
ls                       # explorer le contenu : labo.sh, labo.ps1, un dossier par module (01-… à 08-…)
./labo.sh prerequis
./labo.sh etat           # cluster vide : attendu docker-desktop Ready, aucun namespace du cours

Dans un second terminal (à garder visible tout le lab) :

bash
kubectl get pods -n premiers-pas -w

Les fichiers de ce lab sont dans 01-installer-kubernetes-avec-docker-desktop/pratique/. Vérifier ensuite dans le navigateur : http://localhost:8080 (API whoami).

bash
./labo.sh etat           # livrable : premiers-pas 7/7 pods Running et deux Services
./labo.sh nettoyer       # à la fin, supprime les namespaces du cours

Objectif

Tu rejoins l'équipe qui construit le catalogue de la bibliothèque de la plateforme de cours. Ta cheffe te tend un portable : « Demain matin, je veux que Kubernetes tourne sur ton poste, que tu me prouves en une commande que tout est en ordre, et que tu ne m'appelles pas la première fois qu'un Pod refuse de démarrer. » Tu vas donc installer le kit du cours, dérouler sa liste de contrôle, déployer l'API et la regarder se multiplier en direct, puis casser deux choses exprès : demander une image qui n'existe pas, et oublier -n. Dans les deux cas tu liras le message, tu nommeras la cause et tu répareras. Savoir distinguer « le cluster refuse ce que je lui demande » de « je regarde au mauvais endroit », c'est ce qui évite une heure perdue. Le parcours : kit et prerequisetat à vide → api derrière un LoadBalancer, sous -w → cinq réplicas et dix requêtes → deux pannes provoquées → réparation → frontend en NodePort → livrable.

Avant de commencer

  • Avoir lu les quatre leçons du module : 01, 02, 03 et 04.
  • Docker Desktop lancé, Kubernetes activé en mode kubeadm (leçon 02). Le namespace premiers-pas ne doit plus exister : kubectl get namespace premiers-pas répond NotFound (sinon kubectl delete namespace premiers-pas d'abord).
  • Le kit du cours cloné : git clone https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr.git, puis place-toi à sa racine (celle qui contient labo.ps1 et labo.sh). Toutes les commandes ci-dessous sont lancées depuis là.
  • Deux terminaux côte à côte : le premier pour agir, le second pour regarder (kubectl get pods -n premiers-pas -w). PowerShell ou bash (Git Bash, WSL 2, macOS, Linux) : les commandes kubectl sont identiques, et chaque commande du kit existe dans les deux saveurs (.\labo.ps1 … / ./labo.sh …).
  • Sous Windows, si la leçon 03 t'a montré un avertissement de version, commence chaque terminal par $env:Path = "C:\Program Files\Docker\Docker\resources\bin;" + $env:Path.

Étape 1 — Installer le kit et lancer prerequis

Le script du kit fait en une commande les vérifications des leçons 02 et 03 : docker, démon, Kubernetes Docker Desktop, contexte, versions client/serveur, helm, mémoire, ports. Partout dans cette pratique, la variante bash d'une commande du kit s'obtient en remplaçant .\labo.ps1 par ./labo.sh.

powershell
.\labo.ps1 prerequis

Point de contrôle : une coche par ligne et la phrase finale Tout est prêt. Sortie réelle (la version bash affiche les mêmes lignes, avec le chemin /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

Lis la ligne kubectl client : elle te dit quel binaire répond, avec son chemin. C'est la réponse en une ligne au problème de la leçon 03. Helm ne sert qu'au module 7 : s'il manque, la ligne passe en ! (avertissement), pas en .

Si tu vois autre chose : ✘ 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 (sortie réelle obtenue avec un vieux kubectl en tête du PATH) → leçon 03, étape 3 ; ✘ le démon Docker ne répond pas → Docker Desktop n'est pas lancé ; PowerShell refuse d'exécuter le script → Set-ExecutionPolicy -Scope CurrentUser RemoteSigned une fois pour toutes.

Étape 2 — Lire etat sur un cluster vide

Avant de créer quoi que ce soit, regarde ce que le kit affiche quand il n'y a rien. Tu compareras avec l'étape 9.

powershell
.\labo.ps1 etat

Point de contrôle : le nœud Ready, premiers-pas marqué absent, aucun Service exposé. Sortie réelle (les lignes des autres namespaces du cours, qui apparaîtront module après module, sont coupées) :

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)

Trois symboles à connaître : tout tourne, ! quelque chose mérite un regard (des Pods pas Running, un avertissement), information neutre (absent, rien à afficher). Le script ne regarde que les namespaces du cours : ce qui vit dans default ou kube-system ne l'intéresse pas.

Étape 3 — Déployer l'API et l'exposer, en regardant

Dans le second terminal, lance la surveillance et ne la quitte plus jusqu'au nettoyage :

bash
kubectl get pods -n premiers-pas -w

Il ne se passe rien tant que le namespace n'existe pas, c'est normal. Dans le premier 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

Point de contrôle : trois confirmations puis un Service avec 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

Pendant ce temps, le second terminal a affiché la naissance du premier Pod, ligne par ligne :

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 (le scheduler cherche un nœud), ContainerCreating (le kubelet demande le conteneur au runtime), Running : deux secondes, parce que l'image est déjà sur la machine depuis la leçon 04. Vérifie que ça répond :

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

Extrait : Hostname est le nom du Pod qui a répondu, IP son adresse dans le cluster ; RemoteAddr: 192.168.65.3 est l'adresse du nœud, par laquelle Docker Desktop fait entrer ta requête. Le même Deployment et le même Service, écrits en YAML complet, sont dans le kit : 01-installer-kubernetes-avec-docker-desktop/04-api-declaratif.yaml (module 2 pour les lire ligne à ligne).

Si tu vois autre chose : EXTERNAL-IP <pending> qui ne bouge pas → un autre Service LoadBalancer publie déjà le port 8080 (kubectl get svc -A), supprime-le ou change de port ; AlreadyExists → le namespace traînait de la leçon 04, kubectl delete namespace premiers-pas puis recommence.

Étape 4 — Cinq réplicas, dix requêtes

Regarde bien le second terminal au moment où tu tapes ceci :

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

Point de contrôle : cinq Pods Running, chacun avec sa propre IP 10.1.x.x, tous sur le nœud 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>

Le premier Pod (mh24c, 27 s) a douze secondes de plus que les quatre autres : c'est celui de l'étape 3.

Le second terminal a montré les quatre nouveaux Pods passer ensemble par Pending, ContainerCreating puis Running en trois secondes (extrait réel) :

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

Le kit contient un petit script qui envoie dix requêtes et compte les Pods distincts qui ont répondu :

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

Sortie réelle (PowerShell ; le script affiche d'abord les dix Hostname reçus, un par ligne, puis ce décompte) :

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

Le même script lancé en bash juste après a donné 10 requêtes, 4 Pods distincts avec un Pod servi quatre fois : la répartition est aléatoire, pas un tour de rôle. Sur dix requêtes, voir quatre ou cinq Pods est normal ; en voir un seul ne l'est pas (voir « Si ça coince »). Le script ne fait rien de magique : une boucle de curl qui garde la ligne Hostname:. Ouvre-le pour le lire.

Étape 5 — Casser : demander une image qui n'existe pas

Le kit contient un Pod dont le tag d'image est faux. Lis-le avant de l'appliquer : 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

Point de contrôle : le Pod existe mais n'est pas Running, et les cinq autres ne sont pas affectés.

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

(Trois lignes api-9bfb55fc6-… identiques coupées.)

Dans le second terminal, l'état oscille : le kubelet réessaie, échoue, attend de plus en plus longtemps (extrait réel) :

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

get te dit que ça échoue ; seul describe te dit pourquoi. Va droit à la section Events, en bas :

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

Sortie réelle (seule la fin est montrée) :

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

Tout y est : qui (kubelet), quoi (Failed to pull image), pourquoi (docker.io/traefik/whoami:v9.99: not found), et le rythme (x2 over 34s). Un réflexe à perdre tout de suite : chercher la cause dans les logs. Il n'y en a pas, le conteneur n'a jamais démarré :

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 reste à 0 pour la même raison. Écris en une phrase la cause de la panne : tu en auras besoin à l'étape 9.

Étape 6 — Casser : oublier -n

Tu veux vérifier le Pod cassé, et tu tapes la commande sans le namespace :

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

Point de contrôle : deux erreurs NotFound, alors que le Pod existe bel et bien.

text
Error from server (NotFound): pods "api-cassee" not found
error: error from server (NotFound): pods "api-cassee" not found in namespace "default"

La seconde ligne dit tout : in namespace "default". Sans -n, kubectl cherche dans le namespace du contexte, default, où il n'y a pas d'api-cassee. Le Pod n'a pas disparu, tu regardes dans le mauvais tiroir. Preuve :

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

Retiens la règle : un NotFound sur un objet que tu viens de créer, c'est presque toujours un -n oublié. Ce cours ne change jamais le namespace par défaut du contexte, précisément pour que tu prennes l'habitude de le dire à chaque commande.

Étape 7 — Réparer avec kubectl set image

Un Pod dont l'image ne se télécharge pas ne se répare pas en attendant. Deux remèdes : le supprimer et réappliquer un YAML corrigé, ou changer l'image en place. Fais la seconde, la plus rapide, en regardant le second 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 est le nom du conteneur dans le YAML (containers[0].name), pas celui de l'image.

Point de contrôle :

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

Le second terminal a montré la bascule ImagePullBackOffRunning entre la 36e et la 37e seconde de vie du Pod : une seconde, parce que traefik/whoami:v1.10 est déjà sur la machine. Note que RESTARTS vaut toujours 0 : Kubernetes n'a pas redémarré un conteneur, il en a démarré un pour la première fois avec la bonne image.

Étape 8 — À toi de jouer : exposer le frontend en NodePort

L'API tourne. Ajoute maintenant le second composant de la bibliothèque : un Deployment frontend à partir de l'image nginx:1.27-alpine (port 80), exposé par un Service de type NodePort (pas LoadBalancer). Puis trouve sur quel port de ta machine il répond et affiche le titre de sa page d'accueil avec curl.

Indices : trois commandes de la leçon 04 suffisent, en remplaçant le type du Service ; le port choisi par Kubernetes se lit dans la colonne PORT(S) de kubectl get svc, après les deux-points ; sur Docker Desktop, un NodePort répond sur localhost:<port>. Le kit contient le YAML équivalent si tu préfères : 01-installer-kubernetes-avec-docker-desktop/pratique/02-frontend-nginx-nodeport.yaml.

Correction

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 est le port du Service dans le cluster, 32740 le port de nœud tiré au sort entre 30000 et 32767. Chez toi il sera différent : lis-le, ne recopie pas celui-ci.

bash
curl -s http://localhost:32740 | grep "<title>"
curl -sI http://localhost:32740 | head -3

(PowerShell : curl.exe -s … | Select-String "<title>" et 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 confirme le tag 1.27-alpine. EXTERNAL-IP <none> est normal pour un NodePort : personne ne lui attribue d'adresse, il ouvre un port sur le nœud, et Docker Desktop relaie ce port sur localhost. Le module 4 compare les trois types de Service en détail. Avec le YAML du kit : kubectl apply -f 01-installer-kubernetes-avec-docker-desktop/pratique/02-frontend-nginx-nodeport.yaml crée les deux mêmes objets (vérifié : deployment.apps/frontend created, service/frontend created).

Étape 9 — Le livrable : etat et trois questions

Relance le script du kit et compare avec l'étape 2.

powershell
.\labo.ps1 etat

Sortie réelle (mêmes coupures qu'à l'étape 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

Sept Pods : cinq api, api-cassee réparé, un frontend. Copie cette sortie dans ton livrable, puis réponds en une ou deux phrases chacune :

  1. Pourquoi kubectl logs api-cassee -n premiers-pas n'a-t-il rien pu afficher à l'étape 5, et où as-tu trouvé la cause ?
  2. À l'étape 6, le message disait not found in namespace "default" : le Pod avait-il disparu ? Qu'aurais-tu dû taper ?
  3. api affiche EXTERNAL-IP localhost et frontend <none> : quel type de Service a chacun, et par quel port de ta machine réponds-tu à chacun ?

Vérification finale

  • .\labo.ps1 prerequis (ou ./labo.sh prerequis) se termine par Tout est prêt.
  • kubectl get all -n premiers-pas montre deployment.apps/api 5/5 5 5, deployment.apps/frontend 1/1 1 1, pod/api-cassee 1/1 Running, un replicaset.apps/api-9bfb55fc6 5 5 5 et les deux Services (sortie réelle : sept Pods, deux Services, deux Deployments, deux ReplicaSets).
  • curl -s http://localhost:8080 | grep Hostname (PowerShell : curl.exe -s http://localhost:8080 | Select-String Hostname) renvoie un Hostname: api-… qui change d'un appel à l'autre.
  • curl -sI http://localhost:<port de frontend> renvoie HTTP/1.1 200 OK et Server: nginx/1.27.5.
  • Tu sais dire, sans relire, ce que signifient ErrImagePull, ImagePullBackOff et not found in namespace "default".

Nettoyage

Le kit sait supprimer tous les namespaces du cours d'un coup, mais il refuse de le faire sans confirmation. Regarde d'abord ce qu'il ferait :

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

Pour ce module, supprime seulement premiers-pas, à la main, et garde le second terminal ouvert pour voir les sept Pods passer en 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 dans le second terminal. http://localhost:8080 ne répond plus (curl échoue avec le code 7, connexion refusée, ou 52, réponse vide). .\labo.ps1 nettoyer -oui / ./labo.sh nettoyer --oui servira quand tu voudras remettre tout le cours à zéro : il ne touche jamais default ni kube-system.

Si ça coince

  • labo.ps1 refuse de se lancer : l'exécution de scripts est désactivée sur ce systèmeSet-ExecutionPolicy -Scope CurrentUser RemoteSigned, ferme et rouvre PowerShell. Le script est en UTF-8 avec BOM et fonctionne sous PowerShell 5.1 comme sous PowerShell 7.

  • ./labo.sh: Permission denied ou /usr/bin/env: 'bash\r': No such file or directorychmod +x labo.sh pour le premier ; pour le second, Git a converti les fins de ligne en CRLF à l'extraction : sed -i 's/\r$//' labo.sh (ou git config core.autocrlf false puis re-clone).

  • prerequis affiche ✘ port 8080 déjà occupé par <programme> (PID …) — arrêtez-le ou choisissez un autre port dans les leçons → une application locale écoute déjà (un autre serveur de développement, un port-forward oublié) ; le script nomme le processus. Ferme-le, ou choisis --port=8081 à l'étape 3 et adapte les URL. Si le port est tenu par un Service Kubernetes du cours, la ligne reste : utilisé par un Service du cours (LoadBalancer ou NodePort).

  • EXTERNAL-IP <pending> à l'étape 3 → sur Docker Desktop, le port demandé est déjà publié par un autre Service LoadBalancer (vérifié : un second Service sur 8080 reste <pending> tant que le premier existe). kubectl get svc -A montre lequel. Sur kind ou minikube, <pending> est l'état normal : utilise kubectl port-forward.

  • repartition renvoie dix fois le même Hostname → soit scale n'a pas encore fini (kubectl get pods -n premiers-pas : attends cinq Running), soit le Service n'a qu'un seul point de terminaison : kubectl get endpointslice -n premiers-pas doit lister cinq IP (10.1.2.255,10.1.3.3,10.1.3.0 + 2 more... sur la machine du cours).

  • Après set image, api-cassee reste en ImagePullBackOff → tu as tapé un nom de conteneur ou un tag faux : kubectl describe pod api-cassee -n premiers-pas montre l'image réellement demandée dans Containers: whoami: Image:. Le nom à gauche du = est celui du conteneur (whoami), pas de l'image.

Ce que tu as appris

  • labo.ps1 prerequis / labo.sh prerequis vérifie en une commande ce que les leçons 02 et 03 faisaient à la main, et nomme le binaire kubectl qui répond.
  • labo.ps1 etat résume nœud, Pods par namespace du cours et Services exposés : c'est la photo à joindre quand tu demandes de l'aide.
  • Sous kubectl get pods -w, un Pod passe par Pending, ContainerCreating, Running ; cinq réplicas naissent en trois secondes quand l'image est déjà là.
  • ErrImagePull puis ImagePullBackOff = l'image ne se télécharge pas ; la cause est dans kubectl describe, section Events, jamais dans kubectl logs (le conteneur n'a pas démarré).
  • not found in namespace "default" = -n oublié ; l'objet est toujours là, dans un autre namespace.
  • kubectl set image pod/<nom> <conteneur>=<image> corrige une image en place ; un Service NodePort répond sur localhost:<30000-32767> avec Docker Desktop, un LoadBalancer sur le port que tu as choisi.

Défi bonus (optionnel)

Sans supprimer le namespace, provoque une troisième panne et répare-la : passe le Deployment api sur un tag inexistant avec kubectl set image deployment/api whoami=traefik/whoami:v9.99 -n premiers-pas, puis observe dans le second terminal que Kubernetes ne détruit pas les Pods qui marchent : sur la machine du cours, il a créé trois Pods api-78d7f59657-… en ErrImagePull, retiré un seul ancien, gardé quatre api-9bfb55fc6-… en Running, et s'est arrêté là (kubectl rollout status deployment/api -n premiers-pas reste sur Waiting for deployment "api" rollout to finish: 3 out of 5 new replicas have been updated...). http://localhost:8080 continue de répondre. Répare avec kubectl rollout undo deployment/api -n premiers-pas (deployment.apps/api rolled back) et vérifie que les Pods cassés disparaissent et que cinq api-9bfb55fc6-… tournent. Tu viens de voir le rolling update, ses garde-fous (maxSurge, maxUnavailable) et le rollback, sujets du module 3.