Un Deployment est un objet API Kubernetes qui gère un ensemble répliqué de Pods. Il décrit un état désiré pour ces Pods et le contrôleur de Deployment s'assure que l'état actuel du cluster correspond à cet état désiré, en créant, mettant à jour ou supprimant des Pods si nécessaire.
Le Deployment lui-même ne gère pas directement les Pods. Il s'appuie sur un autre objet appelé ReplicaSet. Voici la hiérarchie :
Lors d'une mise à jour, le Deployment crée un nouveau ReplicaSet avec le nouveau template de Pod et gère la transition progressive de l'ancien ReplicaSet vers le nouveau.
Composants clés d'un Deployment :
spec.replicas
: Nombre de Pods identiques souhaités.spec.selector
: Comment le Deployment (et ses ReplicaSets) identifie les Pods qu'il doit gérer (basé sur les labels des Pods).spec.template
: Le modèle (template) utilisé pour créer de nouveaux Pods. Contient les spécifications des containers (image, ports, etc.) et les métadonnées (labels).spec.strategy
: Comment remplacer les anciens Pods par les nouveaux (ex: RollingUpdate
, Recreate
).Créons un Deployment simple pour exécuter 3 réplicas de Nginx (version 1.25).
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment # Nom du Deployment
labels:
app: nginx # Label pour le Deployment lui-même
spec:
replicas: 3 # Nombre de réplicas souhaité
selector:
matchLabels:
app: nginx # Le Deployment gère les Pods avec ce label
template: # Modèle pour les Pods
metadata:
labels:
app: nginx # Les Pods créés auront ce label
spec:
containers:
- name: nginx
image: nginx:1.27.4 # Image Nginx à utiliser (version spécifique)
ports:
- containerPort: 80 # Port exposé par le container
Pour appliquer ce manifeste :
kubectl apply -f nginx-deployment.yaml # En supposant que le YAML est dans ce fichier
On peut ensuite vérifier l'état :
# Vérifier le Deployment
$ kubectl get deployment nginx-deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 15s
# Vérifier le ReplicaSet créé par le Deployment
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-<hash> 3 3 3 20s
# Vérifier les Pods créés par le ReplicaSet
$ kubectl get pods -l app=nginx # Filtrer par le label défini dans le template
NAME READY STATUS RESTARTS AGE
nginx-deployment-<hash>-abcde 1/1 Running 0 25s
nginx-deployment-<hash>-fghij 1/1 Running 0 25s
nginx-deployment-<hash>-klmno 1/1 Running 0 25s
Si l'on supprime manuellement un Pod géré par ce Deployment, le ReplicaSet associé en créera immédiatement un nouveau pour maintenir l'état désiré de 3 réplicas.
$ kubectl delete pod nginx-deployment-<hash>-abcde
pod "nginx-deployment-<hash>-abcde" deleted
# Quelques secondes plus tard...
$ kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-deployment-<hash>-fghij 1/1 Running 0 1m
nginx-deployment-<hash>-klmno 1/1 Running 0 1m
nginx-deployment-<hash>-pqrst 1/1 Running 0 5s
Pour supprimer complètement l'application, il faut supprimer le Deployment :
kubectl delete deployment nginx-deployment
Le scaling consiste à ajuster le nombre de réplicas d'un Pod pour répondre aux variations de charge.
On peut changer le nombre de réplicas directement dans le yaml en utilisant kubectl apply ou kubectl edit ou via la commande :
$ kubectl scale deployment nginx-deployment --replicas=5
deployment.apps/nginx-deployment scaled
$ kubectl get replicasets
NAME DESIRED CURRENT READY AGE
nginx-deployment-558d6675d6 5 5 5 101s
controlplane:~$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 5/5 5 5 2m13s
On voit donc que le ReplicaSet a bien augmenté le nombre de pods du Déploiement.
Le système est le même si l'on veut supprimer des pods.
Les Deployments permettent de mettre à jour les Pods vers une nouvelle version (ex: nouvelle image Docker) de manière contrôlée. La stratégie de mise à jour est définie dans spec.strategy
.
RollingUpdate
(par défaut)C'est la stratégie par défaut. Elle met à jour les Pods progressivement, en s'assurant qu'un certain nombre de Pods restent disponibles pendant la mise à jour, et qu'on ne dépasse pas un certain nombre total de Pods (anciens + nouveaux).
Cela permet des mises à jour sans interruption de service (Zero Downtime).
Deux paramètres clés contrôlent le déroulement du Rolling Update :
spec.strategy.rollingUpdate.maxUnavailable
: (Défaut: 25%) Le nombre ou pourcentage maximum de Pods (par rapport au replicas
désiré) qui peuvent être indisponibles pendant la mise à jour. Un Pod est indisponible s'il est en cours d'arrêt ou de démarrage et pas encore Ready
.spec.strategy.rollingUpdate.maxSurge
: (Défaut: 25%) Le nombre ou pourcentage maximum de Pods (par rapport au replicas
désiré) qui peuvent être créés en plus du nombre de replicas
souhaité pendant la mise à jour. Cela permet de démarrer de nouveaux Pods avant d'arrêter les anciens.Exemple avec maxSurge
et maxUnavailable
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 10
selector: # ... (comme avant)
template: # ... (comme avant)
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 2 # Au maximum 2 pods sur 10 peuvent être indisponibles
maxSurge: 1 # Au maximum 1 pod peut être créé en plus (total 11 pods max)
Deux autres champs utiles dans spec
:
minReadySeconds
: (Défaut: 0) Nombre de secondes pendant lesquelles un nouveau Pod doit être Ready
(la readiness probe réussit) sans aucun de ses containers qui crashent, avant d'être considéré comme Available
. Cela ralentit le déploiement mais donne du temps pour détecter des problèmes avec la nouvelle version avant de continuer.progressDeadlineSeconds
: (Défaut: 600s = 10 minutes) Nombre maximum de secondes pendant lesquelles le Deployment doit progresser lors d'une mise à jour avant que le système ne le considère comme échoué (status Progressing
avec reason: ProgressDeadlineExceeded
).La manière la plus courante et recommandée (approche déclarative) est de modifier le template du Pod (spec.template
) dans le fichier manifeste YAML du Deployment, puis de ré-appliquer ce fichier :
# 1. Modifier le fichier nginx-deployment.yaml (ex: changer spec.template.spec.containers[0].image)
# vim nginx-deployment.yaml
# 2. Appliquer les changements
kubectl apply -f nginx-deployment.yaml
Kubernetes détecte le changement dans le template et lance automatiquement le rolling update.
On peut aussi utiliser la commande impérative kubectl set image
:
# Mettre à jour l'image du container nommé 'nginx' dans le deployment 'nginx-deployment'
kubectl set image deployment/nginx-deployment nginx=nginx:1.28.0 # Utiliser une version spécifique
# deployment.apps/nginx-deployment image updated
On peut aussi observer le rolling update du ReplicaSet :
$ k get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-558d6675d6 2 2 2 6m1s
nginx-deployment-5fbdcbb6d5 2 2 1 2m25s
$ k get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-558d6675d6 0 0 0 6m3s
nginx-deployment-5fbdcbb6d5 3 3 3 2m27s
# Observer le déploiement en temps réel
kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out
# Obtenir des détails sur le déploiement et ses ReplicaSets
kubectl describe deployment nginx-deployment
Recreate
Cette stratégie est plus simple : elle arrête tous les anciens Pods avant de créer les nouveaux. Cela entraîne une interruption de service pendant la mise à jour.
spec:
strategy:
type: Recreate
Elle est rarement utilisée pour des applications web classiques mais peut être utile dans certains scénarios spécifiques (ex: migration de base de données où une seule instance doit tourner).
Si une nouvelle version déployée pose problème, Kubernetes permet de revenir facilement à une version précédente du Deployment.
Chaque fois qu'un Deployment est mis à jour (que le template change), un nouveau ReplicaSet est créé, et l'historique de ces ReplicaSets est conservé par le Deployment. C'est cet historique qui permet le rollback.
1. Vérifier l'historique des révisions :
kubectl rollout history deployment/nginx-deployment
# deployment.apps/nginx-deployment
# REVISION CHANGE-CAUSE
# 1 <none>
# 2 <none> <-- Imaginons que c'est nginx:1.25.0
# 3 <none> <-- Imaginons que c'est nginx:1.25.3 (la version actuelle qui pose problème)
Note : La colonne CHANGE-CAUSE
est souvent <none>
. Elle était remplie par l'option --record
qui est maintenant dépréciée/supprimée. Pour tracer la raison des changements, il est préférable d'utiliser des annotations (kubernetes.io/change-cause
) ou un système de contrôle de version (Git) pour vos manifestes.
2. Examiner une révision spécifique (optionnel) :
kubectl rollout history deployment/nginx-deployment --revision=2
# Affiche les détails du template de la révision 2
3. Effectuer le Rollback :
Pour revenir à la version précédente (révision 2 dans notre exemple) :
kubectl rollout undo deployment/nginx-deployment
# deployment.apps/nginx-deployment rolled back
Pour revenir à une révision spécifique :
kubectl rollout undo deployment/nginx-deployment --to-revision=2
# deployment.apps/nginx-deployment rolled back
Le Deployment va alors effectuer un "rolling update inversé", en arrêtant progressivement les Pods de la révision 3 (gérés par le ReplicaSet de la révision 3) et en démarrant des Pods de la révision 2 (en réactivant/scalant le ReplicaSet de la révision 2) jusqu'à revenir à l'état défini par cette révision.
On peut suivre le statut du rollback avec kubectl rollout status deployment/nginx-deployment
.
Un StatefulSet est un objet API Kubernetes utilisé pour gérer des applications avec état (stateful applications). Contrairement à un Deployment, un StatefulSet maintient une identité persistante et unique pour chacun de ses Pods. Cette identité comprend :
<nom-statefulset>-<index-ordinal>
(ex: web-0
, web-1
).Voici un exemple simple de StatefulSet pour une application nginx-stateful
avec 2 réplicas. Chaque réplica aura son propre PVC basé sur le volumeClaimTemplates
.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-stateful
spec:
serviceName: "nginx-svc-headless" # Nom du Service Headless pour la découverte réseau
replicas: 2
selector:
matchLabels:
app: nginx-stateful # Doit correspondre aux labels du template de Pod
template:
metadata:
labels:
app: nginx-stateful
spec:
containers:
- name: nginx
image: nginx:1.27.4
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www # Nom du volume à monter
mountPath: /usr/share/nginx/html # Chemin de montage dans le container
volumeClaimTemplates: # Template pour créer dynamiquement les PVCs
- metadata:
name: www # Le nom du PVC sera <nom-volumeClaimTemplate>-<nom-pod> (ex: www-nginx-stateful-0)
spec:
accessModes: [ "ReadWriteOnce" ] # Mode d'accès
resources:
requests:
storage: 1Gi # Taille du volume
storageClassName: "standard" # Optionnel: nom de la StorageClass à utiliser
Il est important de noter qu'un Service Headless (un Service avec clusterIP: None
) est généralement créé pour permettre aux Pods du StatefulSet de se découvrir entre eux via DNS.
# Vérifier le StatefulSet
$ kubectl get statefulset nginx-stateful
NAME READY AGE
nginx-stateful 2/2 30s
# Vérifier les Pods
$ kubectl get pods -l app=nginx-stateful
NAME READY STATUS RESTARTS AGE
nginx-stateful-0 1/1 Running 0 45s
nginx-stateful-1 1/1 Running 0 40s
# Vérifier les PVCs créés
$ kubectl get pvc -l app=nginx-stateful
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-nginx-stateful-0 Bound pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 1Gi RWO standard 50s
www-nginx-stateful-1 Bound pvc-yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy 1Gi RWO standard 45s
Lors du scaling down d'un StatefulSet (ex: kubectl scale statefulset nginx-stateful --replicas=1
), les Pods sont terminés dans l'ordre inverse (le Pod avec l'index le plus élevé en premier). Les PVCs associés ne sont pas supprimés automatiquement pour protéger les données.
Un Job crée un ou plusieurs Pods et s'assure qu'un certain nombre d'entre eux se terminent avec succès. Lorsqu'un Job atteint le nombre de complétions réussies spécifié, il est considéré comme terminé.
Les Jobs sont utiles pour exécuter des tâches batch, ponctuelles ou qui doivent s'exécuter jusqu'à leur complétion, contrairement aux Deployments qui maintiennent des Pods en continu.
Voici un Job simple qui exécute un Pod affichant "Hello Kubernetes!" puis se termine.
apiVersion: batch/v1
kind: Job
metadata:
name: hello-batch-job
spec:
template: # Template pour le(s) Pod(s) du Job
spec:
containers:
- name: hello-batch
image: busybox:1.36 # Image légère
command: ["sh", "-c", "echo \"Hello Kubernetes!\"; sleep 30; echo \"Done\""]
restartPolicy: OnFailure # Ou Never. Ne peut pas être Always pour les Jobs.
# completions: 1 # (Optionnel) Nombre de Pods devant se terminer avec succès. Défaut à 1.
# parallelism: 1 # (Optionnel) Nombre de Pods pouvant s'exécuter en parallèle. Défaut à 1.
# activeDeadlineSeconds: 60 # (Optionnel) Durée maximale pendant laquelle le Job peut s'exécuter.
# backoffLimit: 4 # (Optionnel) Nombre de tentatives avant de marquer le Job comme échoué. Défaut à 6.
Pour appliquer et vérifier le Job :
kubectl apply -f hello-batch-job.yaml
# Vérifier le Job
$ kubectl get job hello-batch-job
NAME COMPLETIONS DURATION AGE
hello-batch-job 1/1 35s 40s
# Vérifier le Pod créé par le Job (il sera en statut Completed)
$ kubectl get pods -l job-name=hello-batch-job
NAME READY STATUS RESTARTS AGE
hello-batch-job-abcde 0/1 Completed 0 50s
# Voir les logs du Pod
$ kubectl logs hello-batch-job-abcde
Hello Kubernetes!
Done
Une fois le Job complété, les Pods ne sont généralement pas supprimés automatiquement pour permettre de consulter leurs logs et leur statut. Vous pouvez les supprimer manuellement, ou configurer un ttlSecondsAfterFinished
pour qu'ils soient nettoyés automatiquement après un certain délai.
apiVersion: batch/v1
kind: Job
metadata:
name: auto-cleanup-job
spec:
ttlSecondsAfterFinished: 100 # Le Job et ses Pods seront supprimés 100s après sa complétion
template:
# ... (définition du Pod comme ci-dessus)
Un CronJob crée des Jobs de manière planifiée, en suivant une syntaxe de type cron.
Il est idéal pour des tâches récurrentes, comme des sauvegardes, la génération de rapports, ou l'envoi d'e-mails planifié.
Chaque exécution d'un CronJob correspond à un Job. Le CronJob gère la création de ces Jobs en fonction de la planification définie.
Voici un CronJob qui exécute un Job toutes les minutes pour afficher la date actuelle.
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello-cronjob
spec:
schedule: "*/1 * * * *" # Syntaxe Cron: toutes les minutes
jobTemplate: # Template pour le Job qui sera créé
spec:
template:
spec:
containers:
- name: hello-cron
image: busybox:1.36
args:
- /bin/sh
- -c
- date; echo "Hello from the CronJob"
restartPolicy: OnFailure # Ou Never
# startingDeadlineSeconds: 100 # (Optionnel) Si un Job manque son heure de lancement (ex: cluster down),
# il sera quand même lancé s'il peut démarrer dans ce délai.
# concurrencyPolicy: Allow # (Optionnel) Allow (par défaut), Forbid (ne lance pas un nouveau Job si le précédent n'est pas fini),
# Replace (annule le Job en cours et le remplace par le nouveau).
# successfulJobsHistoryLimit: 3 # (Optionnel) Nombre de Jobs réussis à conserver. Défaut à 3.
# failedJobsHistoryLimit: 1 # (Optionnel) Nombre de Jobs échoués à conserver. Défaut à 1.
Pour appliquer et vérifier le CronJob :
kubectl apply -f hello-cronjob.yaml
# Vérifier le CronJob
$ kubectl get cronjob hello-cronjob
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello-cronjob */1 * * * * False 0 <none> 10s
# Après une minute ou deux, vérifier les Jobs créés
$ kubectl get jobs --watch
NAME COMPLETIONS DURATION AGE
hello-cronjob-1693486980 1/1 2s 15s
hello-cronjob-1693487040 0/1 0s # En cours
hello-cronjob-1693487040 1/1 1s 5s
# Vérifier les Pods d'un Job spécifique
$ kubectl get pods -l job-name=hello-cronjob-1693486980
NAME READY STATUS RESTARTS AGE
hello-cronjob-1693486980-xyz12 0/1 Completed 0 1m
# Voir les logs d'un Pod
$ kubectl logs hello-cronjob-1693486980-xyz12
Mon Aug 31 10:23:01 UTC 2023
Hello from the CronJob
Les Jobs créés par le CronJob sont conservés (ainsi que leurs Pods) selon les successfulJobsHistoryLimit
et failedJobsHistoryLimit
pour permettre le débogage. Les Jobs plus anciens sont automatiquement supprimés.
Le Horizontal Pod Autoscaler (HPA) ajuste automatiquement le nombre de Pods dans un Deployment, ReplicaSet, ou StatefulSet en fonction de l'utilisation observée des ressources (comme le CPU ou la mémoire) ou de métriques personnalisées.
Le HPA est un contrôleur qui interroge périodiquement les métriques des Pods qu'il cible. Si l'utilisation moyenne dépasse (ou est inférieure à) un seuil défini, le HPA ajuste le nombre de replicas
de l'objet cible (ex: Deployment) pour ramener l'utilisation vers la valeur souhaitée.
Pour que le HPA fonctionne correctement, le Metrics Server doit être déployé dans le cluster afin de fournir les métriques de base (CPU, mémoire) au HPA.
Supposons que nous ayons un Deployment nommé php-apache
qui sert une application PHP consommant du CPU.
1. Déployer une application (exemple) :
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
selector:
matchLabels:
run: php-apache
replicas: 1
template:
metadata:
labels:
run: php-apache
spec:
containers:
- name: php-apache
image: k8s.gcr.io/hpa-example # Ancienne image, mais fonctionne pour l'exemple
# Pour une image plus récente, vous pourriez utiliser registry.k8s.io/hpa-example
# Ou construire la vôtre. Cette image expose un endpoint / qui consomme du CPU.
ports:
- containerPort: 80
resources: # IMPORTANT: Les requests CPU doivent être définies pour le HPA basé sur le CPU
limits:
cpu: 500m
requests:
cpu: 200m
--- # Exposer le Deployment via un Service
apiVersion: v1
kind: Service
metadata:
name: php-apache
labels:
run: php-apache
spec:
ports:
- port: 80
selector:
run: php-apache
Appliquez ce YAML : kubectl apply -f php-apache-deployment.yaml
2. Créer le HPA :
On peut créer un HPA de manière impérative :
$ kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled
Ou de manière déclarative (recommandé) :
apiVersion: autoscaling/v2 # Ou autoscaling/v1 pour les métriques CPU/mémoire uniquement
kind: HorizontalPodAutoscaler
metadata:
name: php-apache-hpa
spec:
scaleTargetRef: # La ressource à scaler
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource # Type de métrique (Resource, Pods, Object, External, ContainerResource)
resource:
name: cpu
target:
type: Utilization # Ou AverageValue
averageUtilization: 50 # Pourcentage cible d'utilisation CPU
# behavior: (Optionnel, pour contrôler la vitesse de scale up/down)
# scaleDown:
# stabilizationWindowSeconds: 300
# policies:
# - type: Percent
# value: 100
# periodSeconds: 15
# scaleUp:
# stabilizationWindowSeconds: 0
# policies:
# - type: Percent
# value: 100
# periodSeconds: 15
# - type: Pods
# value: 4
# periodSeconds: 15
# selectPolicy: Max
Appliquez le YAML du HPA : kubectl apply -f php-apache-hpa.yaml
3. Vérifier le HPA :
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache-hpa Deployment/php-apache <unknown>/50% 1 10 1 15s
# Après quelques instants, si le Metrics Server fonctionne, <unknown> deviendra la valeur actuelle
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache-hpa Deployment/php-apache 0%/50% 1 10 1 1m
4. Générer de la charge (optionnel, pour tester) :
Pour voir le HPA en action, vous pouvez générer de la charge sur le service php-apache
.
# Dans un terminal, lancer une boucle pour envoyer des requêtes
$ kubectl run -i --tty load-generator --rm --image=busybox:1.36 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
# Dans un autre terminal, observer le HPA et le nombre de réplicas du Deployment
watch kubectl get hpa php-apache-hpa
watch kubectl get deployment php-apache
Vous devriez voir le nombre de REPLICAS
augmenter dans la sortie du HPA et le Deployment se scaler automatiquement lorsque la charge CPU dépasse 50%.
Lorsque la charge diminue, le HPA réduira le nombre de réplicas vers minReplicas
.
Le HPA peut aussi scaler en fonction de la mémoire ou de métriques personnalisées (ex: requêtes par seconde, longueur d'une file d'attente) en utilisant type: Pods
, type: Object
, ou type: External
dans la section metrics
et en configurant un adaptateur de métriques personnalisées (comme Prometheus Adapter).