Lab 2.1 : Mon premier Pod¶
| Durée | 45 à 60 min |
| Niveau | Guidé |
| Prérequis | Lab 1.2 réalisé (alias k, notion de manifeste), cours du chapitre 2 lu, cluster à 3 nœuds Ready |
| Acquis visés | AA1, AA2 |
Objectifs
- Écrire et appliquer un manifeste de Pod
- Observer le cycle de vie d'un Pod et lire ses événements
- Constater qu'un Pod isolé n'est pas recréé et que son adresse IP change
- Reconnaître et diagnostiquer
ImagePullBackOffetCrashLoopBackOff - Utiliser les labels et les sélecteurs
Contexte et schéma¶
Au Lab 1.2, vous avez généré un manifeste avec kubectl run. Cette fois, vous écrivez vous-même le manifeste, puis vous observez le comportement d'un Pod dans des situations normales et en panne.
flowchart LR
U["Vous<br/>kubectl apply"] --> API["kube-apiserver"]
API --> S["kube-scheduler<br/>choisit un nœud"]
S --> K["kubelet<br/>démarre le conteneur"]
K --> P["Pod web"]
T["Pod temporaire<br/>busybox"] -- "wget" --> P
Étapes¶
Étape 1 : Préparer le namespace¶
k create namespace tp-k8s
k config set-context --current --namespace=tp-k8s
k config view --minify | grep namespace
Si le namespace existe déjà
Si tp-k8s a été conservé du Lab 1.2, l'erreur AlreadyExists est sans gravité. Vérifiez simplement qu'il est vide avec k get all.
Étape 2 : Écrire et appliquer un manifeste de Pod¶
Créez le fichier web.yaml :
cat <<'EOF' > web.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
tier: frontend
spec:
containers:
- name: web
image: nginx:1.26-alpine
ports:
- containerPort: 80
EOF
Relisez-le : repérez apiVersion, kind, metadata et spec, puis appliquez-le en surveillant les changements d'état (touche Ctrl+C pour quitter la surveillance) :
Observez la progression ContainerCreating puis Running. Lisez ensuite les événements :
Dans la section Events, repérez dans l'ordre : Scheduled, Pulling, Pulled, Created, Started.
Point de contrôle
- Le Pod
webestRunning(colonneREADYà1/1) - Vous avez identifié l'événement
Scheduledet le nœud choisi - Vous avez identifié l'événement qui correspond au téléchargement de l'image
Étape 3 : Joindre le Pod depuis un autre Pod¶
Récupérez l'adresse IP du Pod, puis appelez-la depuis un Pod temporaire (supprimé automatiquement à la fin) :
k get pod web -o wide
IP=$(k get pod web -o jsonpath='{.status.podIP}')
echo $IP
k run test --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://$IP
Vous devez voir la page d'accueil de nginx. Le Pod test a été créé, a exécuté la commande, puis a disparu.
Point de contrôle
- La page « Welcome to nginx! » s'affiche
-
k get podsne montre plus le Podtest
Étape 4 : Constater qu'un Pod isolé n'est pas recréé¶
Le Pod a disparu et rien ne le recrée : aucun contrôleur ne le surveille. Recréez-le puis comparez l'adresse IP :
Point de contrôle
- Après la suppression, aucun Pod
webn'existait - Vous avez comparé l'ancienne et la nouvelle adresse IP (elle peut différer ; ne comptez jamais sur elle)
Étape 5 : Provoquer et diagnostiquer ImagePullBackOff¶
Créez un Pod dont l'image n'existe pas :
cat <<'EOF' > bad-image.yaml
apiVersion: v1
kind: Pod
metadata:
name: bad-image
spec:
containers:
- name: web
image: nginx:inexistant
EOF
k apply -f bad-image.yaml
k get pod bad-image -w
Attendez l'apparition de ErrImagePull puis ImagePullBackOff (environ 30 secondes), puis quittez avec Ctrl+C. Cherchez la cause :
Dans Events, lisez le message d'échec du téléchargement. Corrigez en supprimant le Pod, en réparant le tag dans le fichier (nginx:1.26-alpine), puis en réappliquant :
k delete pod bad-image
sed -i 's|nginx:inexistant|nginx:1.26-alpine|' bad-image.yaml
k apply -f bad-image.yaml
k get pod bad-image
Point de contrôle
- Vous avez lu dans
describela cause de l'échec - Après correction, le Pod
bad-imagepasse àRunning
Étape 6 : Provoquer et diagnostiquer CrashLoopBackOff¶
Cette fois, l'image existe mais le conteneur s'arrête en erreur :
cat <<'EOF' > crash.yaml
apiVersion: v1
kind: Pod
metadata:
name: crash
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo demarrage; sleep 2; echo erreur fatale; exit 1"]
EOF
k apply -f crash.yaml
k get pod crash -w
Laissez tourner environ une minute : observez la colonne RESTARTS augmenter et le statut passer par Error puis CrashLoopBackOff. Quittez avec Ctrl+C, puis :
Les logs expliquent pourquoi le conteneur s'arrête ; describe donne le code de sortie. Le kubelet redémarre le conteneur (politique Always par défaut), en espaçant de plus en plus les tentatives.
Point de contrôle
- Vous avez observé
RESTARTSsupérieur à 0 - Vous avez retrouvé le message
erreur fataledans les logs - Vous avez lu le code de sortie (
Exit Code) dansdescribe
Étape 7 : Politique de redémarrage et phases d'un Pod¶
Créez deux Pods qui ne se relancent pas (restartPolicy: Never) : l'un se termine bien, l'autre en erreur.
cat <<'EOF' > fin.yaml
apiVersion: v1
kind: Pod
metadata:
name: fin-ok
spec:
restartPolicy: Never
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo bonjour; exit 0"]
---
apiVersion: v1
kind: Pod
metadata:
name: fin-erreur
spec:
restartPolicy: Never
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo oups; exit 1"]
EOF
k apply -f fin.yaml
sleep 15
k get pods
k get pod fin-ok -o jsonpath='{.status.phase}'; echo
k get pod fin-erreur -o jsonpath='{.status.phase}'; echo
Point de contrôle
-
fin-okestCompleted(phaseSucceeded) et n'a pas redémarré -
fin-erreurestError(phaseFailed) et n'a pas redémarré -
crasha continué à redémarrer, contrairement àfin-erreur
Étape 8 : Labels et sélecteurs¶
Créez un second Pod avec d'autres labels, puis interrogez par sélecteur :
k run web2 --image=nginx:1.26-alpine --labels="app=web,tier=backend"
k get pods --show-labels
k get pods -l app=web
k get pods -l tier=frontend
k get pods -l 'app=web,tier=backend'
Modifiez un label, puis agissez sur un groupe de Pods par sélecteur :
k label pod web2 tier=frontend --overwrite
k get pods -l tier=frontend
k delete pods -l app=web
k get pods
Point de contrôle
- Chaque requête
-la renvoyé les Pods attendus - Après
delete -l app=web, les Podswebetweb2ont disparu, les autres sont restés
Questions de réflexion¶
Pourquoi le Pod web n'a-t-il pas été recréé après sa suppression ?
Il n'est géré par aucun contrôleur (ReplicaSet ou Deployment). Un Pod isolé disparaît définitivement. Le Lab 2.2 montre comment un Deployment résout ce problème.
Quelle commande vous a donné la cause de ImagePullBackOff, et laquelle celle de CrashLoopBackOff ?
kubectl describe pod (section Events) pour l'image introuvable ; kubectl logs (avec --previous si besoin) pour le conteneur qui plante, complété par le code de sortie dans describe.
Pourquoi crash redémarre-t-il en boucle alors que fin-erreur reste en erreur ?
Leur restartPolicy diffère : Always (défaut) pour crash, Never pour fin-erreur. Le kubelet ne relance que si la politique le permet.
Pourquoi ne faut-il pas utiliser l'adresse IP d'un Pod pour le joindre durablement ?
L'IP est propre à chaque Pod ; un Pod remplacé en reçoit une nouvelle. C'est la raison d'être des Services (chapitre 3).
Dépannage¶
| Symptôme | Pistes |
|---|---|
AlreadyExists à la création d'un Pod |
Le Pod existe déjà : k delete pod <nom> puis réappliquer |
Pod bloqué en ContainerCreating |
Téléchargement lent ; k describe pod ; vérifier l'accès Internet des VMs |
k run test --rm ... reste sans réponse |
Vérifier l'IP avec echo $IP ; la variable est vide si vous avez ouvert un nouveau terminal |
wget: can't connect |
Le Pod web n'est pas Running, ou l'IP a changé après recréation |
Rien ne s'affiche dans k logs crash |
Le conteneur vient de redémarrer : utiliser --previous |
The Pod "..." is invalid à la réapplication |
Certains champs d'un Pod ne se modifient pas ; supprimer puis recréer le Pod |