« Autoscaling » désigne trois mécanismes différents dans Kubernetes, et les confondre est la raison la plus courante pour laquelle un cluster qui devrait s'adapter ne le fait pas. Les pods évoluent avec un HorizontalPodAutoscaler, les nœuds évoluent avec les limites des pools de workers, et le plan de contrôle évolue avec la charge sur l'API. Cet article explique chacun d'eux, puis détaille un vrai test que nous avons mené sur un cluster géré Kube-DC, avec les manifestes pour que vous puissiez le reproduire.
Trois choses peuvent évoluer
| Couche | Ce qui évolue | Sur un cluster géré Kube-DC |
|---|---|---|
| Pods | Le nombre de répliques d'un Deployment ou d'un StatefulSet, piloté par une métrique comme le CPU | HorizontalPodAutoscaler standard, avec metrics-server déjà installé. Testé dans cet article |
| Nœuds | Le nombre de machines de travail disponibles pour exécuter ces pods | Pools de workers avec des limites minimale et maximale par pool, comme documenté pour la plateforme. Ne fait pas partie de notre test |
| Plan de contrôle | Les ressources du serveur d'API et d'etcd à mesure que la charge du cluster augmente | Mise à l'échelle verticale des deux, activée par défaut et dans des limites configurées, comme documenté. Ne fait pas partie de notre test |
Nous indiquons pour chaque couche ce que nous avons testé et ce que nous ne connaissons que par la documentation, car la différence compte lorsque vous planifiez la capacité.
Ce que fait réellement le HorizontalPodAutoscaler
Le HorizontalPodAutoscaler (HPA) est une boucle de contrôle qui s'exécute dans le plan de contrôle Kubernetes. Par défaut, il se réveille toutes les 15 secondes, lit les métriques des pods qu'il cible et ajuste le nombre de répliques du Deployment. Quatre détails expliquent presque toutes les surprises :
- L'utilisation est relative à la requête. Une cible de 50 % de CPU signifie 50 % du CPU demandé par les conteneurs du pod, et non 50 % du nœud. Si un conteneur n'a pas de requête CPU, l'utilisation est indéfinie et l'autoscaler n'agit pas sur cette métrique.
- Il a besoin de l'API de métriques. L'API metrics.k8s.io est normalement fournie par le module complémentaire Metrics Server, qui doit être installé séparément sur un cluster auto-géré.
- Le calcul est un rapport. Le nombre de répliques souhaité est égal au nombre actuel de répliques multiplié par le rapport entre la métrique actuelle et la cible, arrondi à l'entier supérieur. Si le rapport reste dans une tolérance de 0,1 autour de 1,0, il ne se passe rien.
- La réduction est volontairement lente. Le contrôleur mémorise les recommandations récentes et applique la plus élevée dans une fenêtre dont la valeur par défaut est de cinq minutes, ce qui lisse les métriques fluctuantes et évite les oscillations.
Par exemple, une réplique tournant à 57 % de CPU pour une cible de 50 % donne un rapport de 1,14, et l'arrondi supérieur de 1 × 1,14 donne 2 répliques. C'est exactement la première étape que nous avons observée dans le test ci-dessous.
Le test : faire évoluer un cluster géré sous charge réelle
Lors de notre test de bout en bout d'août 2026, nous avons créé un cluster géré nommé test2 avec deux nœuds de travail, chacun dimensionné à 1 vCPU et 2 Go de mémoire pour tenir dans la marge restante du quota du forfait. Le point d'accès de l'API du cluster est privé par défaut : nous l'avons donc atteint depuis le Cloud Shell intégré du projet, via l'adresse de service interne du cluster, plutôt que depuis Internet. Les deux workers étaient Ready, et metrics-server était déjà installé et fonctionnel, ce dont le HPA a besoin.
Nous avons déployé une petite application web avec un HPA ciblant 50 % de CPU et une plage de 1 à 6 répliques. Ce sont des manifestes d'exemple de même forme que ceux du test ; les valeurs de requête CPU sont illustratives :
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
replicas: 1
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginxdemos/hello
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 64Mi
---
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
selector:
app: hello
ports:
- port: 80
targetPort: 80
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: hello
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: hello
minReplicas: 1
maxReplicas: 6
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
Pour générer une charge soutenue, nous avons lancé plusieurs pods jetables dans le cluster, chacun envoyant des requêtes HTTP en boucle au service. Exécutez la première commande dans deux ou trois terminaux avec des noms différents, et surveillez l'autoscaler dans un autre :
kubectl run load-1 --image=busybox:1.36 --restart=Never -- /bin/sh -c "while true; do wget -q -O- http://hello > /dev/null; done"
kubectl get hpa hello --watch
Deux remarques honnêtes sur ce résultat. Nous n'avons pas chronométré la réduction, et la fenêtre de stabilisation par défaut de cinq minutes explique pourquoi elle est en retard sur la montée en charge. Et le test ne prouve que la couche des pods : il montre que le HPA, l'API de métriques et la planification du cluster fonctionnent ensemble sur un cluster géré Kube-DC, mais pas comment les pools de workers se comportent lors d'une mise à l'échelle au niveau des nœuds.
Lorsque vous avez terminé, supprimez la charge avec kubectl delete pod load-1 (et les autres générateurs que vous avez lancés), puis la charge de travail de test avec kubectl delete hpa,svc,deploy hello.
Ce que les pods ne peuvent pas faire seuls : la capacité
Le HPA ajoute des pods, et les pods ont besoin d'un endroit où s'exécuter. Si les workers sont pleins, les répliques supplémentaires restent en Pending jusqu'à ce que de la capacité apparaisse : le maxReplicas du HPA n'a donc de sens que si le pool sous-jacent peut accueillir autant de pods. Multipliez le nombre maximal de répliques par les requêtes de chaque pod et comparez le résultat avec la capacité libre de votre pool de workers.
Sur Kube-DC, les pools de workers sont définis pool par pool (CPU, mémoire, disque et image) avec des limites de mise à l'échelle automatique par pool, et le pool puise dans le quota de votre forfait. Ce quota est mutualisé entre tout ce que vous exécutez : conteneurs, machines virtuelles et clusters gérés partagent la même enveloppe de vCPU et de mémoire. Nous en avons vu la conséquence lors de notre test, où un cluster géré existant et une machine virtuelle utilisaient environ 7,5 des 8 vCPU d'un forfait Pro et ne laissaient aucune place pour un pod GPU. C'est pourquoi nous avons dimensionné les workers du test à 1 vCPU et 2 Go. Considérez votre forfait comme le plafond de chaque couche de mise à l'échelle, et vérifiez la marge restante avant de relever un maximum.
Le plan de contrôle évolue aussi
Un cluster avec de nombreux pods et des mises à l'échelle fréquentes fait peser une charge sur le serveur d'API et sur etcd. La documentation de Kube-DC indique que les ressources du plan de contrôle et d'etcd sont redimensionnées automatiquement dans des limites configurées à mesure que la charge du cluster augmente, avec une mise à l'échelle verticale activée par défaut. Sur un cluster auto-géré, dimensionner correctement ces composants est votre travail, et etcd est sensible à la pénurie de ressources, comme nous l'expliquons dans notre article sur Kubernetes géré et auto-hébergé.
Erreurs courantes
- Aucune requête de ressources. Sans requête CPU, une cible d'utilisation ne peut pas être calculée et le HPA ne fait rien pour cette métrique.
- Ignorer le comportement au démarrage. Un pod qui consomme beaucoup de CPU pendant son initialisation peut déclencher des montées en charge superflues. La documentation Kubernetes recommande une sonde de démarrage, ou une sonde de disponibilité qui ne réussit qu'après le pic, et le contrôleur ignore les échantillons de CPU d'un pod pendant sa période d'initialisation.
- Se baser sur le CPU alors que ce n'est pas le goulot d'étranglement. L'API autoscaling/v2 prend aussi en charge la mémoire et les métriques personnalisées, mieux adaptées aux charges pilotées par des files d'attente ou limitées par les E/S.
- Fixer le maximum sans vérifier la capacité. Comme ci-dessus, les répliques au-delà de ce que le pool peut accueillir restent simplement en Pending.
- Exécuter une seule réplique en production. Un minimum de un signifie un seul pod tant que la charge est faible. Utilisez un minimum plus élevé pour tout ce qui doit survivre à la perte d'un nœud.
Sources
- Kubernetes: Horizontal Pod Autoscaling
- Kube-DC platform datasheet: managed Kubernetes clusters
- Kube-DC: provisioning a Managed Cluster
À lire aussi
- Kubernetes géré vs auto-hébergé : la réalité technique
- Comment évaluer un fournisseur de Kubernetes géré en Europe
- GPU partagé sur Kubernetes : ce que vous obtenez vraiment
Reproduisez ce test sur votre propre cluster
Chaque forfait Kube-DC inclut des clusters Kubernetes imbriqués, créés depuis votre projet dans un portail en libre-service, avec metrics-server prêt et votre quota visible pour planifier la capacité. Derrière eux se trouvent des vCPU dédiés, un stockage NVMe reposant sur Ceph, une IPv4 publique dédiée et un stockage objet compatible S3, avec un support par e-mail et ticket 24h/24 et 7j/7 et un objectif de résolution de 4 heures. Reproduisez le guide ci-dessus et voyez les répliques bouger par vous-même.
Découvrir Kube-DC Kubernetes