CKA - Domaine 4 : Workloads et Scheduling (15%)
🎯 Objectifs
À la fin de ce module, tu seras capable de :
- ✅ Gérer des Deployments : rolling update, rollback, scaling
- ✅ Utiliser ConfigMaps et Secrets comme variables d'environnement et volumes
- ✅ Configurer les ressources (requests/limits) et comprendre leur impact
- ✅ Contrôler le scheduling avec Taints, Tolerations et Node Affinity
- ✅ Créer des DaemonSets, StatefulSets, Jobs et CronJobs
- ✅ Configurer le HPA et les Probes
📋 Prérequis
- Modules Kubernetes Débutant et Avancé complétés
- Module CKA : Architecture lu
🚀 Deployments
Rolling update et rollback
# Déployer
kubectl create deployment nginx --image=nginx:1.27 --replicas=3
# Mettre à jour l'image
kubectl set image deployment/nginx nginx=nginx:1.28
# Suivre le rollout
kubectl rollout status deployment/nginx
# Historique
kubectl rollout history deployment/nginx
kubectl rollout history deployment/nginx --revision=2
# Rollback vers la révision précédente
kubectl rollout undo deployment/nginx
# Rollback vers une révision spécifique
kubectl rollout undo deployment/nginx --to-revision=1
# Mettre en pause un rollout
kubectl rollout pause deployment/nginx
kubectl rollout resume deployment/nginxStratégie de mise à jour
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # pods supplémentaires autorisés pendant la MAJ
maxUnavailable: 0 # pods indisponibles autorisés pendant la MAJ
template:
spec:
containers:
- name: nginx
image: nginx:1.28💡
maxUnavailable: 0garantit qu'aucun pod n'est supprimé avant qu'un nouveau soit prêt. Idéal en production.
Générer le YAML rapidement
kubectl create deployment nginx \
--image=nginx:1.27 \
--replicas=3 \
--dry-run=client -o yaml > deployment.yaml
kubectl apply -f deployment.yaml⚙️ ConfigMaps
Créer un ConfigMap
# Depuis des valeurs littérales
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--from-literal=APP_PORT=8080
# Depuis un fichier
kubectl create configmap nginx-conf \
--from-file=nginx.conf
# YAML
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--dry-run=client -o yaml > configmap.yamlUtiliser un ConfigMap
# En tant que variables d'environnement (toutes les clés)
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: app-config
# En tant que variable individuelle
spec:
containers:
- name: app
env:
- name: MY_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
# En tant que volume (fichiers montés)
spec:
volumes:
- name: config-vol
configMap:
name: nginx-conf
containers:
- name: nginx
volumeMounts:
- name: config-vol
mountPath: /etc/nginx/conf.d🔒 Secrets
Créer un Secret
# Générique (opaque)
kubectl create secret generic db-creds \
--from-literal=DB_USER=admin \
--from-literal=DB_PASS=s3cur3
# TLS
kubectl create secret tls tls-secret \
--cert=tls.crt \
--key=tls.key
# Docker registry
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=password# YAML : les valeurs doivent être en base64
# echo -n "admin" | base64 → YWRtaW4=
apiVersion: v1
kind: Secret
metadata:
name: db-creds
type: Opaque
data:
DB_USER: YWRtaW4=
DB_PASS: czNjdXIzUtiliser un Secret
# En tant que variables d'environnement
spec:
containers:
- name: app
envFrom:
- secretRef:
name: db-creds
# Variable individuelle
spec:
containers:
- name: app
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-creds
key: DB_PASS
# En tant que volume
spec:
volumes:
- name: secret-vol
secret:
secretName: db-creds
containers:
- name: app
volumeMounts:
- name: secret-vol
mountPath: /etc/secrets
readOnly: true⚠️ Les Secrets sont encodés en base64, pas chiffrés. N'oublie pas d'activer l'encryption at rest en production (EncryptionConfiguration).
📐 Resource Requests et Limits
Pourquoi c'est important
- Les requests sont utilisées par le scheduler pour placer le Pod sur un node avec assez de ressources.
- Les limits empêchent un Pod de consommer plus que sa part.
spec:
containers:
- name: app
resources:
requests:
memory: "128Mi" # le scheduler réserve 128 Mo
cpu: "250m" # réserve 0.25 CPU
limits:
memory: "256Mi" # tué (OOMKilled) si dépassé
cpu: "500m" # ralenti (throttled) si dépasséComportements à retenir
| Scénario | CPU | Mémoire |
|---|---|---|
| Dépasse la limit | Throttled (ralenti) | OOMKilled (tué) |
| Dépasse la request | Autorisé si node disponible | Autorisé si node disponible |
| Pas de request défini | = valeur de la limit | = valeur de la limit |
# Voir l'utilisation actuelle
kubectl top pods -n <namespace>
kubectl top nodesLimitRange (pour forcer des defaults dans un namespace)
apiVersion: v1
kind: LimitRange
metadata:
name: limits
namespace: default
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "128Mi"
defaultRequest:
cpu: "250m"
memory: "64Mi"ResourceQuota (pour limiter un namespace entier)
apiVersion: v1
kind: ResourceQuota
metadata:
name: namespace-quota
namespace: default
spec:
hard:
pods: "10"
requests.cpu: "4"
requests.memory: "4Gi"
limits.cpu: "8"
limits.memory: "8Gi"🎯 Scheduling : Taints et Tolerations
Concept
- Une Taint sur un node repousse les Pods.
- Une Toleration sur un Pod lui permet de tolérer une Taint.
# Ajouter une taint
kubectl taint nodes worker1 env=production:NoSchedule
kubectl taint nodes worker1 env=production:NoExecute # expulse aussi les pods existants
kubectl taint nodes worker1 env=production:PreferNoSchedule
# Supprimer une taint (ajouter un "-" à la fin)
kubectl taint nodes worker1 env=production:NoSchedule-
# Voir les taints d'un node
kubectl describe node worker1 | grep -A5 Taints# Toleration correspondante dans le Pod
spec:
tolerations:
- key: "env"
operator: "Equal"
value: "production"
effect: "NoSchedule"
# Tolérer toutes les taints (utile pour les DaemonSets)
tolerations:
- operator: "Exists"💡 Les DaemonSets de kube-system ont souvent des
tolerationspour toutes les taints, ce qui leur permet de tourner sur tous les nodes y compris ceux qui sont "réservés".
📍 Node Affinity
Plus expressif que nodeSelector, Node Affinity permet des conditions complexes.
# Labelliser un node
kubectl label nodes worker1 disktype=ssd
kubectl label nodes worker2 disktype=hddspec:
affinity:
nodeAffinity:
# REQUIRED : le Pod ne sera placé que sur un node correspondant
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In # In, NotIn, Exists, DoesNotExist, Gt, Lt
values:
- ssd
# PREFERRED : préférence, mais pas obligatoire
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: zone
operator: In
values:
- eu-west-1aPod Affinity / Anti-Affinity
spec:
affinity:
# Co-localiser ce Pod avec des Pods ayant app=cache
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: cache
topologyKey: kubernetes.io/hostname
# Ne pas placer ce Pod sur un node qui a déjà app=web
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web
topologyKey: kubernetes.io/hostname🌐 DaemonSets
Un DaemonSet garantit qu'un Pod tourne sur chaque node (ou un sous-ensemble). Utilisé pour les agents de monitoring, les log collectors, les plugins réseau CNI.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
tolerations:
- operator: Exists # tolère toutes les taints pour tourner partout
containers:
- name: fluentd
image: fluent/fluentd:v1.16kubectl get daemonset
kubectl describe daemonset fluentd🗄️ StatefulSets
Pour les applications qui ont besoin :
- D'une identité stable (nom de pod prévisible :
mysql-0,mysql-1...) - D'un stockage persistant propre à chaque pod
- D'un ordre de démarrage garanti
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: "mysql" # headless service requis
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates: # PVC créé automatiquement pour chaque pod
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi⏱️ Jobs et CronJobs
Job
Un Job exécute un Pod jusqu'à complétion (ne redémarre pas une fois terminé avec succès).
kubectl create job test-job --image=busybox -- echo "Hello CKA"apiVersion: batch/v1
kind: Job
metadata:
name: calcul
spec:
completions: 5 # nombre de fois où le job doit réussir
parallelism: 2 # pods qui tournent en parallèle
backoffLimit: 4 # tentatives max avant échec
template:
spec:
restartPolicy: OnFailure # Never ou OnFailure (pas Always !)
containers:
- name: calcul
image: busybox
command: ["sh", "-c", "echo Calcul en cours; sleep 5"]CronJob
kubectl create cronjob backup \
--image=busybox \
--schedule="0 2 * * *" \
-- sh -c "echo Backup de minuit"apiVersion: batch/v1
kind: CronJob
metadata:
name: backup
spec:
schedule: "0 2 * * *" # syntaxe cron standard
concurrencyPolicy: Forbid # Allow, Forbid, Replace
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: busybox
command: ["sh", "-c", "echo Backup"]📈 Horizontal Pod Autoscaler (HPA)
# Créer un HPA (nécessite metrics-server installé)
kubectl autoscale deployment nginx \
--cpu-percent=50 \
--min=2 \
--max=10
kubectl get hpa
kubectl describe hpa nginxapiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50❤️ Probes (sondes de santé)
Les 3 types de probes
| Probe | Rôle | Action si échec |
|---|---|---|
livenessProbe | "Est-il encore en vie ?" | Redémarre le conteneur |
readinessProbe | "Est-il prêt à recevoir du trafic ?" | Retire le pod du Service |
startupProbe | "A-t-il fini de démarrer ?" | Désactive liveness/readiness pendant le démarrage |
spec:
containers:
- name: app
# Probe HTTP
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10 # attendre avant le 1er check
periodSeconds: 10 # fréquence des checks
failureThreshold: 3 # échecs consécutifs avant action
# Probe TCP
readinessProbe:
tcpSocket:
port: 5432
initialDelaySeconds: 5
periodSeconds: 10
# Probe commande
startupProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 0
periodSeconds: 5
failureThreshold: 30 # 30 x 5s = 150s pour démarrer📊 Récapitulatif des commandes clés
# Deployments
kubectl rollout status deployment/<name>
kubectl rollout history deployment/<name>
kubectl rollout undo deployment/<name>
kubectl scale deployment/<name> --replicas=5
# ConfigMaps et Secrets
kubectl create configmap <name> --from-literal=key=val
kubectl create secret generic <name> --from-literal=key=val
# Scheduling
kubectl taint nodes <node> <key>=<value>:<effect>
kubectl label nodes <node> <key>=<value>
kubectl describe node <node> | grep -E "Taints|Labels"
# HPA
kubectl autoscale deployment <name> --cpu-percent=50 --min=2 --max=10
kubectl get hpa
# Top
kubectl top pods --sort-by=cpu
kubectl top nodes🚀 Prochaines Étapes
- Domaine suivant : CKA : Services et Réseau
- Entraîne-toi : crée un StatefulSet avec PVCs, simule une montée en charge et observe le HPA
- Référence : kubernetes.io/docs/concepts/workloads/