45 minutes
Les sessions Killercoda sont éphémères : ce manifeste reconstruit en une commande l'état du fil rouge à la fin du TP 1. Appliquez-le dès maintenant : vos briques serviront de sujet d'observation dans les exercices.
Créez le fichier fil-rouge-tp02.yaml :
# fil-rouge-tp02.yaml
apiVersion: v1
kind: Pod
metadata:
name: vote
labels:
app: vote
spec:
containers:
- name: vote
image: dockersamples/examplevotingapp_vote:latest
ports:
- containerPort: 80
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
---
apiVersion: v1
kind: Pod
metadata:
name: redis
labels:
app: redis
spec:
containers:
- name: redis
image: redis:8-alpine
ports:
- containerPort: 6379
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
---
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
app: redis
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
Puis appliquez-le et vérifiez :
kubectl apply -f fil-rouge-tp02.yaml
kubectl wait --for=condition=Ready pod/vote pod/redis --timeout=120s
kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 40s
vote 1/1 Running 0 40s
Découvrir les composants système de Kubernetes et situer nos applications dans cette architecture.
# Voir tous les Pods système
kubectl get pods -n kube-system
# Voir les composants du Control Plane
kubectl get pods -n kube-system | grep -E "(apiserver|scheduler|controller|etcd)"
# Détailler l'API Server (adaptez le nom exact affiché ci-dessus)
kubectl describe pod kube-apiserver-controlplane -n kube-system
Sur un cluster kubeadm, les composants du Control Plane ne sont pas déployés par l'API Server… puisqu'ils sont l'API Server. Ce sont des Pods statiques, lancés directement par le kubelet à partir de fichiers locaux :
ls /etc/kubernetes/manifests
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
💡 C'est pour cela que leurs noms de Pods sont suffixés par le nom du nœud (kube-apiserver-controlplane) : le kubelet de chaque nœud gère les siens.
# Lister les nœuds avec détails
kubectl get nodes -o wide
# Voir les ressources et la capacité de chaque nœud
kubectl describe nodes
# Sur quel nœud tournent nos Pods ?
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis 1/1 Running 0 5m 192.168.1.4 node01 <none> <none>
vote 1/1 Running 0 5m 192.168.1.3 node01 <none> <none>
💡 Nos Pods tournent sur node01 : le nœud controlplane porte par défaut une restriction (un « taint ») qui en écarte les applications ; nous verrons ce mécanisme au TP 7.
# Relire l'histoire du Pod vote à travers ses événements
kubectl describe pod vote
Dans la section Events, retrouvez qui a fait quoi :
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 6m default-scheduler Successfully assigned default/vote to node01
Normal Pulling 6m kubelet Pulling image "dockersamples/examplevotingapp_vote:latest"
Normal Pulled 5m kubelet Successfully pulled image ...
Normal Created 5m kubelet Created container: vote
Normal Started 5m kubelet Started container vote
Scheduled vient du scheduler (la décision de placement), tout le reste vient du kubelet de node01 (l'exécution).
vote, et qui a téléchargé son image ?Q: Quels sont les 4 composants principaux du Control Plane que vous observez ?
R: kube-apiserver (porte d'entrée de toutes les requêtes), etcd (base de données du cluster), kube-scheduler (placement des Pods) et kube-controller-manager (boucles de réconciliation).
Q: Qui a décidé du placement du Pod vote, et qui a téléchargé son image ?
R: Le scheduler a choisi le nœud (événement Scheduled, émis par default-scheduler) ; le kubelet du nœud choisi a ensuite téléchargé l'image et démarré le conteneur (événements Pulling, Created, Started).
Q: Pourquoi les composants du Control Plane sont-ils des Pods statiques sur un cluster kubeadm ?
R: Problème de l'œuf et de la poule : l'API Server ne peut pas se déployer lui-même. Le kubelet sait démarrer seul les Pods décrits dans /etc/kubernetes/manifests, ce qui amorce le cluster.
Comprendre comment le scheduler place les Pods sur les nœuds.
Vous savez créer un Deployment et un Pod depuis le TP 1 : cette fois, la consigne suffit. Observez le travail du scheduler par vous-même.
scheduler-demo (image nginx:1.29.5, 4 réplicas) et observez sur quels nœuds ses Pods ont été placés.resource-demo (image nginx:1.29.5) qui demande 128Mi/100m et se limite à 256Mi/200m, puis vérifiez son placement et ses événements.💡 Indices : -o wide affiche le nœud ; kubectl get events --sort-by=.metadata.creationTimestamp ; le TP 1 contient un modèle de Pod avec resources.
1. Répartition des réplicas :
kubectl create deployment scheduler-demo --image=nginx:1.29.5 --replicas=4
# Observer le placement sur les nœuds
kubectl get pods -l app=scheduler-demo -o wide
2. Les événements de scheduling :
kubectl get events --sort-by=.metadata.creationTimestamp
3. Le Pod avec ressources spécifiques :
Fichier resource-demo.yaml :
# resource-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: resource-demo
spec:
containers:
- name: app
image: nginx:1.29.5
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
kubectl apply -f resource-demo.yaml
# Observer où il est placé
kubectl get pod resource-demo -o wide
kubectl describe pod resource-demo
Q: Comment le scheduler choisit-il sur quel nœud placer un Pod ?
R: En deux phases : le filtrage élimine les nœuds inéligibles (ressources insuffisantes, taints, sélecteurs non satisfaits), puis le scoring classe les nœuds restants (répartition de charge, affinités) et le mieux noté est retenu.
Q: Que se passe-t-il si aucun nœud n'a les ressources suffisantes ?
R: Le Pod reste en état Pending, avec un événement FailedScheduling expliquant la raison (par exemple Insufficient memory). Il sera schedulé dès qu'un nœud éligible se libère.
Q: Comment voir les événements de scheduling ?
R: kubectl describe pod <nom> (section Events) pour un Pod précis, ou kubectl get events --sort-by=.metadata.creationTimestamp pour l'historique global.
Comprendre comment les composants et les applications se trouvent les uns les autres.
# Les Services du namespace système
kubectl get services -n kube-system
# Le service DNS du cluster et ses endpoints
kubectl get endpointslice -n kube-system
kubectl describe endpointslice -n kube-system <nom-affiché-ci-dessus>
kubectl run dns-test --image=busybox:1.36 -it --rm -- sh
# Dans le Pod de test :
# nslookup kubernetes.default.svc.cluster.local
# exit
redis du fil rougeLe Service redis créé au Point de départ possède lui aussi son nom DNS et ses endpoints :
# Qui se cache derrière le Service redis ?
kubectl get endpointslice -l kubernetes.io/service-name=redis
kubectl describe endpointslice -l kubernetes.io/service-name=redis
L'endpointslice liste l'IP du Pod redis : c'est la table de correspondance que le cluster maintient entre le nom stable et les Pods réels.
# Résolution DNS et test de connectivité vers redis
kubectl run dns-test2 --image=busybox:1.36 -it --rm -- sh
# Dans le Pod de test :
# nslookup redis
# nc -zv redis 6379
# exit
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: redis.default.svc.cluster.local
Address: 10.101.34.156
redis (10.101.34.156:6379) open
✅ C'est exactement le chemin qu'emprunte l'application vote pour joindre sa file d'attente : résolution du nom redis par CoreDNS, puis connexion TCP au port 6379.
Q: Comment les Pods trouvent-ils l'API Server ?
R: Via le Service kubernetes du namespace default (nom DNS kubernetes.default.svc.cluster.local), présent dans tous les clusters, qui pointe vers l'API Server.
Q: Que sont les endpoints (endpointslices) et comment sont-ils créés ?
R: Ce sont les listes d'IP:port des Pods qui correspondent au sélecteur d'un Service. Un contrôleur les maintient à jour automatiquement à chaque création, suppression ou changement d'état de Pod.
Q: Comment CoreDNS résout-il les noms de services ?
R: CoreDNS surveille les Services via l'API Server et publie un enregistrement DNS <service>.<namespace>.svc.cluster.local pointant vers l'IP stable (ClusterIP) de chaque Service. Depuis le même namespace, le nom court (redis) suffit.
Apprendre à diagnostiquer les problèmes de placement des Pods.
Un développeur vous signale que son application ne démarre pas. Le Pod reste en état "Pending". Trouvez et corrigez le problème :
# problematic-scheduling.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: high-memory-app
spec:
replicas: 1
selector:
matchLabels:
app: memory-app
template:
metadata:
labels:
app: memory-app
spec:
containers:
- name: app
image: nginx:1.29.5
resources:
requests:
memory: "64Gi"
cpu: "100m"
limits:
memory: "128Gi"
cpu: "200m"
nodeSelector:
disktype: "nvme"
Instructions :
kubectl describe pour diagnostiquerkubectl describe pod pour voir les événementskubectl describe nodeskubectl get nodes --show-labelsProblèmes identifiés :
disktype: nvme n'existe probablement pasConfiguration corrigée :
apiVersion: apps/v1
kind: Deployment
metadata:
name: high-memory-app
spec:
replicas: 1
selector:
matchLabels:
app: memory-app
template:
metadata:
labels:
app: memory-app
spec:
containers:
- name: app
image: nginx:1.29.5
resources:
requests:
memory: "64Mi" # ✅ Valeur réaliste
cpu: "100m"
limits:
memory: "128Mi" # ✅ Valeur réaliste
cpu: "200m"
# nodeSelector supprimé
Diagnostiquer les problèmes liés aux images de conteneurs.
Votre cluster semble avoir des problèmes. Une nouvelle application ne se déploie pas correctement. Identifiez le problème :
# faulty-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: broken-app
spec:
replicas: 2
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
spec:
containers:
- name: nginx
image: nginx:1.99.nonexistent
ports:
- containerPort: 80
Instructions :
kubectl describe et kubectl get events pour diagnostiquerkubectl get podskubectl describe pod pour voir les détailskubectl get eventsProblème identifié :
nginx:1.99.nonexistent n'existe pas dans le registryErrImagePull puis ImagePullBackOff (Kubernetes réessaie avec un délai croissant)Configuration corrigée :
apiVersion: apps/v1
kind: Deployment
metadata:
name: working-app
spec:
replicas: 2
selector:
matchLabels:
app: working-app
template:
metadata:
labels:
app: working-app
spec:
containers:
- name: nginx
image: nginx:1.29.5 # ✅ Image existante
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
/etc/kubernetes/manifests)Scheduled (scheduler) puis Pulling/Started (kubelet)redis relie un nom DNS stable aux IP réelles des Pods via son endpointslice