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

Exercices CKAD — Entraînement chronométré

27 min de lecture

logo kubernetes

Entraînement brutal, précis, rapide. Ces exercices reproduisent les types de tâches et les patterns récurrents de la CKAD. Chronométrez-vous et 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'
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 ckad-exercises
kn ckad-exercises

Temps cible : 4 minutes

ContexteNamespace ckad-exercises
TâcheCréer un Pod log-processor avec un conteneur app (busybox:1.36) qui écrit “Processing…” dans /logs/app.log toutes les 5s, et un sidecar reader qui lit ce fichier en continu
ContrainteLes deux conteneurs partagent un volume emptyDir monté sur /logs
Validationkubectl logs log-processor -c reader affiche “Processing…”
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: log-processor
namespace: ckad-exercises
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'while true; do echo "Processing..." >> /logs/app.log; sleep 5; done']
volumeMounts:
- name: logs
mountPath: /logs
- name: reader
image: busybox:1.36
command: ['sh', '-c', 'tail -f /logs/app.log']
volumeMounts:
- name: logs
mountPath: /logs
volumes:
- name: logs
emptyDir: {}
EOF
# Validation
kubectl logs log-processor -c reader -n ckad-exercises

Temps cible : 3 minutes

ContexteNamespace ckad-exercises
TâcheCréer un Pod web-init avec un init container init-config qui crée /config/app.conf contenant “ready=true”, puis un conteneur web (nginx:1.25) qui monte ce volume
ContrainteVolume emptyDir nommé config, monté sur /config (init) et /etc/app (web)
Validationkubectl exec web-init -- cat /etc/app/app.conf retourne “ready=true”
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: web-init
namespace: ckad-exercises
spec:
initContainers:
- name: init-config
image: busybox:1.36
command: ['sh', '-c', 'echo "ready=true" > /config/app.conf']
volumeMounts:
- name: config
mountPath: /config
containers:
- name: web
image: nginx:1.25
volumeMounts:
- name: config
mountPath: /etc/app
volumes:
- name: config
emptyDir: {}
EOF
# Validation
kubectl exec web-init -n ckad-exercises -- cat /etc/app/app.conf

Temps cible : 4 minutes

ContexteNamespace ckad-exercises
TâcheCréer un Deployment api avec 3 replicas, image nginx:1.24, et le label app=api sur les Pods. Ajouter des requests CPU (100m) pour préparer le HPA
ContrainteLe label app=api doit être explicitement défini (pas le label auto app=api)
Validationkubectl get pods -l app=api affiche 3 pods
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: ckad-exercises
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: nginx
image: nginx:1.24
resources:
requests:
cpu: 100m
EOF
# Validation
kubectl get pods -l app=api -n ckad-exercises

Point clé : kubectl create deploy génère le label app=<name> automatiquement, mais ici on le définit explicitement pour contrôler les selectors.


Temps cible : 3 minutes

ContexteDeployment api créé à l’exercice 3
TâcheMettre à jour l’image vers nginx:1.25 avec maxSurge=1 et maxUnavailable=0, puis rollback vers nginx:1.24
Validationkubectl get deploy api -o jsonpath='{.spec.template.spec.containers[0].image}' retourne nginx:1.24
Solution
Fenêtre de terminal
# Appliquer la stratégie
kubectl patch deploy api -n ckad-exercises -p '{"spec":{"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'
# Mise à jour
kubectl set image deploy api nginx=nginx:1.25 -n ckad-exercises
kubectl rollout status deploy api -n ckad-exercises
# Rollback
kubectl rollout undo deploy api -n ckad-exercises
# Validation
kubectl get deploy api -n ckad-exercises -o jsonpath='{.spec.template.spec.containers[0].image}'

Temps cible : 3 minutes

ContexteDeployment api avec requests CPU définies
TâcheCréer un HPA api-hpa : min 2, max 10, target CPU 70%
ContrainteNécessite un Metrics Server opérationnel
Validationkubectl get hpa api-hpa affiche le HPA
Solution
Fenêtre de terminal
kubectl autoscale deploy api -n ckad-exercises --name=api-hpa --min=2 --max=10 --cpu-percent=70
# Validation
kubectl get hpa api-hpa -n ckad-exercises

Point clé : Un HPA basé sur CPU nécessite des resources.requests.cpu sur les Pods et un Metrics Server fonctionnel.


Temps cible : 5 minutes

ContexteNamespace ckad-exercises
TâcheCréer un Pod health-check (nginx:1.25) avec liveness (HTTP GET /, délai 10s, période 5s), readiness (délai 5s, période 3s), startup (failureThreshold 30, période 2s)
Validationkubectl describe pod health-check montre les 3 probes configurées
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: health-check
namespace: ckad-exercises
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 3
startupProbe:
httpGet:
path: /
port: 80
failureThreshold: 30
periodSeconds: 2
EOF
# Validation
kubectl describe pod health-check -n ckad-exercises | grep -E "Liveness|Readiness|Startup"

Temps cible : 4 minutes

ContextePod broken-app ne démarre pas
TâcheDiagnostiquer pourquoi le Pod reste en Pending/Error, créer la ressource manquante, et faire démarrer le Pod
Validationkubectl get pod broken-app affiche Running

Créez d’abord le Pod problématique :

Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: broken-app
namespace: ckad-exercises
spec:
containers:
- name: app
image: nginx:1.25
command: ['sh', '-c', 'cat /config/settings.conf && nginx -g "daemon off;"']
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: app-settings
EOF
Solution
Fenêtre de terminal
# Diagnostic
kubectl describe pod broken-app -n ckad-exercises | tail -15
# Event: configmap "app-settings" not found
# Créer le ConfigMap manquant
kubectl create cm app-settings -n ckad-exercises --from-literal=settings.conf="debug=true"
# Recréer le Pod (le Pod existant reste bloqué)
kubectl delete pod broken-app -n ckad-exercises $now
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: broken-app
namespace: ckad-exercises
spec:
containers:
- name: app
image: nginx:1.25
command: ['sh', '-c', 'cat /config/settings.conf && nginx -g "daemon off;"']
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: app-settings
EOF
# Validation
kubectl get pod broken-app -n ckad-exercises

Exercice 8 : ConfigMap comme variables d’environnement

Section intitulée « Exercice 8 : ConfigMap comme variables d’environnement »

Temps cible : 3 minutes

ContexteNamespace ckad-exercises
TâcheCréer un ConfigMap db-config avec DB_HOST=mysql.default.svc et DB_PORT=3306, puis un Pod backend (busybox:1.36) qui injecte ces valeurs comme variables d’environnement
Validationkubectl logs backend affiche DB_HOST=mysql.default.svc
Solution
Fenêtre de terminal
kubectl create cm db-config -n ckad-exercises \
--from-literal=DB_HOST=mysql.default.svc \
--from-literal=DB_PORT=3306
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: backend
namespace: ckad-exercises
spec:
containers:
- name: backend
image: busybox:1.36
command: ['sh', '-c', 'env | grep DB_ && sleep 3600']
envFrom:
- configMapRef:
name: db-config
EOF
# Validation
kubectl logs backend -n ckad-exercises | grep DB_

Temps cible : 4 minutes

ContexteNamespace ckad-exercises
TâcheCréer un Secret api-creds avec username=admin et token=abc123xyz, puis un Pod api-client qui monte ce Secret dans /etc/credentials en lecture seule
Validationkubectl exec api-client -- cat /etc/credentials/username retourne admin
Solution
Fenêtre de terminal
kubectl create secret generic api-creds -n ckad-exercises \
--from-literal=username=admin \
--from-literal=token=abc123xyz
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: api-client
namespace: ckad-exercises
spec:
containers:
- name: client
image: busybox:1.36
command: ['sh', '-c', 'ls /etc/credentials && sleep 3600']
volumeMounts:
- name: creds
mountPath: /etc/credentials
readOnly: true
volumes:
- name: creds
secret:
secretName: api-creds
EOF
# Validation
kubectl exec api-client -n ckad-exercises -- cat /etc/credentials/username

Temps cible : 3 minutes

ContexteNamespace ckad-exercises
TâcheCréer un Pod resource-pod (nginx:1.25) avec requests 100m CPU / 128Mi RAM et limits 200m CPU / 256Mi RAM
Validationkubectl describe pod resource-pod montre les resources configurées
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: resource-pod
namespace: ckad-exercises
spec:
containers:
- name: nginx
image: nginx:1.25
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
EOF
# Validation
kubectl describe pod resource-pod -n ckad-exercises | grep -A4 "Limits\|Requests"

Exercice 11 : ServiceAccount sans token auto-monté

Section intitulée « Exercice 11 : ServiceAccount sans token auto-monté »

Temps cible : 4 minutes

ContexteNamespace ckad-exercises
TâcheCréer un ServiceAccount app-sa, puis un Pod sa-pod (nginx:1.25) qui utilise ce SA avec automountServiceAccountToken: false
Validationkubectl exec sa-pod -- ls /var/run/secrets/kubernetes.io/serviceaccount échoue (répertoire inexistant)
Solution
Fenêtre de terminal
kubectl create sa app-sa -n ckad-exercises
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: sa-pod
namespace: ckad-exercises
spec:
serviceAccountName: app-sa
automountServiceAccountToken: false
containers:
- name: nginx
image: nginx:1.25
EOF
# Validation
kubectl exec sa-pod -n ckad-exercises -- ls /var/run/secrets/kubernetes.io/serviceaccount 2>&1 | grep -i "no such"

Temps cible : 4 minutes

ContexteNamespace ckad-exercises
TâcheCréer un Pod secure-pod (busybox:1.36) avec runAsUser 1000, runAsGroup 3000, fsGroup 2000, et readOnlyRootFilesystem true
Validationkubectl logs secure-pod affiche uid=1000 gid=3000 groups=2000
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: ckad-exercises
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: secure
image: busybox:1.36
command: ['sh', '-c', 'id && sleep 3600']
securityContext:
readOnlyRootFilesystem: true
EOF
# Validation
kubectl logs secure-pod -n ckad-exercises

Temps cible : 5 minutes

ContexteNamespace ckad-exercises
TâcheCréer un PVC data-pvc (500Mi, RWO, StorageClass default), puis un Pod data-pod (nginx:1.25) qui monte ce PVC dans /data
ContrainteLe StorageClass doit supporter le provisionnement dynamique (standard sur Kind/Minikube)
Validationkubectl get pvc data-pvc montre Bound
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
namespace: ckad-exercises
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
---
apiVersion: v1
kind: Pod
metadata:
name: data-pod
namespace: ckad-exercises
spec:
containers:
- name: nginx
image: nginx:1.25
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc
EOF
# Validation
kubectl get pvc data-pvc -n ckad-exercises
kubectl exec data-pod -n ckad-exercises -- df -h /data

Temps cible : 2 minutes

ContexteDeployment api avec label app=api
TâcheCréer un Service api-svc de type ClusterIP exposant le Deployment sur port 8080 → port 80 des Pods
ContrainteLe selector doit être app=api
Validationkubectl get endpoints api-svc montre des IPs
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: api-svc
namespace: ckad-exercises
spec:
selector:
app: api
ports:
- port: 8080
targetPort: 80
EOF
# Validation
kubectl get svc,endpoints api-svc -n ckad-exercises

Temps cible : 3 minutes

ContexteNamespace ckad-exercises
TâcheCréer un nouveau Deployment web (2 replicas, nginx:1.25, label app=web) et un Service NodePort web-np sur port 30080
Validationkubectl get svc web-np montre type NodePort avec port 30080
Solution
Fenêtre de terminal
kubectl create deploy web --image=nginx:1.25 --replicas=2 -n ckad-exercises
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: web-np
namespace: ckad-exercises
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 80
nodePort: 30080
EOF
# Validation
kubectl get svc web-np -n ckad-exercises

Temps cible : 4 minutes

ContexteService web-np exposant le Deployment web
TâcheCréer un Ingress web-ingress routant web.example.com/ vers le Service web-np 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: ckad-exercises
spec:
ingressClassName: nginx
rules:
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-np
port:
number: 80
EOF
# Validation
kubectl get ingress web-ingress -n ckad-exercises

Note : L’API Ingress reste pertinente pour la CKAD, même si l’écosystème évolue vers Gateway API.


Temps cible : 5 minutes

ContexteDeployment api avec label app=api
TâcheCréer une NetworkPolicy api-policy autorisant le trafic ingress sur port 80 uniquement depuis les Pods avec label role=frontend
ContrainteLe CNI doit supporter les NetworkPolicies
Validationkubectl describe netpol api-policy montre la règle
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
namespace: ckad-exercises
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 80
EOF
# Test
kubectl run frontend --image=busybox:1.36 -n ckad-exercises \
--labels="role=frontend" --rm -it -- wget -qO- --timeout=2 api-svc:8080
# Validation
kubectl describe netpol api-policy -n ckad-exercises

Temps cible : 3 minutes

ContexteNamespace ckad-exercises
TâcheCréer un Job batch-job exécutant 5 tâches (completions), max 2 en parallèle, image busybox:1.36, commande echo "Task done" && sleep 5
Validationkubectl get job batch-job montre 5/5 completions
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
namespace: ckad-exercises
spec:
completions: 5
parallelism: 2
template:
spec:
containers:
- name: worker
image: busybox:1.36
command: ['sh', '-c', 'echo "Task done" && sleep 5']
restartPolicy: Never
EOF
# Observer
kubectl get pods -n ckad-exercises -l job-name=batch-job -w

Temps cible : 3 minutes

ContexteNamespace ckad-exercises
TâcheCréer un CronJob cleanup-job qui s’exécute toutes les 5 minutes, conserve 3 jobs réussis et 1 échoué, commande echo "Cleanup at $(date)"
Validationkubectl get cronjob cleanup-job montre le schedule */5 * * * *
Solution
Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: batch/v1
kind: CronJob
metadata:
name: cleanup-job
namespace: ckad-exercises
spec:
schedule: "*/5 * * * *"
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
containers:
- name: cleanup
image: busybox:1.36
command: ['sh', '-c', 'echo "Cleanup at $(date)"']
restartPolicy: OnFailure
EOF
# Validation
kubectl get cronjob cleanup-job -n ckad-exercises

Temps cible : 5 minutes

ContexteNamespace ckad-exercises
TâcheCréer deux Deployments app-blue (nginx:1.24, label version=blue) et app-green (nginx:1.25, label version=green), puis un Service app-prod pointant vers blue. Basculer vers green
ValidationAprès bascule, kubectl get endpoints app-prod montre les IPs des Pods green
Solution
Fenêtre de terminal
# Créer les deux versions
kubectl create deploy app-blue --image=nginx:1.24 --replicas=2 -n ckad-exercises
kubectl label deploy app-blue -n ckad-exercises version=blue
kubectl patch deploy app-blue -n ckad-exercises -p '{"spec":{"template":{"metadata":{"labels":{"version":"blue"}}}}}'
kubectl create deploy app-green --image=nginx:1.25 --replicas=2 -n ckad-exercises
kubectl label deploy app-green -n ckad-exercises version=green
kubectl patch deploy app-green -n ckad-exercises -p '{"spec":{"template":{"metadata":{"labels":{"version":"green"}}}}}'
# Service pointant vers blue
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: app-prod
namespace: ckad-exercises
spec:
selector:
version: blue
ports:
- port: 80
EOF
# Vérifier
kubectl get endpoints app-prod -n ckad-exercises
# Basculer vers green
kubectl patch svc app-prod -n ckad-exercises -p '{"spec":{"selector":{"version":"green"}}}'
# Validation
kubectl get endpoints app-prod -n ckad-exercises

#ExerciceDomaineTempsIndépendant
1Multi-conteneursDesign & Build4 min
2Init ContainerDesign & Build3 min
3Deployment labelsDeployment4 min
4Rolling Update/RollbackDeployment3 min⚠️ Ex.3
5HPADeployment3 min⚠️ Ex.3
6ProbesObservability5 min
7DebuggingObservability4 min
8ConfigMap envEnvironment3 min
9Secret fichierEnvironment4 min
10ResourcesEnvironment3 min
11ServiceAccountEnvironment4 min
12SecurityContextEnvironment4 min
13PVCEnvironment5 min
14Service ClusterIPNetworking2 min⚠️ Ex.3
15Service NodePortNetworking3 min
16IngressNetworking4 min⚠️ Ex.15
17NetworkPolicyNetworking5 min⚠️ Ex.3
18JobBatch3 min
19CronJobBatch3 min
20Blue-GreenDeployment5 min

Temps total : ~74 minutes

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


Fenêtre de terminal
kubectl delete ns ckad-exercises

  1. Labels cohérents : Un label unique (app=api) pour Deployment, Service, NetworkPolicy
  2. Requests CPU obligatoires pour HPA
  3. kubectl explain pour trouver la syntaxe rapidement
  4. $do = --dry-run=client -o yaml pour générer des YAML
  5. Init containers : exécutés séquentiellement avant les containers principaux
  6. Probes : startup → readiness → liveness (ordre de vérification)
  7. PVC : provisionnement dynamique avec StorageClass default
  8. NetworkPolicy : nécessite un CNI compatible (pas Flannel seul)

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