
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.
Réflexes CKAD
Section intitulée « Réflexes CKAD »Configurez votre environnement avant de commencer :
# Alias essentielsalias k=kubectlalias 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étionsource <(kubectl completion bash)complete -F __start_kubectl k
# Namespace de travailkubectl create ns ckad-exerciseskn ckad-exercisesApplication Design & Build (20%)
Section intitulée « Application Design & Build (20%) »Exercice 1 : Pod multi-conteneurs (sidecar)
Section intitulée « Exercice 1 : Pod multi-conteneurs (sidecar) »Temps cible : 4 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Cré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 |
| Contrainte | Les deux conteneurs partagent un volume emptyDir monté sur /logs |
| Validation | kubectl logs log-processor -c reader affiche “Processing…” |
Solution
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: log-processor namespace: ckad-exercisesspec: 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
# Validationkubectl logs log-processor -c reader -n ckad-exercisesExercice 2 : Init Container
Section intitulée « Exercice 2 : Init Container »Temps cible : 3 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Cré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 |
| Contrainte | Volume emptyDir nommé config, monté sur /config (init) et /etc/app (web) |
| Validation | kubectl exec web-init -- cat /etc/app/app.conf retourne “ready=true” |
Solution
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: web-init namespace: ckad-exercisesspec: 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
# Validationkubectl exec web-init -n ckad-exercises -- cat /etc/app/app.confApplication Deployment (20%)
Section intitulée « Application Deployment (20%) »Exercice 3 : Deployment avec label explicite
Section intitulée « Exercice 3 : Deployment avec label explicite »Temps cible : 4 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Cré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 |
| Contrainte | Le label app=api doit être explicitement défini (pas le label auto app=api) |
| Validation | kubectl get pods -l app=api affiche 3 pods |
Solution
kubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata: name: api namespace: ckad-exercisesspec: replicas: 3 selector: matchLabels: app: api template: metadata: labels: app: api spec: containers: - name: nginx image: nginx:1.24 resources: requests: cpu: 100mEOF
# Validationkubectl get pods -l app=api -n ckad-exercisesPoint clé : kubectl create deploy génère le label app=<name> automatiquement, mais ici on le définit explicitement pour contrôler les selectors.
Exercice 4 : Rolling Update et Rollback
Section intitulée « Exercice 4 : Rolling Update et Rollback »Temps cible : 3 minutes
| Contexte | Deployment api créé à l’exercice 3 |
|---|---|
| Tâche | Mettre à jour l’image vers nginx:1.25 avec maxSurge=1 et maxUnavailable=0, puis rollback vers nginx:1.24 |
| Validation | kubectl get deploy api -o jsonpath='{.spec.template.spec.containers[0].image}' retourne nginx:1.24 |
Solution
# Appliquer la stratégiekubectl patch deploy api -n ckad-exercises -p '{"spec":{"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'
# Mise à jourkubectl set image deploy api nginx=nginx:1.25 -n ckad-exerciseskubectl rollout status deploy api -n ckad-exercises
# Rollbackkubectl rollout undo deploy api -n ckad-exercises
# Validationkubectl get deploy api -n ckad-exercises -o jsonpath='{.spec.template.spec.containers[0].image}'Exercice 5 : Horizontal Pod Autoscaler
Section intitulée « Exercice 5 : Horizontal Pod Autoscaler »Temps cible : 3 minutes
| Contexte | Deployment api avec requests CPU définies |
|---|---|
| Tâche | Créer un HPA api-hpa : min 2, max 10, target CPU 70% |
| Contrainte | Nécessite un Metrics Server opérationnel |
| Validation | kubectl get hpa api-hpa affiche le HPA |
Solution
kubectl autoscale deploy api -n ckad-exercises --name=api-hpa --min=2 --max=10 --cpu-percent=70
# Validationkubectl get hpa api-hpa -n ckad-exercisesPoint clé : Un HPA basé sur CPU nécessite des resources.requests.cpu sur les Pods et un Metrics Server fonctionnel.
Application Observability & Maintenance (15%)
Section intitulée « Application Observability & Maintenance (15%) »Exercice 6 : Probes complètes
Section intitulée « Exercice 6 : Probes complètes »Temps cible : 5 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Cré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) |
| Validation | kubectl describe pod health-check montre les 3 probes configurées |
Solution
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: health-check namespace: ckad-exercisesspec: 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: 2EOF
# Validationkubectl describe pod health-check -n ckad-exercises | grep -E "Liveness|Readiness|Startup"Exercice 7 : Debugging — ConfigMap manquant
Section intitulée « Exercice 7 : Debugging — ConfigMap manquant »Temps cible : 4 minutes
| Contexte | Pod broken-app ne démarre pas |
|---|---|
| Tâche | Diagnostiquer pourquoi le Pod reste en Pending/Error, créer la ressource manquante, et faire démarrer le Pod |
| Validation | kubectl get pod broken-app affiche Running |
Créez d’abord le Pod problématique :
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: broken-app namespace: ckad-exercisesspec: 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-settingsEOFSolution
# Diagnostickubectl describe pod broken-app -n ckad-exercises | tail -15# Event: configmap "app-settings" not found
# Créer le ConfigMap manquantkubectl 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 $nowkubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: broken-app namespace: ckad-exercisesspec: 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-settingsEOF
# Validationkubectl get pod broken-app -n ckad-exercisesEnvironment, Configuration & Security (25%)
Section intitulée « Environment, Configuration & Security (25%) »Exercice 8 : ConfigMap comme variables d’environnement
Section intitulée « Exercice 8 : ConfigMap comme variables d’environnement »Temps cible : 3 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Cré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 |
| Validation | kubectl logs backend affiche DB_HOST=mysql.default.svc |
Solution
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: v1kind: Podmetadata: name: backend namespace: ckad-exercisesspec: containers: - name: backend image: busybox:1.36 command: ['sh', '-c', 'env | grep DB_ && sleep 3600'] envFrom: - configMapRef: name: db-configEOF
# Validationkubectl logs backend -n ckad-exercises | grep DB_Exercice 9 : Secret comme fichier
Section intitulée « Exercice 9 : Secret comme fichier »Temps cible : 4 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Cré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 |
| Validation | kubectl exec api-client -- cat /etc/credentials/username retourne admin |
Solution
kubectl create secret generic api-creds -n ckad-exercises \ --from-literal=username=admin \ --from-literal=token=abc123xyz
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: api-client namespace: ckad-exercisesspec: 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-credsEOF
# Validationkubectl exec api-client -n ckad-exercises -- cat /etc/credentials/usernameExercice 10 : Requests et Limits
Section intitulée « Exercice 10 : Requests et Limits »Temps cible : 3 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Créer un Pod resource-pod (nginx:1.25) avec requests 100m CPU / 128Mi RAM et limits 200m CPU / 256Mi RAM |
| Validation | kubectl describe pod resource-pod montre les resources configurées |
Solution
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: resource-pod namespace: ckad-exercisesspec: containers: - name: nginx image: nginx:1.25 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 200m memory: 256MiEOF
# Validationkubectl 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
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Créer un ServiceAccount app-sa, puis un Pod sa-pod (nginx:1.25) qui utilise ce SA avec automountServiceAccountToken: false |
| Validation | kubectl exec sa-pod -- ls /var/run/secrets/kubernetes.io/serviceaccount échoue (répertoire inexistant) |
Solution
kubectl create sa app-sa -n ckad-exercises
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: sa-pod namespace: ckad-exercisesspec: serviceAccountName: app-sa automountServiceAccountToken: false containers: - name: nginx image: nginx:1.25EOF
# Validationkubectl exec sa-pod -n ckad-exercises -- ls /var/run/secrets/kubernetes.io/serviceaccount 2>&1 | grep -i "no such"Exercice 12 : SecurityContext
Section intitulée « Exercice 12 : SecurityContext »Temps cible : 4 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Créer un Pod secure-pod (busybox:1.36) avec runAsUser 1000, runAsGroup 3000, fsGroup 2000, et readOnlyRootFilesystem true |
| Validation | kubectl logs secure-pod affiche uid=1000 gid=3000 groups=2000 |
Solution
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata: name: secure-pod namespace: ckad-exercisesspec: securityContext: runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 containers: - name: secure image: busybox:1.36 command: ['sh', '-c', 'id && sleep 3600'] securityContext: readOnlyRootFilesystem: trueEOF
# Validationkubectl logs secure-pod -n ckad-exercisesExercice 13 : PersistentVolumeClaim
Section intitulée « Exercice 13 : PersistentVolumeClaim »Temps cible : 5 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Créer un PVC data-pvc (500Mi, RWO, StorageClass default), puis un Pod data-pod (nginx:1.25) qui monte ce PVC dans /data |
| Contrainte | Le StorageClass doit supporter le provisionnement dynamique (standard sur Kind/Minikube) |
| Validation | kubectl get pvc data-pvc montre Bound |
Solution
kubectl apply -f - <<'EOF'apiVersion: v1kind: PersistentVolumeClaimmetadata: name: data-pvc namespace: ckad-exercisesspec: accessModes: - ReadWriteOnce resources: requests: storage: 500Mi---apiVersion: v1kind: Podmetadata: name: data-pod namespace: ckad-exercisesspec: containers: - name: nginx image: nginx:1.25 volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName: data-pvcEOF
# Validationkubectl get pvc data-pvc -n ckad-exerciseskubectl exec data-pod -n ckad-exercises -- df -h /dataServices & Networking (20%)
Section intitulée « Services & Networking (20%) »Exercice 14 : Service ClusterIP
Section intitulée « Exercice 14 : Service ClusterIP »Temps cible : 2 minutes
| Contexte | Deployment api avec label app=api |
|---|---|
| Tâche | Créer un Service api-svc de type ClusterIP exposant le Deployment sur port 8080 → port 80 des Pods |
| Contrainte | Le selector doit être app=api |
| Validation | kubectl get endpoints api-svc montre des IPs |
Solution
kubectl apply -f - <<'EOF'apiVersion: v1kind: Servicemetadata: name: api-svc namespace: ckad-exercisesspec: selector: app: api ports: - port: 8080 targetPort: 80EOF
# Validationkubectl get svc,endpoints api-svc -n ckad-exercisesExercice 15 : Service NodePort
Section intitulée « Exercice 15 : Service NodePort »Temps cible : 3 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Créer un nouveau Deployment web (2 replicas, nginx:1.25, label app=web) et un Service NodePort web-np sur port 30080 |
| Validation | kubectl get svc web-np montre type NodePort avec port 30080 |
Solution
kubectl create deploy web --image=nginx:1.25 --replicas=2 -n ckad-exercises
kubectl apply -f - <<'EOF'apiVersion: v1kind: Servicemetadata: name: web-np namespace: ckad-exercisesspec: type: NodePort selector: app: web ports: - port: 80 targetPort: 80 nodePort: 30080EOF
# Validationkubectl get svc web-np -n ckad-exercisesExercice 16 : Ingress
Section intitulée « Exercice 16 : Ingress »Temps cible : 4 minutes
| Contexte | Service web-np exposant le Deployment web |
|---|---|
| Tâche | Créer un Ingress web-ingress routant web.example.com/ vers le Service web-np port 80 |
| Contrainte | Utiliser ingressClassName: nginx |
| Validation | kubectl get ingress web-ingress montre l’Ingress créé |
Solution
kubectl apply -f - <<'EOF'apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: web-ingress namespace: ckad-exercisesspec: ingressClassName: nginx rules: - host: web.example.com http: paths: - path: / pathType: Prefix backend: service: name: web-np port: number: 80EOF
# Validationkubectl get ingress web-ingress -n ckad-exercisesNote : L’API Ingress reste pertinente pour la CKAD, même si l’écosystème évolue vers Gateway API.
Exercice 17 : NetworkPolicy — Allow specific
Section intitulée « Exercice 17 : NetworkPolicy — Allow specific »Temps cible : 5 minutes
| Contexte | Deployment api avec label app=api |
|---|---|
| Tâche | Créer une NetworkPolicy api-policy autorisant le trafic ingress sur port 80 uniquement depuis les Pods avec label role=frontend |
| Contrainte | Le CNI doit supporter les NetworkPolicies |
| Validation | kubectl describe netpol api-policy montre la règle |
Solution
kubectl apply -f - <<'EOF'apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: api-policy namespace: ckad-exercisesspec: podSelector: matchLabels: app: api policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: role: frontend ports: - protocol: TCP port: 80EOF
# Testkubectl run frontend --image=busybox:1.36 -n ckad-exercises \ --labels="role=frontend" --rm -it -- wget -qO- --timeout=2 api-svc:8080
# Validationkubectl describe netpol api-policy -n ckad-exercisesBatch Jobs (partie de Design & Build)
Section intitulée « Batch Jobs (partie de Design & Build) »Exercice 18 : Job avec parallélisme
Section intitulée « Exercice 18 : Job avec parallélisme »Temps cible : 3 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Cré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 |
| Validation | kubectl get job batch-job montre 5/5 completions |
Solution
kubectl apply -f - <<'EOF'apiVersion: batch/v1kind: Jobmetadata: name: batch-job namespace: ckad-exercisesspec: completions: 5 parallelism: 2 template: spec: containers: - name: worker image: busybox:1.36 command: ['sh', '-c', 'echo "Task done" && sleep 5'] restartPolicy: NeverEOF
# Observerkubectl get pods -n ckad-exercises -l job-name=batch-job -wExercice 19 : CronJob
Section intitulée « Exercice 19 : CronJob »Temps cible : 3 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Cré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)" |
| Validation | kubectl get cronjob cleanup-job montre le schedule */5 * * * * |
Solution
kubectl apply -f - <<'EOF'apiVersion: batch/v1kind: CronJobmetadata: name: cleanup-job namespace: ckad-exercisesspec: 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: OnFailureEOF
# Validationkubectl get cronjob cleanup-job -n ckad-exercisesExercice 20 : Blue-Green avec Services
Section intitulée « Exercice 20 : Blue-Green avec Services »Temps cible : 5 minutes
| Contexte | Namespace ckad-exercises |
|---|---|
| Tâche | Cré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 |
| Validation | Après bascule, kubectl get endpoints app-prod montre les IPs des Pods green |
Solution
# Créer les deux versionskubectl create deploy app-blue --image=nginx:1.24 --replicas=2 -n ckad-exerciseskubectl label deploy app-blue -n ckad-exercises version=bluekubectl 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-exerciseskubectl label deploy app-green -n ckad-exercises version=greenkubectl patch deploy app-green -n ckad-exercises -p '{"spec":{"template":{"metadata":{"labels":{"version":"green"}}}}}'
# Service pointant vers bluekubectl apply -f - <<'EOF'apiVersion: v1kind: Servicemetadata: name: app-prod namespace: ckad-exercisesspec: selector: version: blue ports: - port: 80EOF
# Vérifierkubectl get endpoints app-prod -n ckad-exercises
# Basculer vers greenkubectl patch svc app-prod -n ckad-exercises -p '{"spec":{"selector":{"version":"green"}}}'
# Validationkubectl get endpoints app-prod -n ckad-exercisesRécapitulatif des temps
Section intitulée « Récapitulatif des temps »| # | Exercice | Domaine | Temps | Indépendant |
|---|---|---|---|---|
| 1 | Multi-conteneurs | Design & Build | 4 min | ✅ |
| 2 | Init Container | Design & Build | 3 min | ✅ |
| 3 | Deployment labels | Deployment | 4 min | ✅ |
| 4 | Rolling Update/Rollback | Deployment | 3 min | ⚠️ Ex.3 |
| 5 | HPA | Deployment | 3 min | ⚠️ Ex.3 |
| 6 | Probes | Observability | 5 min | ✅ |
| 7 | Debugging | Observability | 4 min | ✅ |
| 8 | ConfigMap env | Environment | 3 min | ✅ |
| 9 | Secret fichier | Environment | 4 min | ✅ |
| 10 | Resources | Environment | 3 min | ✅ |
| 11 | ServiceAccount | Environment | 4 min | ✅ |
| 12 | SecurityContext | Environment | 4 min | ✅ |
| 13 | PVC | Environment | 5 min | ✅ |
| 14 | Service ClusterIP | Networking | 2 min | ⚠️ Ex.3 |
| 15 | Service NodePort | Networking | 3 min | ✅ |
| 16 | Ingress | Networking | 4 min | ⚠️ Ex.15 |
| 17 | NetworkPolicy | Networking | 5 min | ⚠️ Ex.3 |
| 18 | Job | Batch | 3 min | ✅ |
| 19 | CronJob | Batch | 3 min | ✅ |
| 20 | Blue-Green | Deployment | 5 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.
Nettoyage
Section intitulée « Nettoyage »kubectl delete ns ckad-exercisesÀ retenir
Section intitulée « À retenir »- Labels cohérents : Un label unique (
app=api) pour Deployment, Service, NetworkPolicy - Requests CPU obligatoires pour HPA
kubectl explainpour trouver la syntaxe rapidement$do=--dry-run=client -o yamlpour générer des YAML- Init containers : exécutés séquentiellement avant les containers principaux
- Probes : startup → readiness → liveness (ordre de vérification)
- PVC : provisionnement dynamique avec StorageClass default
- NetworkPolicy : nécessite un CNI compatible (pas Flannel seul)