L’équipe a roulé une nouvelle version de l’application par rafraîchissement d’instances. La console ASG montre toutes les instances de la flotte en InService. La console du target group ALB montre la moitié en Unhealthy, retournant 5xx sur l’endpoint de santé. Le pager se met à sonner. Vingt minutes de débogage plus tard : l’application a démarré, l’instance EC2 est joignable, mais l’application plante à la première requête à cause d’une variable d’environnement manquante. Le health check EC2 voit une instance qui tourne et rapporte saine. Le health check ELB voit l’application retourner 503 et rapporte non saine. L’ASG, configuré avec le type EC2 par défaut, voit sa propre vérification passer et laisse l’instance en service indéfiniment. L’ALB arrête de lui envoyer du trafic. La flotte est à la moitié de sa vraie capacité. Les deux health checks font exactement ce qu’on leur a dit de faire. Personne n’a dit à l’ASG d’écouter l’ALB.
Ce qui se passe
Il y a deux modèles de health check en jeu sur un ASG derrière ALB, et ils répondent à des questions différentes.
Les health checks EC2 répondent à « l’instance tourne-t-elle ? » — l’ASG interroge l’API instance-status d’AWS. La vérification passe si le statut système est ok (l’hôte EC2 est joignable) et le statut d’instance est ok (l’OS est joignable et pas dans un état dégradé). La vérification échoue quand AWS détecte une défaillance niveau hôte, une panique noyau, ou l’instance qui passe à arrêtée/terminée. Les health checks EC2 sont le défaut ASG. Ils sont rapides, peu coûteux, utiles — mais ils ne répondent pas à la question qui compte : « l’application fonctionne-t-elle ? »
Les health checks ELB répondent à « l’application répond-elle ? » — le load balancer fait une requête HTTP, HTTPS, TCP ou gRPC vers un endpoint configuré sur chaque cible et juge selon la réponse. Pour un ALB, c’est typiquement GET /health retournant 200. Pour un NLB, ce peut être une sonde de connexion TCP ou un check HTTP/HTTPS. La vérification passe quand la cible atteint le seuil de bonne santé (réponses consécutives réussies) et échoue quand elle franchit le seuil de mauvaise santé.
L’ASG peut être configuré pour écouter l’un ou l’autre, ou les deux. Le champ HealthCheckType sur l’ASG est EC2 (défaut) ou ELB. Le mettre à ELB ne désactive pas les health checks EC2 — il ajoute le verdict de l’ELB à l’équation. L’ASG considère une instance non saine si l’un de ses health checks configurés échoue. Donc HealthCheckType=ELB veut dire « considérer l’instance non saine si EC2 ou ELB le dit » — qui est ce que la plupart des équipes veulent vraiment.
L’interaction :
- L’ASG lance une instance. Elle est
Pendingdans la machine à états de l’ASG. - L’instance démarre. Les health checks EC2 commencent à passer une fois que le statut système est
ok. - L’ALB commence à vérifier la nouvelle cible. La cible est
initialle temps que le premier check complète. - L’ASG marque l’instance
InServicequand les health checks EC2 passent. - L’ALB marque la cible
healthyquand les health checks ELB passent. - L’ALB envoie du trafic à la cible seulement quand elle est
healthy.
Deux problèmes de timing résultent de cette danse :
- L’instance est
InService(ASG) avant que l’application soit prête (ALB). C’est normal — le rôle de l’ASG est de suivre si l’instance existe ; le rôle de l’ALB est de suivre si elle répond. Les deux convergent une fois que l’application démarre. - L’instance reste
InService(ASG) pendant que l’ALB l’a marquéeUnhealthy. C’est l’état cassé. L’ASG voit une machine qui tourne, l’ALB voit une application cassée, et sansHealthCheckType=ELBl’ASG ne remplace jamais la machine.
Le piège du grace-period des health checks s’applique à l’étape 4 spécifiquement. Le grace-period (HealthCheckGracePeriod sur l’ASG) est le nombre de secondes que l’ASG attend après le lancement avant d’évaluer les health checks. Le défaut est 300 secondes, bien pour la plupart des applications. L’erreur est de le laisser à 300 quand l’application prend 8 minutes à démarrer — l’ASG arrive à 5 minutes, l’ELB rapporte toujours non saine parce que l’app démarre encore, l’ASG voit « ELB non saine après le grace-period » et termine l’instance. Prochaine instance lancée, même chose. L’ASG brûle des instances en boucle. La flotte ne grandit jamais.
L’erreur inverse est de régler le grace-period trop haut. Avec un grace-period de 30 minutes, une instance vraiment cassée — une qui plante immédiatement au démarrage — reste InService 30 minutes avant que l’ASG le remarque. La flotte tourne à capacité réduite toute cette fenêtre.
Comment vérifier
Inspecter la configuration de health check sur un ASG :
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names my-app-prod-asg \
--query 'AutoScalingGroups[0].[HealthCheckType,HealthCheckGracePeriod]' \
--output table
EC2 avec un grace-period de 300 secondes est le défaut. Chercher deux drapeaux de mauvaise configuration :
HealthCheckType=EC2sur un ASG derrière ALB. Presque toujours faux. L’équipe voulaitELBet a oublié.HealthCheckGracePeriod=300sur une application qui prend plus de 300 secondes à démarrer. Commun après migration d’une AMI mince à une plus grosse en oubliant de monter le grace-period.
Vérifier la vue du target group ALB sur la même flotte :
aws elbv2 describe-target-health \
--target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-app/abc \
--query 'TargetHealthDescriptions[].[Target.Id,TargetHealth.State,TargetHealth.Reason]' \
--output table
Les états que vous pourriez voir, et ce que chacun veut dire :
healthy— la cible passe les health checks et reçoit du trafic.unhealthy— la cible échoue les health checks. Le champ raison clarifie :Target.FailedHealthChecks,Target.Timeout,Target.ResponseCodeMismatch, etc.initial— la cible vient d’être enregistrée et l’ALB n’a pas terminé le premier tour de health checks.draining— la cible a été désenregistrée et termine les requêtes en vol.unused— la cible n’est dans aucune zone que l’ALB couvre. Rare ; habituellement un mauvais sous-réseau.
La combinaison qui déclenche l’intro — ASG dit InService, ALB dit unhealthy — est le pistolet fumant pour HealthCheckType=EC2 sur un ASG derrière ALB.
Pour chronométrer le temps réel que l’application met à être prête :
# Lancer une instance fraîche dans l'ASG et regarder
aws autoscaling set-desired-capacity \
--auto-scaling-group-name my-app-prod-asg \
--desired-capacity $(($(aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names my-app-prod-asg \
--query 'AutoScalingGroups[0].DesiredCapacity' \
--output text) + 1))
# Puis sonder la santé des cibles jusqu'à ce que la nouvelle instance passe healthy
Le delta entre « l’instance apparaît dans le target group comme initial » et « l’instance apparaît comme healthy » est le vrai temps de disponibilité de l’application. Le HealthCheckGracePeriod de l’ASG devrait être ce nombre plus une marge (typiquement 30 à 60 secondes), pas le défaut de 300.
La correction
La configuration qui marche vraiment sur un ASG derrière ALB, par importance décroissante :
-
Régler
HealthCheckType=ELB. C’est le drapeau le plus important. L’ASG écoute les deux checks, remplace les instances que l’ALB dit cassées, et reste utile à travers les cas limites liés au grace-period :aws autoscaling update-auto-scaling-group \ --auto-scaling-group-name my-app-prod-asg \ --health-check-type ELB \ --health-check-grace-period 360 -
Régler
HealthCheckGracePeriodau vrai temps de démarrage de l’application plus 30 à 60 secondes de marge. Le mesurer. Ne pas deviner. Une application qui prend 4 minutes à démarrer a besoin de 270 à 300 secondes de grace ; une application qui prend 90 secondes a besoin de 120 à 150. -
Configurer le health check du target group pour correspondre au vrai signal de disponibilité de l’application. Le check par défaut (
GET /retournant 200) est rarement le bon. Un endpoint de santé correct :- Retourne 200 seulement quand l’application est vraiment capable de servir du trafic réel — incluant la connectivité à la base, la joignabilité des services en aval, et toute initialisation en processus.
- Retourne 503 (pas 500, pas 404) quand l’application démarre, draine, ou est autrement non prête. 503 a la sémantique explicite « service indisponible, réessayer » que les load balancers comprennent.
- Est peu coûteux. Les health checks se déclenchent toutes les 30 secondes environ, par cible, par instance de LB. Un endpoint de santé qui fait une vraie requête de base à chaque check est un DDoS contre la base.
Régler la config de health check du target group :
aws elbv2 modify-target-group \ --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-app/abc \ --health-check-path /health \ --health-check-interval-seconds 30 \ --healthy-threshold-count 2 \ --unhealthy-threshold-count 3 \ --health-check-timeout-seconds 5Les défauts conviennent dans l’ensemble —
interval=30,healthy_threshold=5,unhealthy_threshold=2,timeout=5. Le réglage ci-dessus baissehealthy_thresholdà 2 (récupération plus rapide) et monteunhealthy_thresholdà 3 (tolère un seul flap pendant la charge de pointe). -
Aligner le grace-period au motif de déploiement. Pendant un rafraîchissement d’instances, l’ASG lance de nouvelles instances et attend le grace-period avant d’évaluer leur santé. Si votre grace-period est 600 secondes et vous avez 20 instances à rafraîchir une à la fois, le refresh prend au moins 200 minutes juste sur les grace-periods. Démarrages plus rapides = grace-periods plus courts = déploiements plus rapides.
-
Utiliser des surcharges de
HealthCheckGracePeriodpour les cas spéciaux. Les lifecycle hooks pendant la phase de lancement peuvent étendre le temps de disponibilité effectif sans monter le grace-period pour tout le monde. Le champdefault_instance_warmupsur l’ASG est la manière moderne de contrôler ça pour les politiques de scaling spécifiquement — indépendamment du grace de health check.
Le piège de déploiement : quand vous poussez une nouvelle AMI et démarrez un rafraîchissement, les nouvelles instances démarrent depuis une nouvelle AMI mais l’ASG garde le même HealthCheckGracePeriod. Si la nouvelle AMI prend plus long à démarrer que l’ancienne — commun pendant une transition d’artefacts cuits en AMI vers tirés via user-data — le grace-period qui suffisait pour l’ancienne est trop court pour la nouvelle. Le refresh « réussit » pour les premières instances (elles démarrent dans le grace-period, ont la chance d’être saines, tout va bien), puis échoue pour les instances qui ont une contention I/O et démarrent lentement. La correction est de monter le grace-period dans le cadre du déploiement de la nouvelle AMI, pas comme un changement séparé.
Trois règles à chaque ASG derrière ALB :
HealthCheckType=ELB est le défaut que vous voulez. Tout autre veut dire que l’ASG ne peut pas remplacer les instances cassées qui ont l’air vivantes du point de vue EC2. L’économie de tourner en EC2 seul n’est pas réelle — l’ALB fait ses propres checks ; la question est si l’ASG entend la réponse.
Les endpoints de santé sont du code applicatif, pas de la config infrastructure. Un endpoint de santé qui retourne 200 quand la base est tombée est pire qu’aucun endpoint. Faire en sorte que l’endpoint vérifie vraiment les choses qui peuvent échouer.
Mesurer le temps de démarrage. Régler le grace-period depuis la mesure. « 300 secondes parce que c’est le défaut » est un rapport d’incident futur qui s’écrit.
Le modèle est simple une fois vu : health checks EC2 pour « la machine est vivante », health checks ELB pour « l’application marche ». L’ASG écoute ce que vous configurez. La plupart des équipes veulent les deux, et la plupart le découvrent à la dure pendant un déploiement raté. Voir l’article sur les lifecycle hooks lié pour le motif de hook de lancement qui protège contre le problème inverse — des instances qui passent InService avant d’être vraiment prêtes.
Stack Harbor opère les paliers applicatifs sur ASG et les politiques d’auto-scaling à coût plafonné dans le cadre de Gestion AWS et de la couche Auto-scaling. Si vos événements de scaling sont encore des surprises, commencez par une consultation d’infrastructure.