Aller au contenu
Conteneurs & Orchestration medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Exercices CKA — Entraînement chronométré

30 min de lecture

logo kubernetes

Entraînement brutal, précis, rapide. Chaque exercice reproduit le format CKA : énoncé court, objectif observable, contrainte de temps. Visez 80% de réussite.

Configurez votre environnement avant de commencer :

Fenêtre de terminal
# Alias essentiels
alias k=kubectl
alias kn='kubectl config set-context --current --namespace'
alias kgp='kubectl get pods'
alias kgn='kubectl get nodes'
export do="--dry-run=client -o yaml"
export now="--force --grace-period=0"
# Autocomplétion
source <(kubectl completion bash)
complete -F __start_kubectl k
# Namespace de travail
kubectl create ns cka-exercises
kn cka-exercises

Temps cible : 15 minutes

ContexteCluster kubeadm en version 1.30.x
TâcheUpgrade le control plane et un worker vers la dernière version patch de 1.31
ContrainteUtiliser les versions disponibles dans le repo apt
Validationkubectl get nodes affiche tous les nodes en v1.31.x
Solution
Fenêtre de terminal
# 1. Identifier la dernière version patch disponible
apt update
apt-cache madison kubeadm | grep 1.31
# Exemple : 1.31.4-1.1
# 2. Sur le control plane
apt-get install -y kubeadm=1.31.4-1.1
kubeadm upgrade plan
kubeadm upgrade apply v1.31.4
apt-get install -y kubelet=1.31.4-1.1 kubectl=1.31.4-1.1
systemctl daemon-reload && systemctl restart kubelet
# 3. Drain le worker depuis le control plane
kubectl drain worker1 --ignore-daemonsets --delete-emptydir-data
# 4. Sur le worker (via SSH)
apt-get install -y kubeadm=1.31.4-1.1
kubeadm upgrade node
apt-get install -y kubelet=1.31.4-1.1
systemctl daemon-reload && systemctl restart kubelet
# 5. Uncordon depuis le control plane
kubectl uncordon worker1
kubectl get nodes

Point clé : Toujours upgrader vers la dernière version patch (1.31.4), pas la version .0.


Temps cible : 6 minutes

ContexteCluster kubeadm standard
TâcheCréer un snapshot etcd dans /opt/backup/etcd-snapshot.db
ContrainteCertificats dans /etc/kubernetes/pki/etcd/
Validationetcdctl snapshot status /opt/backup/etcd-snapshot.db --write-out=table affiche les infos
Solution
Fenêtre de terminal
export ETCDCTL_API=3
mkdir -p /opt/backup
etcdctl snapshot save /opt/backup/etcd-snapshot.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# Validation
etcdctl snapshot status /opt/backup/etcd-snapshot.db --write-out=table

Temps cible : 10 minutes

ContexteSnapshot etcd disponible dans /opt/backup/etcd-snapshot.db
TâcheRestaurer etcd vers /var/lib/etcd-restore et reconfigurer le cluster
ContrainteUtiliser etcdutl (pas etcdctl restore qui est déprécié)
Validationkubectl get pods -n kube-system fonctionne après la restauration
Solution
Fenêtre de terminal
# 1. Arrêter l'API server (déplacer le manifest)
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/
# 2. Restore avec etcdutl (pas etcdctl !)
etcdutl snapshot restore /opt/backup/etcd-snapshot.db \
--data-dir=/var/lib/etcd-restore
# 3. Modifier le manifest etcd pour pointer vers le nouveau répertoire
sed -i 's|/var/lib/etcd|/var/lib/etcd-restore|g' /etc/kubernetes/manifests/etcd.yaml
# 4. Remettre l'API server
mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
# 5. Attendre que les composants redémarrent (~30 secondes)
sleep 30
kubectl get pods -n kube-system

Point clé : etcdctl snapshot restore est déprécié depuis etcd 3.5. Utiliser etcdutl snapshot restore.


Temps cible : 4 minutes

ContexteNamespace cka-exercises
TâcheCréer un Role pod-reader (get, list, watch pods) et un RoleBinding read-pods pour l’utilisateur jane
Validationkubectl auth can-i list pods -n cka-exercises --as=jane retourne yes
Solution
Fenêtre de terminal
kubectl create role pod-reader \
--verb=get,list,watch \
--resource=pods \
-n cka-exercises
kubectl create rolebinding read-pods \
--role=pod-reader \
--user=jane \
-n cka-exercises
# Validation
kubectl auth can-i list pods -n cka-exercises --as=jane # yes
kubectl auth can-i delete pods -n cka-exercises --as=jane # no

Temps cible : 4 minutes

ContexteRessource cluster-wide : nodes
TâcheCréer un ClusterRole node-viewer (get, list, watch nodes) et un ClusterRoleBinding node-view-binding pour l’utilisateur bob
Validationkubectl auth can-i list nodes --as=bob retourne yes
Solution
Fenêtre de terminal
kubectl create clusterrole node-viewer \
--verb=get,list,watch \
--resource=nodes
kubectl create clusterrolebinding node-view-binding \
--clusterrole=node-viewer \
--user=bob
# Validation
kubectl auth can-i list nodes --as=bob # yes
kubectl auth can-i delete nodes --as=bob # no

Temps cible : 5 minutes

ContexteNamespace cka-exercises
TâcheCréer un ServiceAccount app-sa, un Role secret-reader (get, list secrets), les lier, et créer un Pod app-pod utilisant ce SA
ContrainteLe Pod doit utiliser serviceAccountName dans le YAML (pas le flag déprécié --serviceaccount)
Validationkubectl auth can-i list secrets --as=system:serviceaccount:cka-exercises:app-sa -n cka-exercises retourne yes
Solution
Fenêtre de terminal
# ServiceAccount
kubectl create sa app-sa -n cka-exercises
# Role
kubectl create role secret-reader \
--verb=get,list \
--resource=secrets \
-n cka-exercises
# RoleBinding
kubectl create rolebinding app-secret-binding \
--role=secret-reader \
--serviceaccount=cka-exercises:app-sa \
-n cka-exercises
# Pod avec serviceAccountName (pas --serviceaccount qui est déprécié)
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: app-pod
namespace: cka-exercises
spec:
serviceAccountName: app-sa
containers:
- name: nginx
image: nginx
EOF
# Validation
kubectl auth can-i list secrets \
--as=system:serviceaccount:cka-exercises:app-sa \
-n cka-exercises

Point clé : Le flag --serviceaccount de kubectl run est déprécié. Utiliser un manifest YAML avec serviceAccountName.


Exercice 7 : Taints, Tolerations et placement garanti

Section intitulée « Exercice 7 : Taints, Tolerations et placement garanti »

Temps cible : 6 minutes

ContexteNode worker1 disponible
TâcheAjouter un taint env=prod:NoSchedule sur worker1, créer un Pod prod-app qui tolère ce taint et qui est garanti d’être placé sur worker1
ContrainteLa toleration seule ne garantit pas le placement — il faut aussi un nodeSelector
Validationkubectl get pod prod-app -o wide montre worker1 dans la colonne NODE
Solution
Fenêtre de terminal
# Taint
kubectl taint node worker1 env=prod:NoSchedule
# Pod avec toleration ET nodeSelector
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: prod-app
namespace: cka-exercises
spec:
nodeSelector:
kubernetes.io/hostname: worker1
tolerations:
- key: env
operator: Equal
value: prod
effect: NoSchedule
containers:
- name: nginx
image: nginx
EOF
# Validation
kubectl get pod prod-app -n cka-exercises -o wide | grep worker1
# Nettoyage
kubectl taint node worker1 env-

Point clé : Une toleration permet de scheduler sur un node tainté, mais ne garantit pas le placement. Pour garantir, ajouter un nodeSelector ou nodeAffinity.


Temps cible : 5 minutes

ContexteNode worker1 disponible
TâcheLabeler worker1 avec disktype=ssd, créer un Deployment storage-app (3 replicas, nginx) qui préfère les nodes avec ce label
Validationkubectl get pods -o wide montre les pods sur worker1 (si disponible)
Solution
Fenêtre de terminal
# Label
kubectl label node worker1 disktype=ssd
# Deployment
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: storage-app
namespace: cka-exercises
spec:
replicas: 3
selector:
matchLabels:
app: storage-app
template:
metadata:
labels:
app: storage-app
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx
image: nginx
EOF
# Validation
kubectl get pods -n cka-exercises -o wide

Temps cible : 3 minutes

ContexteNode worker1 avec des pods
TâcheCordon, drain, puis uncordon worker1
Validationkubectl get nodes montre worker1 Ready sans SchedulingDisabled
Solution
Fenêtre de terminal
kubectl cordon worker1
kubectl drain worker1 --ignore-daemonsets --delete-emptydir-data
kubectl uncordon worker1
kubectl get nodes

Temps cible : 3 minutes

ContexteNamespace cka-exercises
TâcheCréer un Deployment web (2 replicas, nginx), l’exposer en ClusterIP sur port 80
Validationkubectl run test --rm -it --image=busybox:1.36 -- wget -qO- web retourne du HTML
Solution
Fenêtre de terminal
kubectl create deploy web --image=nginx --replicas=2 -n cka-exercises
kubectl expose deploy web --port=80 -n cka-exercises
# Validation
kubectl run test --rm -it --image=busybox:1.36 -n cka-exercises -- wget -qO- web

Temps cible : 4 minutes

ContexteNamespace cka-exercises avec le Deployment web
TâcheCréer une NetworkPolicy deny-all bloquant tout trafic ingress et egress dans le namespace
Validationkubectl describe netpol deny-all montre Allowing ingress traffic: <none>
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: cka-exercises
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
EOF
# Validation
kubectl describe netpol deny-all -n cka-exercises

Temps cible : 6 minutes

ContexteDeployment web avec label app=web dans cka-exercises
TâcheCréer une NetworkPolicy allow-frontend autorisant le trafic ingress sur port 80 uniquement depuis les pods avec label role=frontend
ContrainteLe podSelector de la policy doit cibler app=web
ValidationUn pod avec role=frontend peut accéder à web, un pod sans ce label ne peut pas
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: cka-exercises
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 80
EOF
# Test positif
kubectl run frontend --image=busybox:1.36 -n cka-exercises \
--labels="role=frontend" --rm -it -- wget -qO- --timeout=2 web
# Test négatif (doit timeout)
kubectl run other --image=busybox:1.36 -n cka-exercises \
--rm -it -- wget -qO- --timeout=2 web

Point clé : Vérifier que les labels dans le podSelector correspondent aux labels réels des pods target.


Temps cible : 5 minutes

ContexteService web exposé sur port 80
TâcheCréer un Ingress web-ingress routant web.example.com/ vers le service web port 80
ContrainteUtiliser ingressClassName: nginx
Validationkubectl get ingress web-ingress montre l’Ingress créé
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: cka-exercises
spec:
ingressClassName: nginx
rules:
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
EOF
# Validation
kubectl get ingress web-ingress -n cka-exercises

Temps cible : 7 minutes

ContexteStorageClass manual pour hostPath
TâcheCréer un PV pv-data (1Gi, RWO, hostPath /data/pv-data), un PVC pvc-data (500Mi), et un Pod storage-pod montant le PVC
Validationkubectl get pv montre BOUND, kubectl get pvc -n cka-exercises montre BOUND
Solution
Fenêtre de terminal
# PV (cluster-scoped, pas de namespace)
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-data
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /data/pv-data
EOF
# PVC (namespaced)
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-data
namespace: cka-exercises
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
storageClassName: manual
EOF
# Pod
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: storage-pod
namespace: cka-exercises
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: pvc-data
EOF
# Validation
kubectl get pv # cluster-scoped
kubectl get pvc -n cka-exercises # namespaced

Point clé : Les PV sont cluster-scoped (kubectl get pv sans -n). Les PVC sont namespaced (kubectl get pvc -n <ns>).


C’est le domaine le plus important de l’examen : 30% des points.

Temps cible : 3 minutes

ContextePod broken-pod avec image nginx:nonexistent-tag
TâcheDiagnostiquer et corriger le Pod
Validationkubectl get pod broken-pod montre Running
Solution
Fenêtre de terminal
# Créer le problème
kubectl run broken-pod --image=nginx:nonexistent-tag -n cka-exercises
# Diagnostic
kubectl get pod broken-pod -n cka-exercises
kubectl describe pod broken-pod -n cka-exercises | tail -10
# Correction
kubectl set image pod/broken-pod broken-pod=nginx -n cka-exercises
# Validation
kubectl get pod broken-pod -n cka-exercises

Temps cible : 8 minutes

ContexteNode worker1 en status NotReady
TâcheIdentifier la cause et réparer le node
SimulationSur worker1 : systemctl stop kubelet
Validationkubectl get nodes montre worker1 Ready
Solution
Fenêtre de terminal
# Diagnostic depuis le control plane
kubectl get nodes
kubectl describe node worker1 | grep -A5 Conditions
# SSH vers le worker
ssh worker1
# Vérifier kubelet
systemctl status kubelet
journalctl -u kubelet --since "5 minutes ago" | tail -20
# Réparer
systemctl start kubelet
systemctl status kubelet
# Retour sur le control plane
exit
kubectl get nodes

Temps cible : 4 minutes

ContexteService broken-svc avec selector app=myapp, Pod avec label application=myapp
TâcheDiagnostiquer pourquoi le Service n’a pas d’endpoints et corriger
Validationkubectl get endpoints broken-svc montre une IP
Solution
Fenêtre de terminal
# Créer le problème
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: broken-svc
namespace: cka-exercises
spec:
selector:
app: myapp
ports:
- port: 80
---
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
namespace: cka-exercises
labels:
application: myapp
spec:
containers:
- name: nginx
image: nginx
EOF
# Diagnostic
kubectl get endpoints broken-svc -n cka-exercises
kubectl describe svc broken-svc -n cka-exercises | grep Selector
kubectl get pod myapp-pod -n cka-exercises --show-labels
# Correction (labels ne matchent pas)
kubectl label pod myapp-pod -n cka-exercises app=myapp
# Validation
kubectl get endpoints broken-svc -n cka-exercises

Temps cible : 8 minutes

ContexteNode worker ne rejoint pas le cluster
TâcheLe kubelet ne démarre pas. Identifier la cause et réparer
IndicesVérifier les logs kubelet avec journalctl -u kubelet
Validationkubectl get nodes montre le node Ready
Solution
Fenêtre de terminal
# Sur le worker - vérifier le status
systemctl status kubelet
# Voir les logs
journalctl -u kubelet --no-pager | tail -50
# Causes courantes et corrections :
# 1. Fichier de config manquant
ls -la /var/lib/kubelet/config.yaml
ls -la /etc/kubernetes/kubelet.conf
# 2. Container runtime non démarré
systemctl status containerd
systemctl start containerd
# 3. Swap activé (interdit par défaut)
swapoff -a
# 4. Erreur de configuration
cat /var/lib/kubelet/config.yaml
# Redémarrer kubelet après correction
systemctl restart kubelet
systemctl status kubelet

Temps cible : 10 minutes

Contextekubectl retourne connection refused
TâcheL’API Server ne répond pas. Diagnostiquer et réparer
SimulationModifier /etc/kubernetes/manifests/kube-apiserver.yaml pour introduire une erreur
Validationkubectl get nodes fonctionne
Solution
Fenêtre de terminal
# 1. Vérifier si le container tourne
crictl ps | grep kube-apiserver
# Si vide → le pod static n'a pas démarré
# 2. Vérifier les logs du container mort
crictl ps -a | grep kube-apiserver
crictl logs <container-id>
# Ou via les logs du pod static
ls /var/log/pods/kube-system_kube-apiserver-*/
cat /var/log/pods/kube-system_kube-apiserver-*/kube-apiserver/*.log | tail -50
# 3. Vérifier le manifest
cat /etc/kubernetes/manifests/kube-apiserver.yaml
# Erreurs courantes :
# - Port incorrect (--secure-port)
# - Chemin de certificat erroné (--tls-cert-file)
# - Typo dans une option
# 4. Corriger le manifest
vim /etc/kubernetes/manifests/kube-apiserver.yaml
# 5. Le kubelet redémarre automatiquement le pod static
# Attendre 30-60 secondes
sleep 30
# 6. Validation
kubectl get nodes

Point clé : Les composants du control plane sont des pods statiques. Modifier le manifest dans /etc/kubernetes/manifests/ déclenche un redémarrage automatique par le kubelet.


Temps cible : 5 minutes

ContexteLa résolution DNS ne fonctionne pas
TâcheLes pods CoreDNS sont en CrashLoopBackOff. Diagnostiquer et réparer
Validationkubectl run test --rm -it --image=busybox:1.36 -- nslookup kubernetes retourne une IP
Solution
Fenêtre de terminal
# Diagnostic
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=20
# Si crash loop, redémarrer le deployment
kubectl rollout restart deploy coredns -n kube-system
# Attendre que les pods soient Ready
kubectl get pods -n kube-system -l k8s-app=kube-dns -w
# Validation
kubectl run test --rm -it --image=busybox:1.36 -- nslookup kubernetes

Temps cible : 6 minutes

ContexteCoreDNS pods Running mais résolution DNS échoue
TâcheLe ConfigMap coredns contient une erreur. Identifier et corriger
Validationkubectl run test --rm -it --image=busybox:1.36 -- nslookup kubernetes retourne une IP
Solution
Fenêtre de terminal
# Vérifier le ConfigMap
kubectl get cm coredns -n kube-system -o yaml
# Chercher les erreurs de syntaxe dans le Corefile
# Erreurs courantes :
# - Bloc mal fermé
# - Directive inconnue
# - Mauvais format d'upstream
# Sauvegarder et corriger
kubectl get cm coredns -n kube-system -o yaml > coredns-backup.yaml
kubectl edit cm coredns -n kube-system
# Redémarrer CoreDNS pour appliquer
kubectl rollout restart deploy coredns -n kube-system
# Validation
kubectl run test --rm -it --image=busybox:1.36 -- nslookup kubernetes

Temps cible : 5 minutes

ContextePods CoreDNS Running mais le service kube-dns n’existe pas
TâcheRecréer le service kube-dns pour que la résolution fonctionne
ContrainteLe service doit cibler les pods CoreDNS et exposer les ports 53 TCP/UDP
Validationkubectl get endpoints kube-dns -n kube-system montre des IPs
Solution
Fenêtre de terminal
# Vérifier que le service n'existe pas
kubectl get svc kube-dns -n kube-system
# Recréer le service
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.96.0.10
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
EOF
# Validation
kubectl get svc,endpoints kube-dns -n kube-system
kubectl run test --rm -it --image=busybox:1.36 -- nslookup kubernetes

Point clé : L’IP du service kube-dns (souvent 10.96.0.10) est configurée dans le kubelet. Recréer le service avec la même IP cluster.


#ExerciceDomaineTempsType
1Upgrade clusterArchitecture15 minkubeadm
2Backup etcdArchitecture6 minkubeadm
3Restore etcdArchitecture10 minkubeadm
4Role et RoleBindingRBAC4 mingénérique
5ClusterRole nodesRBAC4 mingénérique
6ServiceAccountRBAC5 mingénérique
7Taints + placementScheduling6 mingénérique
8Node AffinityScheduling5 mingénérique
9Drain et CordonScheduling3 mingénérique
10Service ClusterIPNetworking3 mingénérique
11NetworkPolicy DenyNetworking4 mingénérique
12NetworkPolicy AllowNetworking6 mingénérique
13IngressNetworking5 mingénérique
14PV et PVCStorage7 mingénérique
15ImagePullBackOffTroubleshooting3 mingénérique
16Node NotReadyTroubleshooting8 minkubeadm
17Service endpointsTroubleshooting4 mingénérique
18kubelet crashTroubleshooting8 minkubeadm
19API Server downTroubleshooting10 minkubeadm
20CoreDNS crashTroubleshooting5 mingénérique
21CoreDNS configTroubleshooting6 mingénérique
22CoreDNS serviceTroubleshooting5 mingénérique

Temps total : ~132 minutes

L’examen CKA dure 2 heures (120 min). Si vous complétez ces exercices en moins de 110 minutes avec 80%+ de réussite, vous êtes prêt.


Fenêtre de terminal
kubectl delete ns cka-exercises
kubectl taint node worker1 env- 2>/dev/null
kubectl label node worker1 disktype- 2>/dev/null

  1. etcdutl pour restore (pas etcdctl restore déprécié)
  2. Upgrade vers la dernière patch (1.31.4), pas la .0
  3. serviceAccountName dans le YAML (pas --serviceaccount)
  4. Toleration ≠ placement garanti — ajouter nodeSelector
  5. PV cluster-scoped, PVC namespaced
  6. Pods statiques : modifier le manifest → redémarrage auto
  7. CoreDNS : 3 points de défaillance (pods, config, service)
  8. Alias CKA : k, kn, $do, $now

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn