L’auto scaling dans Kubernetes désigne la capacité du cluster à ajuster automatiquement le nombre de pods ou les ressources allouées en fonction de la charge courante. Il existe trois mécanismes principaux, complémentaires :
| Mécanisme | Agit sur | Métrique type |
|---|---|---|
| HPA – Horizontal Pod Autoscaler | Nombre de pods | CPU, mémoire, métriques custom |
| VPA – Vertical Pod Autoscaler | Ressources d’un pod (CPU/RAM) | Utilisation historique |
| Cluster Autoscaler | Nombre de nœuds | Pods en état Pending |
Prérequis :
kubectlconfiguré,metrics-serverinstallé (requis pour HPA/VPA).
$ kubectl top nodes
$ kubectl top pods -A
error: Metrics API not available # Indique que l'API n'est pas installé!
Si la commande échoue, installez metrics-server :
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
I0324 17:14:24.000330 1 server.go:192] "Failed probe" probe="metric-storage-ready" err="no metrics to serve"
E0324 17:14:33.686318 1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.65.3:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.65.3 because it doesn't contain any IP SANs" node="docker-desktop"
I0324 17:14:33.998735 1 server.go:192] "Failed probe" probe="metric-storage-ready" err="no metrics to serve"
I0324 17:14:36.996841 1 server.go:192] "Failed probe" probe="metric-storage-ready" err="no metrics to serve"
Le metrics-server ne peut pas valider le certificat TLS du nœud Docker Desktop car il ne contient pas de SAN IP. C’est un problème classique avec Docker Desktop.
La solution est d’ajouter le flag –kubelet-insecure-tls au déploiement du metrics-server pour bypasser la vérification TLS.
kubectl patch deployment metrics-server -n kube-system \
--type='json' \
-p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
👉 Vérification :
Attendez ~30 secondes, puis vérifiez que le pod roule :
$ kubectl get pods -n kube-system | grep metrics-server
NAME READY STATUS RESTARTS AGE
metrics-server-5b57c547cb-6nmjv 1/1 Running 0 5m
# Et testez que les métriques remontent :
$ kubectl top nodes
# Résultat:
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%)
docker-desktop 546m 2% 2112Mi 6%
$ kubectl top pods -A
# Résultat:
NAMESPACE NAME CPU(cores) MEMORY(bytes)
default ma-alpine 0m 0Mi
default serveur-web-645fdccc79-4p2nm 0m 18Mi
default serveur-web-645fdccc79-75ftg 0m 43Mi
kube-system coredns-66bc5c9577-7p44d 2m 15Mi
kube-system coredns-66bc5c9577-cnlnk 2m 15Mi
kube-system etcd-docker-desktop 17m 58Mi
...
Le HPA surveille les métriques d’un Deployment (ou StatefulSet, ReplicaSet) et ajuste le nombre de réplicas à la hausse ou à la baisse.
Charge augmente → métriques dépassent le seuil → HPA crée de nouveaux pods
Charge diminue → métriques sous le seuil → HPA supprime des pods (avec stabilisation)
kubectl create deployment php-apache \
--image=registry.k8s.io/hpa-example \
--port=80
kubectl expose deployment php-apache --port=80
kubectl set resources deployment php-apache \
--requests=cpu=200m,memory=128Mi \
--limits=cpu=500m,memory=256Mi
# Via kubectl autoscale (méthode rapide)
kubectl autoscale deployment php-apache \
--cpu='50%' \
--min=1 \
--max=10
Ou via un manifeste YAML :
# hpa-cpu.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
kubectl apply -f hpa-cpu.yaml
kubectl get hpa php-apache --watch
# AFFICHE : NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
# php-apache Deployment/php-apache 0%/50% 1 10 1
kubectl run load-generator \
--image=busybox:1.28 \
--restart=Never \
-- sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
Observez le scaling :
$ kubectl get hpa php-apache --watch
# Les REPLICAS vont augmenter progressivement - Il faut être patient ici ...
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache cpu: 0%/50% 1 10 1 6m17s
php-apache Deployment/php-apache cpu: 250%/50% 1 10 1 6m40s
php-apache Deployment/php-apache cpu: 83%/50% 1 10 4 7m40s
php-apache Deployment/php-apache cpu: 50%/50% 1 10 7 8m41s
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
load-generator 1/1 Running 0 4m14s
php-apache-c49f9fd4-55d8v 1/1 Running 0 2m17s
php-apache-c49f9fd4-7nls5 1/1 Running 0 2m17s
php-apache-c49f9fd4-hlqk7 1/1 Running 0 3m17s
php-apache-c49f9fd4-jklzb 1/1 Running 0 12m
php-apache-c49f9fd4-mqp7t 1/1 Running 0 3m17s
php-apache-c49f9fd4-t9mzh 1/1 Running 0 2m17s
php-apache-c49f9fd4-zv9fk 1/1 Running 0 3m17s
# =========================================
# Deployment: php-apache
# =========================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
selector:
matchLabels:
app: php-apache
template:
metadata:
labels:
app: php-apache
spec:
containers:
- name: php-apache
image: registry.k8s.io/hpa-example
ports:
- containerPort: 80
resources:
requests:
cpu: 200m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
---
# =========================================
# Service: php-apache
# =========================================
apiVersion: v1
kind: Service
metadata:
name: php-apache
spec:
selector:
app: php-apache
ports:
- port: 80
targetPort: 80
---
# =========================================
# HorizontalPodAutoscaler: php-apache
# =========================================
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
---
# =========================================
# Pod: Le générateur de charge
# =========================================
apiVersion: v1
kind: Pod
metadata:
name: load-generator
spec:
restartPolicy: Never
containers:
- name: load-generator
image: busybox:1.28
command:
- sh
- -c
- "while sleep 0.01; do wget -q -O- http://php-apache; done"
$ kubectl delete service php-apache
$ kubectl apply -f manifeste.yaml
Pour surveiller le HPA en temps réel :
# Observer le scaling
$ kubectl get hpa php-apache --watch
# Voir les pods se multiplier
$ kubectl get pods --watch
💡 Note : Le load-generator démarre immédiatement le stress test dès l’application du manifeste. Pour le contrôler, vous pouvez le commenter et l’appliquer séparément quand vous êtes prêt.
$ kubectl delete pod load-generator
# Le scale-down a un délai de stabilisation par défaut de 5 minutes - patience ...
$ kubectl get hpa php-apache --watch
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache cpu: 0%/50% 1 10 1 6m17s
php-apache Deployment/php-apache cpu: 250%/50% 1 10 1 6m40s
php-apache Deployment/php-apache cpu: 83%/50% 1 10 4 7m40s
php-apache Deployment/php-apache cpu: 50%/50% 1 10 7 8m41s
php-apache Deployment/php-apache cpu: 49%/50% 1 10 7 9m41s
php-apache Deployment/php-apache cpu: 0%/50% 1 10 7 11m
php-apache Deployment/php-apache cpu: 0%/50% 1 10 1 16m
kubectl delete deployment php-apache
kubectl delete service php-apache
kubectl delete hpa php-apache
# hpa-memory.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-memory
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 8
metrics:
- type: Resource
resource:
name: memory
target:
type: AverageValue
averageValue: 200Mi
Il est possible de contrôler la vitesse de scaling pour éviter les oscillations :
spec:
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # Attente avant de réduire
policies:
- type: Percent
value: 10
periodSeconds: 60 # Supprimer max 10% par minute
scaleUp:
stabilizationWindowSeconds: 0 # Scale-up immédiat
policies:
- type: Pods
value: 4
periodSeconds: 15 # Ajouter max 4 pods toutes les 15s
Le VPA ajuste les requêtes de ressources (CPU/RAM) d’un pod existant, sans changer le nombre de réplicas. Il est particulièrement utile pour les charges de travail imprévisibles ou mal calibrées.
⚠️ Le VPA nécessite une installation séparée.
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-install.sh
Vérification :
kubectl get pods -n kube-system | grep vpa
| Mode | Comportement |
|---|---|
Off |
Recommandations uniquement (pas d’application) |
Initial |
Applique les recommendations seulement à la création du pod |
Auto |
Recrée les pods avec les nouvelles ressources (redémarrage) |
Recreate |
Identique à Auto mais force la recréation |
kubectl create deployment vpa-demo \
--image=nginx:alpine \
--replicas=2
# vpa-demo.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: vpa-demo
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: vpa-demo
updatePolicy:
updateMode: "Off" # Recommandations uniquement
resourcePolicy:
containerPolicies:
- containerName: nginx
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: 1
memory: 512Mi
kubectl apply -f vpa-demo.yaml
kubectl describe vpa vpa-demo
# Chercher la section "Recommendation"
Ou en JSON pour parser les valeurs :
kubectl get vpa vpa-demo -o json | \
jq '.status.recommendation.containerRecommendations'
kubectl delete deployment vpa-demo
kubectl delete vpa vpa-demo
Le Cluster Autoscaler agit au niveau de l’infrastructure : il ajoute ou supprime des nœuds du cluster selon que des pods sont en attente (Pending) ou que des nœuds sont sous-utilisés.
⚠️ Le Cluster Autoscaler est spécifique à chaque provider cloud (GKE, EKS, AKS…). Les exemples ci-dessous sont génériques.
Pod Pending (plus de ressources dispo) → Cluster Autoscaler ajoute un nœud
Nœud sous-utilisé depuis > 10 min → Cluster Autoscaler supprime le nœud
Vous pouvez tester le comportement sur un cluster local en créant intentionnellement une pression sur les ressources :
# Voir les ressources disponibles sur les nœuds
kubectl describe nodes | grep -A5 "Allocated resources"
# Créer un déploiement qui demande beaucoup de CPU
kubectl create deployment resource-hog \
--image=nginx:alpine \
--replicas=10
kubectl set resources deployment resource-hog \
--requests=cpu=500m,memory=256Mi
# Observer les pods en état Pending
kubectl get pods --watch | grep Pending
# Empêcher l'éviction d'un pod critique
metadata:
annotations:
cluster-autoscaler.kubernetes.io/safe-to-evict: "false"
# Voir les événements du Cluster Autoscaler
kubectl get events --field-selector reason=TriggeredScaleUp
kubectl get events --field-selector reason=ScaleDown
KEDA (Kubernetes Event-Driven Autoscaling) étend le HPA pour scaler sur des sources externes : files de messages (Kafka, RabbitMQ, SQS), bases de données, métriques Prometheus, etc.
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace
Vérification :
kubectl get pods -n keda
# keda-rabbitmq.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: rabbitmq-consumer
spec:
scaleTargetRef:
name: rabbitmq-consumer-deployment
minReplicaCount: 0 # Scale-to-zero possible !
maxReplicaCount: 20
triggers:
- type: rabbitmq
metadata:
queueName: my-queue
host: amqp://guest:guest@rabbitmq:5672/
queueLength: "5" # 1 pod par 5 messages en attente
# keda-prometheus.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: prometheus-scaler
spec:
scaleTargetRef:
name: my-app
minReplicaCount: 1
maxReplicaCount: 15
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus-server.monitoring:9090
metricName: http_requests_per_second
threshold: "100"
query: sum(rate(http_requests_total[2m]))
kubectl apply -f keda-prometheus.yaml
# Surveiller le ScaledObject
kubectl get scaledobject prometheus-scaler
kubectl describe scaledobject prometheus-scaler
requests et limitsLe HPA ne peut pas fonctionner sans requests CPU/mémoire définis sur les conteneurs.
# Vérifier que les ressources sont définies
kubectl get deployment my-app -o json | \
jq '.spec.template.spec.containers[].resources'
AutoHPA et VPA en mode automatique peuvent entrer en conflit. Préférez :
Off ou Initial pour les recommandationsPodDisruptionBudgets (PDB)Un PDB garantit qu’un minimum de pods reste disponible pendant un scale-down.
# pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
minAvailable: 2 # ou maxUnavailable: 1
selector:
matchLabels:
app: my-app
kubectl apply -f pdb.yaml
kubectl get pdb
# Événements HPA
kubectl describe hpa my-hpa
# Événements généraux liés au scaling
kubectl get events --sort-by=.metadata.creationTimestamp | grep -i scale
# Logs du controller HPA (dans kube-system)
kubectl logs -n kube-system \
-l component=kube-controller-manager \
--tail=50 | grep -i hpa
kubectl top pour valider les métriques# Ressources des pods en temps réel
kubectl top pods --sort-by=cpu
# Ressources des nœuds
kubectl top nodes
# Créer un HPA rapidement
kubectl autoscale deployment <nom> --cpu-percent=50 --min=1 --max=10
# Voir l'état du HPA
kubectl get hpa
kubectl describe hpa <nom>
# Voir les recommandations VPA
kubectl describe vpa <nom>
# Forcer un scaling manuel (contourne temporairement le HPA)
kubectl scale deployment <nom> --replicas=5
# Voir tous les objets de scaling
kubectl get hpa,vpa,scaledobject -A
(c) VE2CUY - Version 2026.02.26.1