Ressources
HPA Kubernetes : quand l'autoscaling aggrave l'incident et déplace le goulot
HPA Kubernetes : formule, tolérance, fenêtres et politiques, délai des nœuds, plafond de réplicas tiré des dépendances et lecture d'un incident amplifié.
L'équipe CTN Solutions · publié le · 20 min de lecture
Faits vérifiés sur les sources primaires le
Le trafic monte, la latence suit, et le HorizontalPodAutoscaler (HPA) de Kubernetes ajoute des pods, comme prévu. Quelques minutes plus tard, la base de données refuse des connexions, une API (interface de programmation) tierce répond 429 (trop de requêtes) et une partie des nouveaux pods attend un nœud. Le service va plus mal qu'avant. Le HPA n'est pas forcément en défaut : il lit une métrique sur les pods qu'il pilote et ignore la capacité de ce qui se trouve derrière eux.
Cet article décrit l'algorithme du HPA tel que le documente Kubernetes 1.37 (version mineure la plus récente au ), le délai qu'ajoutent Cluster Autoscaler et Karpenter, une méthode pour fixer maxReplicas à partir des dépendances, et une grille pour lire la chronologie d'un incident amplifié.
Pourquoi plus de pods peut aggraver un incident
Le HPA répond à une question précise : combien de réplicas faut-il pour ramener une métrique à sa cible ? Il ne répond pas à celle qui compte pendant un incident : la ressource qui sature est-elle celle que les nouveaux réplicas apportent ? Ajouter des pods ajoute du CPU, de la mémoire et du parallélisme côté application. Si la ressource rare se trouve ailleurs, chaque pod supplémentaire augmente la demande qui pèse sur elle.
Quatre goulots que les réplicas ne desserrent pas
- Les connexions à la base de données. Dans PostgreSQL 18,
max_connectionsfixe le nombre maximal de connexions simultanées (en général 100 par défaut, modifiable seulement au démarrage du serveur), dont trois réservées par défaut aux superutilisateurs (superuser_reserved_connections). Si chaque pod ouvre son propre pool, le total croît avec les réplicas. Au-delà de la limite, le serveur n'accepte plus de connexion ; en deçà, davantage de requêtes concurrentes peut encore allonger leur durée sur un serveur déjà saturé. - Les quotas d'API tierces. Un fournisseur de paiement ou d'envoi de messages peut appliquer son quota par compte ou par clé. Une limite de débit posée dans chaque pod, multipliée par le nombre de réplicas, le dépasse alors dès que le HPA monte, et les tentatives qui suivent les réponses 429 consomment le reste.
- Les files et les traitements en aval. Un frontal mis à l'échelle accepte plus de travail ; si le consommateur de la file ne suit pas, l'attente se déplace sans disparaître.
- Les nœuds. Un pod créé par le HPA n'apporte rien tant qu'il n'est pas planifié, démarré et prêt. Sans place, il reste en attente (état
Pending) et la charge continue de peser sur les pods existants.
La boucle de rétroaction
Le livre de Google consacré à l'ingénierie de fiabilité des sites (Site Reliability Engineering, SRE) définit une défaillance en cascade comme une défaillance qui grandit avec le temps par rétroaction positive. La mise à l'échelle automatique peut fermer une telle boucle : la dépendance sature, les requêtes échouent, clients et bibliothèques réessaient, ce travail perdu fait monter le CPU, et le HPA ajoute des pods qui ouvrent à leur tour connexions et tentatives. À l'inverse, des pods qui attendent la base sans consommer de CPU gardent une métrique basse et le HPA ne fait rien : la métrique choisie décide du sens de l'erreur.
La boucle d'amplification par la mise à l'échelle
Le schéma montre comment une dépendance saturée peut ramener la charge vers la métrique que le HPA surveille.
Schéma défilable horizontalement ; sa version textuelle complète suit.
Lire le schéma sous forme textuelle
- Un pic de trafic porte le CPU moyen au-dessus de la cible.
- Le HPA ajoute des réplicas.
- Chaque réplica ouvre des connexions et appelle les dépendances.
- La base ou l'API tierce atteint sa limite.
- Les requêtes échouent ou ralentissent.
- Clients et bibliothèques réessaient.
- Ces tentatives font remonter le CPU et relancent l'étape 2.
Le même livre recommande de borner les nouvelles tentatives : backoff exponentiel aléatoire, limite par requête, budget de tentatives par processus, une seule couche de la pile qui réessaie. Ces protections appartiennent à l'application ; le HPA ne les remplace pas, et un plafond de réplicas ne tient que si elles existent.
L'algorithme du HPA dans Kubernetes 1.37
Le HPA est une ressource de l'API autoscaling/v2, stable, et un contrôleur exécuté par le kube-controller-manager. Il agit sur la sous-ressource scale d'une cible comme un Deployment ou un StatefulSet ; il ne s'applique pas à un DaemonSet. Ce n'est pas un processus continu : la boucle s'exécute à l'intervalle fixé par --horizontal-pod-autoscaler-sync-period, 15 secondes par défaut.
La formule et la tolérance
À chaque passage, pour chaque métrique, la documentation donne la formule suivante : réplicas souhaités = ceil(réplicas actuels × valeur actuelle / valeur cible). Pour une cible en utilisation (Utilization), la valeur actuelle est la moyenne, sur les pods ciblés, de la consommation rapportée à la request de chaque conteneur. Si un conteneur n'a pas de request pour cette ressource, l'utilisation n'est pas définie et le HPA n'agit pas sur cette métrique. Dans le code du contrôleur (tag v1.37.2), la multiplication porte sur le nombre de pods prêts dont la métrique est disponible.
Si le rapport entre valeur actuelle et cible reste dans la tolérance, le HPA ne fait rien. Elle vaut 10 % par défaut pour tout le cluster, réglable par --horizontal-pod-autoscaler-tolerance. Depuis Kubernetes 1.37, la fonctionnalité HPAConfigurableTolerance est stable : un champ tolerance se règle par HPA et par direction, sous behavior.scaleUp et behavior.scaleDown. Une tolérance de montée plus faible fait réagir plus tôt, mais elle fait aussi réagir au bruit de la métrique.
Métriques manquantes et pods pas encore prêts
Avant de conclure, le contrôleur ignore les pods en cours de suppression et écarte les pods en échec. Il met de côté les pods dont la métrique manque et, pour le CPU, ceux qui ne sont pas encore prêts ou dont le dernier échantillon précède le passage à l'état prêt. Deux options du kube-controller-manager, réglables uniquement pour tout le cluster, encadrent cette période de démarrage : --horizontal-pod-autoscaler-cpu-initialization-period (5 minutes par défaut) et --horizontal-pod-autoscaler-initial-readiness-delay (30 secondes par défaut).
Le calcul est ensuite refait de façon prudente. Pour une descente, les pods sans métrique sont supposés consommer 100 % de la cible ; pour une montée, 0 %. Pour le CPU, si une montée est proposée alors que des pods ne sont pas encore prêts, ceux-ci comptent pour 0 %. Si le nouveau rapport inverse le sens ou retombe dans la tolérance, le HPA ne change rien. Le statut publié garde pourtant la moyenne d'origine : la valeur que montre kubectl get hpa peut rester au-dessus de la cible sans que le HPA ajoute de pods.
Calcul du HPA à chaque passage
Le schéma suit les étapes que le contrôleur du HPA enchaîne à chaque passage, de la formule à la décision de changer ou non le nombre de réplicas.
Schéma défilable horizontalement ; sa version textuelle complète suit.
Lire le schéma sous forme textuelle
- Formule : réplicas souhaités = ceil(réplicas actuels × valeur actuelle / valeur cible). Pour une cible en utilisation, la valeur actuelle est la moyenne de la consommation rapportée à la request, sur les pods ciblés.
- Les pods en cours de suppression et ceux en échec sont écartés. Les pods dont la métrique manque sont mis de côté ; pour le CPU, les pods pas encore prêts, ou dont le dernier échantillon précède le passage à l'état prêt, aussi.
- Recalcul prudent : les pods sans métrique comptent pour 100 % de la cible en descente et pour 0 % en montée. Pour le CPU, les pods pas encore prêts comptent pour 0 % en montée.
- Décision : si le sens du changement s'inverse ou si le rapport revient dans la tolérance de 10 % par défaut, aucun changement.
- Sinon, le changement est borné par les politiques de
behavioret parmaxReplicas.
Deux conséquences comptent pendant un incident. Une sonde de disponibilité (readiness probe) qui réussit trop tôt laisse entrer le pic de CPU du démarrage dans le calcul ; la documentation conseille une startupProbe, ou une readinessProbe qui ne réussit qu'après ce pic. Et quand cette sonde échoue, le contrôleur EndpointSlice retire le pod des Services : si elle interroge la base, une base saturée peut sortir des pods du trafic et reporter la charge sur les autres.
Plusieurs métriques, métriques illisibles
Avec plusieurs métriques, le HPA retient la plus grande proposition, dans la limite de maxReplicas. Si une métrique est illisible et que les autres proposent une descente, il ne descend pas ; il peut encore monter. S'il ne parvient plus à calculer, par exemple quand Metrics Server ou l'adaptateur ne répond plus, la condition ScalingActive passe à False, signe en général d'un problème de métriques, et les réplicas restent au dernier ajustement.
Fenêtres de stabilisation et politiques
Le champ behavior, stable depuis Kubernetes 1.23, décrit séparément la montée (scaleUp) et la descente (scaleDown). Les valeurs par défaut, fusionnées avec ce que vous déclarez, sont les suivantes :
| Réglage | Montée (scaleUp) | Descente (scaleDown) |
|---|---|---|
| Fenêtre de stabilisation | 0 seconde | 300 secondes, ou la valeur de --horizontal-pod-autoscaler-downscale-stabilization |
| Politiques | 100 % des réplicas ou 4 pods par période de 15 secondes | 100 % des réplicas par période de 15 secondes |
Sélection (selectPolicy) | Max : la politique qui autorise le plus grand changement | une seule politique |
| Tolérance | 10 % pour le cluster, sauf champ tolerance | 10 % pour le cluster, sauf champ tolerance |
En descente, la fenêtre retient la plus haute recommandation de la période, pour éviter de retirer des pods qu'il faudrait recréer peu après. Une politique Pods ou Percent borne le changement sur periodSeconds (1 800 secondes au plus) ; selectPolicy: Min retient la plus restrictive, Disabled interdit la direction. Quand une politique bride la montée, la condition ScalingLimited passe à True avec la raison ScaleUpLimit ; quand c'est maxReplicas, avec TooManyReplicas.
Les types de métriques
Resource: CPU ou mémoire des pods, lus par l'APImetrics.k8s.io, que fournit en général Metrics Server, installé séparément.ContainerResource: la même mesure pour un seul conteneur, stable depuis Kubernetes 1.30, utile quand un conteneur annexe fausse la moyenne du pod.Pods: une métrique applicative par pod, lue parcustom.metrics.k8s.iovia un adaptateur.Object: une métrique d'un autre objet Kubernetes, comme les requêtes par seconde d'un Ingress.External: une métrique hors de Kubernetes, comme la profondeur d'une file, lue parexternal.metrics.k8s.io.
Pour Object et External, le calcul s'appuie sur les pods prêts. Avec au moins une métrique de ce type, minReplicas: 0 devient possible : HPAScaleToZero est en bêta et activée par défaut depuis Kubernetes 1.37, sur l'API server comme sur le kube-controller-manager, et la condition ScaledToZero signale l'état.
Les nœuds : le délai qu'ajoutent Cluster Autoscaler et Karpenter
Le HPA change un nombre de réplicas ; il ne crée pas de capacité. Si les nouveaux pods ne tiennent pas sur les nœuds existants, le planificateur les laisse en attente et un autoscaler de nœuds doit provisionner. La documentation de Kubernetes cite deux projets parrainés par le SIG Autoscaling (le groupe d'intérêt du projet Kubernetes consacré à l'autoscaling), Cluster Autoscaler et Karpenter. Tous deux réagissent aux pods non planifiables et dimensionnent les nœuds d'après les requests des pods, pas d'après leur consommation réelle.
Cluster Autoscaler
Cluster Autoscaler agrandit des groupes de nœuds préconfigurés et recherche les pods non planifiables toutes les 10 secondes par défaut (--scan-interval). Sa FAQ décompose le délai en trois parts : réaction du HPA, réaction de Cluster Autoscaler, provisionnement du nœud, qui dépend surtout du fournisseur cloud. Les événements TriggeredScaleUp et NotTriggerScaleUp sur le pod disent s'il a demandé un nœud, ou pourquoi pas.
Karpenter
Karpenter (v1.15 au ) crée des nœuds directement à partir des contraintes d'un NodePool, sans groupe homogène préalable. Il regroupe les pods en attente par lot : la fenêtre se ferme après 1 seconde sans nouveau pod en attente (BATCH_IDLE_DURATION) et dure au plus 10 secondes (BATCH_MAX_DURATION). Le champ spec.limits d'un NodePool plafonne les ressources qu'il peut consommer ; sans ce champ, seuls les quotas du fournisseur cloud bornent la croissance, et une fois la limite atteinte, aucun nœud n'est créé tant que d'autres ne sont pas terminés. Ce plafond protège un budget de calcul, pas une dépendance : il ignore tout de la base de données.
Ce que le délai change pendant l'incident
Du pic de charge au pod qui sert
Le schéma montre les étapes qui séparent la hausse d'une métrique d'un pod réellement capable de servir.
Schéma défilable horizontalement ; sa version textuelle complète suit.
Lire le schéma sous forme textuelle
- La charge monte sur les pods existants.
- Metrics Server ou un adaptateur collecte la métrique.
- Le HPA recalcule à son prochain passage (toutes les 15 secondes par défaut).
- Les nouveaux pods sont créés.
- Si un nœud a de la place, le pod démarre.
- Sinon, il attend qu'un autoscaler de nœuds obtienne un nœud du fournisseur cloud.
- Le pod ne reçoit du trafic qu'après le succès de sa sonde de disponibilité.
Pendant ce délai, le HPA a déjà compté les nouveaux réplicas : la cible peut en compter nettement plus que les pods qui servent réellement. Pour une métrique CPU, les pods pas encore prêts comptent pour 0 % dans un calcul de montée, ce qui freine la surenchère. Pour une métrique Object ou External, le rapport est multiplié par le nombre de pods prêts : si la métrique reste haute, d'autres montées restent possibles.
Quand les nœuds arrivent, les pods en attente démarrent ensemble et ouvrent leurs connexions en même temps, caches vides. Le livre SRE de Google rappelle qu'un service adossé à un cache ne tient pas sa charge avec un cache vide, et conseille d'augmenter la charge progressivement. Une politique de montée plus lente, ou une capacité tampon, adoucit ce front. La FAQ de Cluster Autoscaler décrit un tel tampon : des pods « pause » à très basse priorité réservent de la place, sont préemptés par les pods normaux (mécanisme de priorité et de préemption de Kubernetes), puis deviennent non planifiables et déclenchent l'ajout d'un nœud. Sa taille se décide avant le pic.
Fixer maxReplicas et les politiques à partir des dépendances
maxReplicas n'est pas une estimation de ce que le cluster peut héberger. C'est le nombre de réplicas au-delà duquel une dépendance cesse de suivre. Le calcul part donc de la dépendance la plus étroite du chemin critique.
- Lister les dépendances du chemin critique et la ressource limitante de chacune : connexions, requêtes par seconde, quota d'API, consommateurs d'une file.
- Lire la limite réelle à sa source :
SHOW max_connections;sur la base, le quota affiché par le fournisseur ou inscrit au contrat. - Retirer ce que consomment les autres : connexions réservées, autres services, workers, tâches planifiées, migrations, outils d'administration, marge pour une intervention humaine.
- Diviser par la consommation maximale d'un pod : taille maximale de son pool, débit sortant autorisé.
- Réserver la place du surplus de déploiement.
maxSurgevaut 25 % par défaut, arrondi au supérieur : pendant un déploiement, la cible peut comptermaxReplicasplus ce surplus. Un pod en cours d'arrêt peut aussi garder ses connexions pendant son délai de grâce, 30 secondes par défaut. - Arrondir à l'inférieur. Si le plafond reste sous le besoin, la réponse est dans la dépendance (pooler, réplicas en lecture, quota renégocié, cache), pas dans le HPA.
Exemple : un service, sa base et son quota d'API
Un service api tourne avec 6 réplicas, une cible CPU de 60 % (averageUtilization: 60) et une utilisation moyenne de 90 %.
- Rapport : 90 / 60 = 1,5, hors de la tolérance de 10 %.
- Proposition : ceil(6 × 1,5) = 9 réplicas.
- Plafond de vitesse sur un passage : 6 × 2 = 12 (la politique Percent, 100 %, autorise 12 ; la politique Pods, 4 pods, autorise 10 ; Max retient 12). Les 9 réplicas sont autorisés.
- Au passage suivant, les 3 nouveaux pods ne sont pas encore prêts et les 6 anciens restent à 90 %. Sans eux, le rapport serait encore 1,5 ; en les comptant à 0 %, la moyenne devient (6 × 90 + 3 × 0) / 9 = 60 %, soit un rapport de 1,0, dans la tolérance. Le HPA attend.
Budget de connexions de la base PostgreSQL qu'utilise ce service :
max_connectionsconfiguré : 400.- Réservées aux superutilisateurs (valeur par défaut) : 3.
- Workers : 40 ; tâches planifiées : 20 ; marge d'administration et de migration : 17.
- Reste pour
api: 400 - 3 - 40 - 20 - 17 = 320 connexions. - Pool maximal par pod : 10 connexions, soit 32 pods au plus en même temps.
- Avec un surplus de 25 % arrondi au supérieur, il faut R + ceil(R / 4) ≤ 32. Pour R = 25 : 25 + 7 = 32, le budget tient. Pour R = 26 : 26 + 7 = 33, il ne tient plus. Le plafond est
maxReplicas: 25.
Du budget de connexions à maxReplicas
Le schéma part de la limite de connexions de la base et aboutit au plafond de réplicas du service, dans l'exemple chiffré de l'article.
Schéma défilable horizontalement ; sa version textuelle complète suit.
Lire le schéma sous forme textuelle
max_connectionsconfiguré : 400.- Retirées : 3 connexions réservées aux superutilisateurs, 40 pour les workers, 20 pour les tâches planifiées, 17 pour l'administration et les migrations. Reste pour
api: 400 - 3 - 40 - 20 - 17 = 320 connexions. - Pool maximal de 10 connexions par pod : 320 / 10 = 32 pods au plus en même temps.
- Surplus de déploiement de 25 % arrondi au supérieur : R + ceil(R / 4) ≤ 32.
- Pour R = 25 : 25 + 7 = 32, le budget tient. Pour R = 26 : 26 + 7 = 33, il ne tient plus. Le plafond est
maxReplicas: 25.
Si la charge porte ensuite le CPU à 95 % sur ces 25 pods, le HPA propose ceil(25 × 95 / 60) = 40. Il reste à 25 et affiche ScalingLimited à True, raison TooManyReplicas. Pendant l'incident, c'est le signal attendu d'un plafond qui protège la base, pas une invitation à le relever.
Même raisonnement pour une API tierce dont le quota est de 50 requêtes par seconde pour le compte : avec une limite locale de 5 requêtes par seconde par pod, le quota est atteint dès 10 pods. Le onzième n'ajoute aucun débit, seulement des refus 429 et des tentatives. Une limite partagée entre tous les pods, ou un plafond de réplicas sur le service appelant, protège le quota ; une limite par pod ne le fait pas.
Régler la montée et la descente
Par défaut, la montée peut doubler les réplicas toutes les 15 secondes. Si la dépendance a besoin de temps pour absorber de nouveaux clients (connexions, caches), une politique Pods qui limite les ajouts par minute étale le front ; avec une politique Percent et selectPolicy: Min, la plus restrictive l'emporte. La contrepartie, une réponse plus lente à un vrai pic, est une décision à écrire.
À la descente, la fenêtre de 300 secondes garde la capacité pendant qu'une file se vide. En incident, selectPolicy: Disabled sur scaleDown bloque toute descente ; retirez ce réglage ensuite, sinon le HPA ne redescend plus.
À adapter à votre environnement : remplacez les valeurs entre chevrons par celles de votre calcul de budget et de vos essais.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: <NOM_HPA>
namespace: <NAMESPACE>
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: <NOM_DEPLOYMENT>
minReplicas: <MINIMUM_DE_SERVICE>
maxReplicas: <PLAFOND_ISSU_DU_BUDGET>
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: <CIBLE_CPU>
behavior:
scaleUp:
tolerance: <TOLERANCE_MONTEE>
stabilizationWindowSeconds: 0
policies:
- type: Pods
value: <PODS_AJOUTES_PAR_PERIODE>
periodSeconds: 60
- type: Percent
value: <POURCENTAGE_AJOUTE_PAR_PERIODE>
periodSeconds: 60
selectPolicy: Min
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: <POURCENTAGE_RETIRE_PAR_PERIODE>
periodSeconds: 60Quand le goulot est le nombre de pools plutôt que le CPU, une autre option existe : le redimensionnement en place des ressources d'un conteneur, stable depuis Kubernetes 1.35, change CPU et mémoire sans recréer le pod, donc sans ouvrir de nouveau pool. Il modifie aussi les requests, donc le dénominateur de l'utilisation que lit le HPA : la cible doit être relue en même temps.
Dégrader plutôt que multiplier
Un plafond protège la dépendance, mais une partie des requêtes reste alors sans réponse rapide. Le choix de ce qui échoue se prépare : délester les requêtes les moins utiles quand la file interne dépasse un seuil, couper un appel vers une dépendance qui ne répond plus (coupe-circuit), désactiver une fonction coûteuse par un interrupteur de configuration. Le livre SRE de Google demande que cette dégradation reste rare et qu'elle puisse être désactivée ou réglée rapidement.
Files d'attente : KEDA en bref
KEDA (Kubernetes Event-driven Autoscaling, v2.21 au ) met à l'échelle des consommateurs d'événements. Il ne remplace pas le HPA : son opérateur gère seul le passage de zéro à un réplica et de un à zéro, et il crée un HPA qui gère la plage de un à N à partir des métriques externes que publie son serveur de métriques. Une valeur d'activation décide du réveil, une valeur cible pilote ensuite le nombre de réplicas. La mise à zéro n'est pas possible avec des déclencheurs CPU ou mémoire, faute de pod à mesurer. Le budget s'applique de la même façon : maxReplicaCount vaut 100 par défaut dans un ScaledObject, et chaque consommateur ouvre lui aussi ses connexions.
Lire la chronologie d'un incident où la mise à l'échelle a amplifié la charge
Après l'incident, une question doit recevoir une réponse fondée sur des horodatages : la mise à l'échelle a-t-elle soulagé le service, l'a-t-elle aggravé, ou n'a-t-elle simplement pas eu le temps d'agir ? La réponse se lit dans l'ordre des événements, pas dans un graphique isolé.
Recueillir avant que les preuves disparaissent
Par défaut, l'API server ne conserve les événements Kubernetes qu'une heure (--event-ttl). Exportez donc les événements du HPA et des pods dès le début de l'incident, ou envoyez-les en continu vers la chaîne de journalisation. À adapter à votre environnement : remplacez les noms entre chevrons.
# Conditions et derniers événements du HPA
kubectl describe hpa <NOM_HPA> -n <NAMESPACE>
# Décisions du HPA (SuccessfulRescale : "New size: N; reason: ...")
kubectl get events -n <NAMESPACE> \
--field-selector involvedObject.kind=HorizontalPodAutoscaler,involvedObject.name=<NOM_HPA> \
--sort-by=.lastTimestamp
# Pods en attente de nœud et raisons de non planification
kubectl get pods -n <NAMESPACE> --field-selector=status.phase=Pending -o wide
kubectl get events -n <NAMESPACE> --field-selector reason=FailedScheduling --sort-by=.metadata.creationTimestamp
# Réaction de l'autoscaler de nœuds
kubectl get events -A --field-selector reason=TriggeredScaleUp --sort-by=.lastTimestamp
kubectl get nodeclaims -o wide
kubectl get nodes --sort-by=.metadata.creationTimestampLa commande kubectl get nodeclaims ne concerne que Karpenter ; l'événement TriggeredScaleUp, que Cluster Autoscaler. Le planificateur écrit ses événements par l'API events.k8s.io/v1 : il renseigne eventTime, et le champ lastTimestamp que lit kubectl get events reste vide ; le tri des événements FailedScheduling se fait donc sur leur date de création. Pour un événement répété, cette date est celle de la première occurrence, et la dernière figure dans series.lastObservedTime.
Aligner les signaux sur un seul axe de temps
| Signal | Où le lire | Ce qu'il établit |
|---|---|---|
| Réplicas souhaités | événements SuccessfulRescale du HPA | le moment et l'ampleur de chaque décision |
| Bridage | conditions ScalingLimited (ScaleUpLimit, TooManyReplicas) et ScalingActive | si une politique, le plafond ou une panne de métriques a borné le HPA |
| Pods prêts et pods en attente | statut des pods, événements FailedScheduling | l'écart entre réplicas comptés et réplicas qui servent |
| Nœuds | événements de l'autoscaler, date de création et passage à l'état prêt des nœuds | la part du délai due à la capacité |
| Connexions à la base | nombre de connexions actives rapporté à max_connections, refus de connexion | si chaque montée a rapproché la base de sa limite |
| Appels sortants | réponses 429 ou 503 (service indisponible), nombre de tentatives par appel | si les tentatives ont multiplié la charge |
| Symptôme utilisateur | taux d'erreur et latence du parcours critique | si le service rendu s'est amélioré ou dégradé après chaque décision |
Toutes les horloges sont ramenées à un même fuseau, de préférence UTC, et chaque ligne de la chronologie note l'heure, la décision du HPA, les pods prêts, les pods en attente, les connexions, les erreurs et ce qui a été fait à la main.
Reconnaître le scénario
- Amplification : chaque hausse des réplicas souhaités précède une hausse des connexions ou des refus 429, puis des erreurs, du CPU et une nouvelle décision. Si les erreurs précèdent la première décision, la mise à l'échelle n'est pas la cause première ; si la saturation suit chaque décision, elle l'a aggravée.
- Retard de capacité : les réplicas souhaités montent, les pods prêts stagnent, les pods en attente s'accumulent. Le délai de provisionnement est en cause, pas le calcul.
- Gel :
ScalingActivepasse àFalsealors que la cible compte au moins un réplica. La raison de la condition dit pourquoi : en général une métrique illisible (raisons commençant parFailedGet) ; sinon un sélecteur invalide ou ambigu (InvalidSelector,AmbiguousSelector). Le HPA n'a plus pu calculer. - Plafond atteint :
ScalingLimitedavecTooManyReplicas. Dépendance à sa limite : le plafond a joué son rôle. Dépendance saine et symptôme persistant : le goulot est dans le service. - Descente prématurée : les réplicas baissent alors que la file n'est pas vide ; la descente est à revoir.
Une bascule de la base coupe les connexions de tous les pods à la fois : plus il y a de réplicas, plus la vague de reconnexion est large ; ce point se prépare avec la topologie décrite dans PostgreSQL sur Kubernetes en haute disponibilité : topologies, bascule, RPO et tests de panne. Pour décider lesquels de ces signaux méritent une alerte, voir Des alertes qui sonnent pour rien : un socle d'observabilité pour une scale-up sans SRE.
La chronologie se termine par des décisions écrites : budget de connexions et maxReplicas recalculés, politiques revues, règles de tentatives, dégradation prévue, alertes sur la saturation des dépendances. Quand une mise en production approche, nous traitons ces plafonds comme des critères de passage : seuils d'alerte posés avant l'exposition, critères d'arrêt explicites pendant la montée du trafic, chemin de retour exécuté. C'est la démarche de Mise en production Kubernetes : ouvrir par étapes.
Sources officielles et limites de lecture
Faits datés
Les comportements décrits ont été vérifiés le sur la documentation de Kubernetes 1.37 (dernière version corrective : 1.37.2, publiée le ), le code du contrôleur HPA au tag v1.37.2, la FAQ de Cluster Autoscaler, la documentation de Karpenter v1.15, de KEDA 2.21 et de PostgreSQL 18. Les valeurs par défaut sont celles du projet amont : une distribution managée peut en fixer d'autres pour le kube-controller-manager, ou empêcher de les modifier ; la documentation de votre fournisseur fait foi.
Vérifié le
Sources
- Horizontal Pod Autoscaling, documentation Kubernetes : https://kubernetes.io/docs/concepts/workloads/autoscaling/horizontal-pod-autoscale/
- HorizontalPodAutoscaler Walkthrough, documentation Kubernetes : https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/
- Feature Gates (HPAConfigurableTolerance, HPAScaleToZero, HPAContainerMetrics, InPlacePodVerticalScaling), documentation Kubernetes : https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/
- Contrôleur podautoscaler (horizontal.go, replica_calculator.go), dépôt kubernetes/kubernetes, tag v1.37.2 : https://github.com/kubernetes/kubernetes/tree/v1.37.2/pkg/controller/podautoscaler
- Releases, Kubernetes : https://kubernetes.io/releases/
- Kubernetes v1.37.2, note de version : https://github.com/kubernetes/kubernetes/releases/tag/v1.37.2
- Node Autoscaling, documentation Kubernetes : https://kubernetes.io/docs/concepts/cluster-administration/node-autoscaling/
- Cluster Autoscaler FAQ, dépôt kubernetes/autoscaler : https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md
- Settings (v1.15), documentation Karpenter : https://karpenter.sh/v1.15/reference/settings/
- NodePools (v1.15), documentation Karpenter : https://karpenter.sh/v1.15/concepts/nodepools/
- KEDA Concepts (2.21), documentation KEDA : https://keda.sh/docs/2.21/concepts/
- Scaling Deployments, StatefulSets and Custom Resources (2.21), documentation KEDA : https://keda.sh/docs/2.21/concepts/scaling-deployments/
- ScaledObject specification (2.21), documentation KEDA : https://keda.sh/docs/2.21/reference/scaledobject-spec/
- Deployments, documentation Kubernetes : https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
- Pod Lifecycle, documentation Kubernetes : https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/
- Pod Priority and Preemption, documentation Kubernetes : https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/
- Resize CPU and Memory Resources assigned to Containers, documentation Kubernetes : https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/
- kube-apiserver (option --event-ttl), documentation Kubernetes : https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/
- Connections and Authentication (PostgreSQL 18), documentation PostgreSQL : https://www.postgresql.org/docs/18/runtime-config-connection.html
- Addressing Cascading Failures, Site Reliability Engineering, Google : https://sre.google/sre-book/addressing-cascading-failures/