Infrastructure
Azure AKS : diagnostiquer un HPA qui oscille avant d'augmenter les replicas
Un runbook de production pour séparer métrique bruitée, requests mal calibrées, démarrage lent et capacité nœud avant de modifier les bornes d'un Horizontal Pod Autoscaler AKS.
Une API sur AKS passe de 4 à 12 pods, redescend à 4 quelques minutes plus tard, puis remonte au pic suivant. Les latences se dégradent pendant le warm-up, les connexions aval se multiplient et l’équipe propose d’augmenter minReplicas ou maxReplicas pour stabiliser la production.
Cette oscillation ne prouve pas que la borne est trop basse. Le HPA peut suivre une métrique qui ne représente pas la demande, calculer l’utilisation sur des requests CPU irréalistes, attendre une métrique manquante ou demander des pods que le cluster ne peut pas planifier. Le cas fil rouge est un Deployment AKS autoscalé sur CPU, avec un démarrage de 90 secondes et une base de données qui supporte au plus 120 connexions applicatives. La sortie attendue est une décision : corriger le signal, borner la vitesse, ajouter de la capacité ou rollbacker la modification sans masquer le problème avec des replicas permanents.
Figer une chronologie avant de toucher aux bornes
Conservez une fenêtre UTC couvrant au moins deux cycles complets de montée et descente. Exportez le HPA, le Deployment, les événements, les pods et les nœuds. Notez la release, la version Kubernetes, le fournisseur de métriques et tout déploiement ou pic de trafic corrélé.
NS="checkout-prod"
HPA="checkout-api"
DEPLOY="checkout-api"
kubectl -n "$NS" get hpa "$HPA" -o yaml > hpa.yaml
kubectl -n "$NS" describe hpa "$HPA" > hpa.describe.txt
kubectl -n "$NS" get deploy "$DEPLOY" -o yaml > deployment.yaml
kubectl -n "$NS" get pods -l app=checkout-api -o wide > pods.txt
kubectl get nodes -o wide > nodes.txt
kubectl -n "$NS" get events --sort-by=.metadata.creationTimestamp > events.txt Ne changez pas simultanément requests, cible CPU, fenêtres de stabilisation et bornes. Une oscillation disparaîtrait peut-être, mais sans indiquer quel contrat était faux. Stoppez l’analyse si un second contrôleur écrit aussi spec.replicas, par exemple un ScaledObject KEDA ou une automatisation de déploiement : deux boucles concurrentes ne se règlent pas avec un seuil différent.
Lire le HPA comme une boucle de contrôle
Le calcul de base compare la valeur courante à la cible puis applique le ratio au nombre de replicas. Pour une cible moyenne, l’ordre de grandeur est :
desiredReplicas = ceil(currentReplicas * currentMetric / targetMetric)
Exemple
replicas courants: 4
CPU moyen observe: 90 % des requests
cible: 60 %
recommandation brute: ceil(4 * 90 / 60) = 6 Le résultat visible n’est toutefois pas la formule seule. Les pods en initialisation, les métriques manquantes, la tolérance, les recommandations récentes, minReplicas, maxReplicas et les policies de behavior modifient ou retardent l’action. Avec plusieurs métriques, le HPA retient la recommandation la plus élevée ; si une métrique est indisponible alors que les autres demandent une baisse, il peut refuser de réduire.
Lisez status.conditions, currentMetrics, desiredReplicas, lastScaleTime et les événements. AbleToScale, ScalingActive et ScalingLimited répondent à des questions différentes : le contrôleur peut agir, la métrique est exploitable, et la recommandation rencontre ou non une limite.
Prouver le signal réellement consommé
Un graphique applicatif ne prouve pas ce que lit le contrôleur. Interrogez l’API de métriques depuis le cluster et comparez-la au statut du HPA au même instant.
NS="checkout-prod"
HPA="checkout-api"
kubectl -n "$NS" get hpa "$HPA" -o jsonpath='{range .status.currentMetrics[*]}{.type}{" "}{.resource.name}{" "}{.resource.current.averageUtilization}{"
"}{end}'
kubectl get --raw '/apis/metrics.k8s.io/v1beta1/namespaces/'"$NS"'/pods' | jq -r '.items[] | select(.metadata.labels.app == "checkout-api") | [.metadata.name,.containers[].usage.cpu] | @tsv'
kubectl -n "$NS" top pods -l app=checkout-api --containers Pour une métrique custom ou externe, interrogez respectivement custom.metrics.k8s.io ou external.metrics.k8s.io. Vérifiez nom, sélecteur, unité, fraîcheur et cardinalité. Une série agrégée sur le mauvais label peut faire scaler un service depuis la charge d’un autre ; une valeur cumulée utilisée comme un débit monte sans jamais représenter la demande instantanée.
Si kubectl top est vide ou intermittent, inspectez Metrics Server et ses erreurs avant de retoucher le HPA. Pour un adapter Prometheus, contrôlez la règle de mapping, la requête générée et le délai entre collecte et exposition. L’objectif est de relier chaque recommandation à une valeur reproductible, pas seulement à une courbe visuellement proche.
Vérifier les requests et l’enveloppe par replica
Une cible averageUtilization CPU est calculée par rapport aux requests. Si un pod demande 100m mais consomme normalement 80m, le HPA voit déjà 80 %, même si le nœud reste largement disponible. À l’inverse, une request surdimensionnée peut retarder une montée utile.
NS="checkout-prod"
DEPLOY="checkout-api"
kubectl -n "$NS" get deploy "$DEPLOY" -o jsonpath='{range .spec.template.spec.containers[*]}{.name}{" request="}{.resources.requests.cpu}{" limit="}{.resources.limits.cpu}{"
"}{end}'
kubectl -n "$NS" top pods -l app=checkout-api --containers
kubectl -n "$NS" get pods -l app=checkout-api -o jsonpath='{range .items[*]}{.metadata.name}{" ready="}{.status.containerStatuses[0].ready}{" restarts="}{.status.containerStatuses[0].restartCount}{"
"}{end}' Calibrez les requests depuis une distribution observée et un test de charge, pas depuis un snapshot isolé. Mesurez aussi la capacité métier d’un replica : requêtes par seconde, concurrence, latence, pool de connexions et coût du warm-up. Douze pods avec vingt connexions chacun dépassent déjà la limite aval du cas fil rouge, même si le cluster peut les exécuter.
Séparer oscillation, démarrage et manque de nœuds
Une montée saine peut ressembler à une oscillation si les nouveaux pods deviennent Running mais restent non prêts. Pendant le warm-up, les anciens pods portent la charge ; la métrique reste haute et le HPA demande encore des replicas. Quand tous deviennent prêts, la moyenne chute brutalement et déclenche la baisse.
Contrôlez les startupProbe et readinessProbe, la durée réelle jusqu’à Ready, le chargement des caches et les erreurs de dépendances. Un pod ne doit pas recevoir du trafic avant d’être prêt, mais son initialisation ne doit pas non plus rester invisible au budget de montée.
Comparez ensuite desiredReplicas, replicas disponibles et pods Pending. Si le HPA demande 12 pods mais que 4 restent Pending, le problème se situe dans le scheduling ou la capacité nœud : requests trop élevées, affinité, taints, quota, limites de node pool ou délai du Cluster Autoscaler. Relever maxReplicas ne crée aucune capacité.
Reconstruire les cycles avec Prometheus
Les noms exacts dépendent de votre export kube-state-metrics. Le tableau utile superpose recommandation, état réel, disponibilité et signal métier.
# Ecart entre recommandation et replicas courants
kube_horizontalpodautoscaler_status_desired_replicas{
namespace="checkout-prod", horizontalpodautoscaler="checkout-api"
}
-
kube_horizontalpodautoscaler_status_current_replicas{
namespace="checkout-prod", horizontalpodautoscaler="checkout-api"
}
# Variation absolue de la recommandation sur 15 minutes
changes(kube_horizontalpodautoscaler_status_desired_replicas{
namespace="checkout-prod", horizontalpodautoscaler="checkout-api"
}[15m])
# Pods indisponibles du Deployment
kube_deployment_status_replicas_unavailable{
namespace="checkout-prod", deployment="checkout-api"
} Ajoutez CPU utilisé et demandé, durée jusqu’à Ready, pods Pending, latence p95, taux d’erreur, débit et saturation aval. Une oscillation est importante lorsqu’elle dégrade le service ou consomme une ressource limitée ; le seul changement de nombre de pods n’est pas un verdict.
Canaryer un comportement, pas une valeur magique
Créez un HPA de canari sur un Deployment ou un namespace isolé avec le même profil de charge. Commencez par limiter la vitesse de descente et conserver une montée réactive. Le réglage ci-dessous est un exemple à mesurer, pas un défaut universel.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: checkout-api-canary
namespace: checkout-canary
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: checkout-api-canary
minReplicas: 3
maxReplicas: 12
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 600
policies:
- type: Pods
value: 1
periodSeconds: 60 Rejouez une rampe, un plateau, un pic bref et un retour au calme. Le canari doit respecter la latence cible, ne pas produire de pods Pending, rester sous le budget de connexions aval et revenir à sa base sans cycles répétés. Testez aussi la perte temporaire de métriques : le comportement doit être explicable et ne pas provoquer une baisse dangereuse.
Décider, valider ou rollbacker
Corrigez le signal lorsque la métrique ne représente pas la demande ou arrive trop tard. Recalibrez les requests lorsqu’elles faussent l’utilisation relative. Allongez la stabilisation de descente ou limitez sa vitesse lorsque les pics courts et le warm-up créent des cycles. Ajoutez de la capacité nœud uniquement si des pods utiles restent Pending et si les contraintes de scheduling sont intentionnelles.
Augmentez minReplicas lorsque la capacité minimale nécessaire à la disponibilité et au temps de démarrage est mesurée. Augmentez maxReplicas seulement après avoir prouvé que le cluster et les dépendances supportent ce nombre. Une borne plus haute sans budget aval déplace l’incident vers la base de données ou l’API partenaire.
Rollbackez si le canari augmente latence, erreurs, pods indisponibles, connexions aval ou coût sans réduire les cycles. Restaurez le manifeste HPA et les requests connus, arrêtez le canari, puis vérifiez que currentReplicas, desiredReplicas et replicas disponibles convergent pendant une fenêtre complète.
Conclusion
Un HPA qui oscille est une boucle de contrôle qui reçoit un signal, applique un contrat et agit sur une capacité finie. Le nombre de pods n’est que la sortie visible.
Figez la chronologie, prouvez la métrique, calibrez les requests, mesurez le warm-up et vérifiez le scheduling avant de changer les bornes. La décision devient alors défendable : corriger le signal, amortir la descente, fournir des nœuds, relever une limite prouvée ou revenir au dernier comportement stable.