Networking

Azure AKS : diagnostiquer l’épuisement SNAT avant d’ajouter un NAT Gateway

Un runbook de production pour séparer DNS, dépendance distante, NetworkPolicy, conntrack et pression SNAT Azure sur AKS avant de modifier le chemin de sortie du cluster.

30 sept. 2026 azureakskubernetesegresssnatload-balancernat-gatewaynetworkingobservabilityrunbookrollbackproduction

Plusieurs workloads AKS commencent à subir des timeouts intermittents vers une API publique. Les retries masquent une partie des échecs, mais la latence augmente et des jobs planifiés dépassent leur fenêtre. La destination est saine, le DNS répond encore et l’ajout de replicas aggrave le symptôme. La première proposition consiste à attacher un NAT Gateway pour augmenter la capacité sortante.

Ce changement peut être justifié. Il peut aussi donner au cluster une nouvelle IP source, invalider les allowlists des partenaires et conserver le comportement applicatif qui a épuisé le chemin précédent. Ce runbook part du pod en échec et prouve si le SNAT Azure est réellement le goulot avant de modifier l’architecture de sortie AKS. Il se termine par une décision : corriger la gestion des connexions, ajouter une capacité mesurée ou rollbacker le dernier changement applicatif ou réseau.

Figer une fenêtre d’échec comparable

Consignez la fenêtre UTC, le cluster, le node pool, la révision du déploiement, le namespace, le FQDN et le port de destination, l’erreur observée, le nombre de retries et l’IP source vue par la destination. Conservez une requête réussie et une requête en échec avec leurs correlation IDs lorsque le service distant en fournit.

Ne commencez pas par un redémarrage global. La pression SNAT est souvent inégale entre les nœuds. Un restart redistribue les pods et libère des connexions : il peut faire disparaître temporairement la preuve sans supprimer la cause.

bash 01-capture-aks-egress-context.sh
RG="rg-platform-prod"
AKS="aks-platform-prod"

az aks show --resource-group "$RG" --name "$AKS" --query "{nodeResourceGroup:nodeResourceGroup,outboundType:networkProfile.outboundType,loadBalancerSku:networkProfile.loadBalancerSku,loadBalancerProfile:networkProfile.loadBalancerProfile,natGatewayProfile:networkProfile.natGatewayProfile}" --output json > aks-egress-profile.json

kubectl get deploy,pods -A -o wide > workload-placement.txt
kubectl get events -A --sort-by=.lastTimestamp > kubernetes-events.txt

La formulation de l’incident doit rester testable : « entre 14:05 et 14:18 UTC, les pods de la révision checkout-7f8c9 placés sur deux nœuds ont subi des timeouts vers api.partner.example:443 ; les mêmes probes ont réussi depuis les autres nœuds ».

Prouver le chemin de sortie effectif

Lisez networkProfile.outboundType avant de supposer que le Load Balancer AKS porte le SNAT. Un cluster peut utiliser loadBalancer, un NAT Gateway managé ou user-assigned, userDefinedRouting ou un modèle de sortie explicitement restreint. Avec une UDR, le next hop effectif peut être Azure Firewall ou une autre network virtual appliance : les métriques SNAT du Load Balancer ne prouvent alors rien sur ce chemin.

Pour loadBalancer, identifiez le Load Balancer géré par AKS et ses frontends sortants dans le node resource group. Pour un NAT Gateway, identifiez le gateway attaché à chacun des subnets de node pools concernés. Pour une UDR, capturez la route table, la route effective 0.0.0.0/0, le next hop et l’IP publique réellement utilisée par l’équipement de sortie.

bash 02-inventory-aks-outbound-path.sh
NODE_RG="$(jq -r .nodeResourceGroup aks-egress-profile.json)"

az network lb list --resource-group "$NODE_RG" --query "[].{name:name,id:id,frontends:frontendIpConfigurations[].publicIpAddress.id,outboundRules:outboundRules[].name}" --output json > load-balancers.json

az network nat gateway list --query "[].{name:name,resourceGroup:resourceGroup,id:id,publicIps:publicIpAddresses[].id,subnets:subnets[].id}" --output json > nat-gateways.json

az network route-table list --query "[].{name:name,resourceGroup:resourceGroup,subnets:subnets[].id,routes:routes[].{prefix:addressPrefix,nextHop:nextHopType,nextHopIp:nextHopIpAddress}}" --output json > route-tables.json

Comparez cet inventaire à l’IP source observée par la dépendance distante. Une différence signifie que le modèle du chemin est incomplet : un autre équipement NAT, proxy, firewall ou une association de subnet intervient.

Écarter les pannes qui ressemblent au SNAT

Testez depuis le contexte du workload affecté, pas depuis le poste d’un administrateur. Conservez si possible le même namespace, le même nœud, la même politique DNS, le même service account et la même destination. Séparez ces signaux :

  • DNS : résolution lente, réponses alternées ou adresse privée joignable depuis une partie seulement du cluster ;
  • dépendance distante : réponse HTTP 429, 503, refus TLS ou deny d’allowlist après établissement de la connexion TCP ;
  • NetworkPolicy, NSG, UDR ou firewall : refus ou timeout reproductible lié à une destination, un subnet ou une règle ;
  • conntrack ou ports éphémères du nœud : pression locale avant l’étape SNAT Azure ;
  • SNAT Azure : échec d’ouverture de nouveaux flux vers des endpoints publics alors que les connexions existantes peuvent continuer.

Capturez séparément la résolution, le temps de connexion TCP/TLS et la réponse applicative. Un seul code retour curl fusionne trop de couches.

bash 03-probe-from-affected-pod.sh
NS="checkout"
POD="checkout-7f8c9-abcde"
HOST="api.partner.example"

kubectl exec -n "$NS" "$POD" -- getent ahostsv4 "$HOST"
kubectl exec -n "$NS" "$POD" -- sh -c 'time curl -sS -o /dev/null -w "connect=%{time_connect} tls=%{time_appconnect} total=%{time_total} code=%{http_code}\n" https://api.partner.example/health'

kubectl get networkpolicy -A -o yaml > network-policies.yaml
kubectl get pod -n "$NS" "$POD" -o wide > affected-pod.txt

Si l’image applicative ne contient aucun outil de diagnostic, utilisez un ephemeral debug container approuvé ou une probe préinstallée avec le même contexte réseau et le même placement. N’élargissez pas durablement l’image de production pour un incident.

Corréler les métriques SNAT Azure par nœud backend

Pour un chemin sortant via Standard Load Balancer, lisez UsedSnatPorts, AllocatedSnatPorts et SnatConnectionCount à une granularité d’une minute. Ventilez par IP backend et protocole lorsque la surface de supervision le permet. Une moyenne sur tout le backend pool peut masquer un seul nœud saturé.

bash 04-read-load-balancer-snat-metrics.sh
LB_ID="<aks-load-balancer-resource-id>"
START="2026-09-30T14:00:00Z"
END="2026-09-30T14:25:00Z"

az monitor metrics list --resource "$LB_ID" --metric UsedSnatPorts AllocatedSnatPorts SnatConnectionCount --interval PT1M --start-time "$START" --end-time "$END" --output json > load-balancer-snat-metrics.json

La preuve devient solide lorsque le nœud en échec approche de ses ports alloués, que les connexions SNAT en échec montent dans la même fenêtre, que les nouveaux flux publics échouent et que le workload crée beaucoup de connexions vers peu de tuples de destination. Une consommation inférieure à l’allocation ne suffit pas à innocenter le réseau : vérifiez le bon frontend, le bon nœud backend, le bon protocole et le bon équipement de sortie.

Pour un NAT Gateway ou un chemin via firewall, utilisez les métriques de l’équipement réellement traversé et conservez la même corrélation entre nœud et pod. Ne comparez pas la capacité NAT Gateway et l’allocation de ports Load Balancer comme si elles décrivaient le même mécanisme.

Trouver le workload qui crée les flux

Reliez l’IP backend affectée à son nœud AKS, puis listez tous les pods qui y sont placés. Comparez ce placement au changement de déploiement et au début de l’incident.

bash 05-map-node-to-pods.sh
NODE="aks-userpool-12345678-vmss00000a"

kubectl get node "$NODE" -o wide
kubectl get pods -A --field-selector spec.nodeName="$NODE" -o wide
kubectl top pods -A --containers --sort-by=cpu
kubectl get deploy,statefulset,job,cronjob -A -o wide > workload-owners.txt

Cherchez une release qui a désactivé le keep-alive, réduit la réutilisation du pool, raccourci la durée de vie du client, augmenté la concurrence des workers, synchronisé des jobs ou ajouté des retries agressifs. Une hausse du nombre de replicas peut multiplier les créations de connexions à volume de requêtes constant. Mesurez les nouvelles connexions par seconde et les connexions concurrentes par destination ; le nombre de requêtes ne suffit pas.

Si l’inspection du nœud est autorisée, réalisez une capture de connexions ou de paquets courte et bornée pendant l’échec. Filtrez les destinations IPv4 publiques et ne conservez que les champs nécessaires au comptage des flux. Évitez de collecter des payloads ou des credentials.

Appliquer d’abord la correction la plus petite

Préférez une correction du workload lorsque le churn de connexions est anormal : réutilisation des clients HTTP et des pools de base de données, concurrence bornée, backoff exponentiel avec jitter, respect des indications de retry du serveur et arrêt des retries lorsque leur deadline ne peut plus être tenue. Validez sur une seule révision et gardez disponible le digest de l’image précédente.

N’ajoutez de la capacité réseau qu’après avoir mesuré la concurrence légitime. Pour un chemin Load Balancer, contrôlez les outbound rules explicites, les ports alloués par backend et le nombre d’IP sortantes. Pour un egress durablement volumineux, NAT Gateway peut fournir un modèle de capacité et d’IP source plus lisible. Avec un réseau BYO, utilisez le modèle user-assigned supporté sur le subnet AKS ; n’attachez pas un gateway sans réconcilier la configuration de sortie du cluster.

Traitez le changement de chemin sortant comme une migration de production :

  • figez les anciennes et nouvelles IP sources publiques et mettez à jour les allowlists approuvées ;
  • inventoriez chaque subnet de node pool et chaque association de route table ;
  • canaryez un workload représentatif et testez DNS, TLS, autorisation de la dépendance et connexions soutenues ;
  • surveillez les échecs de connexion, la latence, les ports utilisés et les retries applicatifs ;
  • conservez la configuration de sortie précédente jusqu’à la fin de la fenêtre de validation.

Des ports supplémentaires ne remplacent pas des clients bornés. La capacité peut absorber un pic attendu ; elle ne doit pas normaliser une boucle illimitée de connexions ou de retries.

Valider, étendre ou rollbacker

Approuvez la correction applicative lorsque le rythme de création des connexions baisse, que le débit utile est conservé, que les timeouts disparaissent sur les nœuds précédemment affectés et que les métriques Azure gardent une marge pendant un pic représentatif.

Approuvez un changement Load Balancer ou NAT Gateway uniquement lorsque le chemin effectif, les IP sources, la couverture des subnets, les allowlists des dépendances et le modèle de capacité sont documentés, que le canari réussit et que le chemin précédent reste reproductible.

Rollbackez la révision applicative lorsque l’incident coïncide avec une modification de la gestion des connexions et que le digest précédent rétablit une réutilisation stable. Rollbackez la migration réseau lorsque l’identité source, le routage ou une allowlist downstream diffère du plan approuvé. Si l’ancien chemin n’a plus assez de capacité, contenez d’abord la demande au lieu de revenir vers un épuisement connu.

Arrêtez et escaladez lorsque les preuves pointent vers une NVA ou un firewall détenu par une autre équipe, lorsque le tuple de destination reste inconnu, ou lorsque plusieurs node pools utilisent des sorties différentes absentes du plan de changement.

Conclusion

Un timeout sortant intermittent sur AKS ne prouve pas que le cluster a besoin d’un NAT Gateway. Le diagnostic utile relie un échec de pod à un nœud, un tuple de destination et l’équipement de sortie réel, puis corrèle ce chemin avec le comportement des connexions et la pression sur les ports.

La décision de production devient explicite : corriger la réutilisation des connexions, ajouter une capacité SNAT justifiée, migrer la sortie avec un canari ou rollbacker le changement qui a créé la pression. Dans tous les cas, la validation couvre l’application, l’équipement réseau et l’identité source vue par la dépendance.