Networking

AKS : diagnostiquer un DNS intermittent avant de redémarrer CoreDNS

Un runbook de production pour séparer resolver du pod, CoreDNS, chemin de service, DNS upstream et panne liée à un nœud avant redémarrage ou rollback du DNS AKS.

21 sept. 2026 azureakskubernetesdnscorednsnetworkingobservabilityincidentrunbookrollbackproduction

Un workload AKS commence à produire des erreurs ENOTFOUND, SERVFAIL ou des timeouts DNS intermittents. Un retry réussit souvent, la readiness reste au vert et seuls certains pods semblent touchés. Redémarrer CoreDNS paraît rapide et concret. Ce geste peut aussi effacer le groupe en échec, remettre des compteurs à zéro et masquer un problème de nœud, de forme de requête ou de DNS upstream jusqu’au prochain pic.

Le cas fil rouge est une API du namespace orders-prod qui résout une dépendance interne et un nom de service Azure. Les erreurs ont augmenté après un déploiement, sans preuve sur la couche responsable. Ce runbook préserve les éléments utiles, rejoue les mêmes noms depuis des cohortes contrôlées, sépare le service DNS Kubernetes de ses resolvers upstream et n’autorise un redémarrage ou un rollback que lorsque la couche en faute est identifiée.

Figer un contrat de panne DNS

Ne partez pas de « le DNS est instable ». Capturez la question exacte et le contexte runtime. Un nom court, le FQDN d’un service du cluster et un FQDN externe ne suivent pas les mêmes suffixes de recherche ni les mêmes règles de forwarding.

yaml incident-dns-aks.yml
cluster: aks-platform-prod
window_utc: <start>/<end>
client:
namespace: orders-prod
workload: deployment/orders-api
pod: <pod-name>
node: <node-name>
questions:
- name: payments.payments-prod.svc.cluster.local
type: A
- name: <internal-zone-name>
type: A
- name: <azure-service-fqdn>
type: A
observed_errors: [timeout, SERVFAIL]
recent_changes:
- image applicative ou librairie de résolution
- ConfigMap CoreDNS ou autoscaling
- NetworkPolicy ou policy Cilium
- image de nœud, pool ou routage
- DNS upstream ou resolver privé
stop_condition: pas de redémarrage global avant identification de la couche en échec

Conservez la release applicative, la révision du Deployment CoreDNS, la resource version de la ConfigMap, les versions d’image des nœuds et les manifests de policy. Notez si les erreurs se concentrent par nœud, namespace, suffixe, type de réponse ou fenêtre horaire.

Reproduire depuis le workload et une cohorte témoin

Une résolution réussie depuis le poste d’un administrateur ne prouve rien sur le chemin du pod. Lisez la configuration du resolver dans un pod touché, puis comparez-la avec un pod sain qui utilise la même image et le même namespace.

bash 01-client-et-temoin.sh
kubectl -n orders-prod exec <affected-pod> -- cat /etc/resolv.conf
kubectl -n orders-prod exec <affected-pod> -- getent hosts payments.payments-prod.svc.cluster.local
kubectl -n orders-prod exec <affected-pod> -- getent hosts <internal-zone-name>

kubectl -n orders-prod get pod -o wide
kubectl -n orders-prod get networkpolicy
kubectl -n orders-prod get events --sort-by=.lastTimestamp

Si l’image applicative ne contient aucun outil de diagnostic, utilisez un conteneur éphémère approuvé ou un pod de diagnostic avec les mêmes contraintes de namespace et de placement. N’installez pas de package dans un conteneur de production pendant l’incident.

Exécutez une courte série bornée de requêtes plutôt qu’un unique nslookup. Conservez latence, code retour et réponse à chaque tentative. Comparez quatre cohortes : pod touché, pod sain sur le même nœud, pod sain sur un autre nœud et pod de diagnostic dans un autre namespace. Cette matrice sépare configuration du workload, policy du namespace, chemin du nœud et panne globale du cluster.

Mesurer l’amplification avant d’accuser la capacité

Lisez search, nameserver, options ndots et timeout dans /etc/resolv.conf. Un client qui résout un nom pointé mais non absolu peut essayer plusieurs suffixes avant le nom voulu. Une requête applicative peut donc générer plusieurs questions DNS, surtout lorsque les librairies ajoutent leurs propres retries.

Capturez les noms réellement envoyés si les traces applicatives ou les logs de résolution disponibles les exposent déjà. Comparez le débit de requêtes à celui de la release et au taux d’erreur DNS. N’activez pas un logging CoreDNS verbeux sur tout le cluster sans borner durée, volume et rétention ; un canari ou une courte fenêtre de capture est plus défendable.

Préférez des FQDN pour les dépendances externes lorsque l’application et la librairie le permettent. Traitez un changement de ndots, de search domains ou de retry client comme une modification applicative : canari et rollback restent nécessaires. Ce n’est pas un correctif CoreDNS universel.

Prouver le chemin du service DNS Kubernetes

Inspectez le service, ses endpoints prêts et le placement des pods CoreDNS avant de toucher au Deployment.

bash 02-chemin-service-coredns.sh
kubectl -n kube-system get service kube-dns -o wide
kubectl -n kube-system get endpointslice -l kubernetes.io/service-name=kube-dns -o wide
kubectl -n kube-system get deployment coredns -o wide
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide
kubectl -n kube-system describe deployment coredns
kubectl -n kube-system get events --sort-by=.lastTimestamp

Vérifiez que les EndpointSlices prêts correspondent à des pods CoreDNS sains et que les échecs ne suivent pas un endpoint ou un nœud. Recherchez restarts, échecs de readiness, throttling, pression mémoire et placement déséquilibré. Si le DNS du cluster utilise un service différent ou un layout d’add-on managé, découvrez d’abord les objets réellement déployés au lieu d’imposer ces noms.

Un pod CoreDNS sain ne prouve pas que le trafic pod-vers-service l’atteint. Identifiez le data plane AKS et l’implémentation de policy actifs. UDP 53 est le chemin habituel, mais une réponse volumineuse ou tronquée peut basculer sur TCP 53. Une policy limitée à UDP peut sembler intermittente : les petites réponses passent, les plus grandes échouent.

Séparer les noms du cluster des noms upstream

Testez un FQDN de service Kubernetes et un nom upstream dans la même fenêtre.

  • Si les noms de service du cluster échouent, concentrez-vous sur resolver du pod, service DNS, endpoints, policies et data plane du nœud.
  • Si les noms du cluster passent mais tous les suffixes upstream échouent, examinez le forwarding CoreDNS et la joignabilité de ses resolvers.
  • Si un seul suffixe échoue, inspectez la règle de forwarding, le resolver privé, le serveur DNS custom ou la délégation de zone correspondants.
  • Si seule une famille de réponse ou une réponse volumineuse échoue, testez le fallback TCP et les preuves de MTU avant d’augmenter les timeouts.

Lisez la configuration CoreDNS déployée et l’éventuel override supporté sans les modifier à chaud :

bash 03-lire-configuration-dns.sh
kubectl -n kube-system get configmap coredns -o yaml
kubectl -n kube-system get configmap coredns-custom -o yaml 2>/dev/null || true
kubectl -n kube-system logs deployment/coredns --since=20m --prefix
kubectl -n kube-system top pods -l k8s-app=kube-dns

La ConfigMap custom peut ne pas exister, ce qui est normal. Cherchez un changement de forwarding, un server block invalide, une erreur de reload, une boucle ou des timeouts. Corrélez-les à la fenêtre UTC et au suffixe d’origine. Des logs hors fenêtre donnent du contexte, pas une preuve.

Pour un DNS custom ou hybride, interrogez l’upstream depuis un chemin de diagnostic approuvé qui partage la joignabilité réseau de CoreDNS. Validez UDP et TCP 53, la latence, le comportement de récursion et la réponse attendue pour la zone. Un Private Endpoint peut consommer une zone privée, mais il ne constitue pas le modèle du diagnostic : les preuves doivent suivre le suffixe et la chaîne de forwarding réels.

Isoler une panne liée au nœud

Lorsque les erreurs suivent un nœud, comparez-le à un pair sain : version d’image, conditions de pression, redémarrages récents, santé des agents réseau, routes effectives et événements de policy. Vérifiez si tous les pods du nœud sont touchés ou seulement ceux d’un workload.

Ne drainez pas le nœud avant d’avoir enregistré sa liste de pods, les timestamps et la matrice de tests DNS. Le drain déplace les clients et peut faire disparaître le symptôme sans identifier le chemin défaillant. Si le confinement impose un cordon, bloquez d’abord les nouveaux placements, gardez le nœud disponible pour une collecte bornée, puis ne drainez qu’après validation de la capacité et de la disruption des workloads.

Un cache local au nœud, s’il est volontairement déployé, ajoute un saut de résolution. Prouvez que le pod pointe vers son adresse, que le listener répond et qu’il joint le DNS du cluster ou l’upstream. Ne supposez pas que NodeLocal DNSCache est actif parce que la panne suit un nœud.

Appliquer la correction réversible la plus étroite

Choisissez la correction à partir des preuves :

  • rollbacker la release applicative si la forme des requêtes ou les retries ont amplifié la charge ;
  • rollbacker l’override CoreDNS si les erreurs commencent avec sa resource version ;
  • restaurer UDP et TCP 53 sur le chemin exact lorsque les verdicts de policy prouvent le refus ;
  • corriger une seule règle de forwarding ou le chemin d’un resolver lorsque seul son suffixe échoue ;
  • scaler CoreDNS seulement si utilisation durable, attente ou perte de requêtes prouvent une pression de capacité ;
  • recycler un pod ou un nœud défaillant après conservation des preuves de cohorte et validation de la capacité restante.

Un redémarrage global de CoreDNS est un changement d’exploitation, pas une commande de diagnostic. S’il est indispensable pour restaurer le service, consignez-le comme mesure de confinement, conservez le ReplicaSet et la configuration précédents, puis poursuivez l’analyse de cause. La réussite du restart ne valide pas la cause.

Décider reprise ou rollback

Réutilisez les noms d’origine, les namespaces touchés et les cohortes de nœuds. Incluez un service du cluster, chaque suffixe upstream utile, une réponse négative attendue et au moins une résolution capable d’utiliser TCP.

text porte-reprise-dns.txt
PROMOUVOIR OU REPRENDRE si
Les requêtes répétées respectent la fenêtre de succès et de latence convenue
Les résultats passent depuis chaque cohorte de nœud et de namespace requise
Les noms cluster et upstream suivent les chemins attendus
UDP et TCP DNS sont autorisés lorsque nécessaires
CoreDNS garde des endpoints prêts sans pression de ressources
La requête applicative d'origine réussit sans croissance cachée des retries

MAINTENIR LE BLOCAGE si
Le succès dépend des retries ou d'un seul endpoint resolver
Un nœud, suffixe ou type de réponse reste inexpliqué
Un logging verbeux ou un accès temporaire reste nécessaire

ROLLBACKER si
Le changement candidat augmente SERVFAIL, timeouts ou volume de requêtes
La correction élargit la policy au-delà des dépendances DNS
La configuration applicative ou DNS connue comme saine restaure le contrat

Supprimez le logging temporaire, les pods de diagnostic et les policies d’urgence après la fenêtre d’observation. Conservez la matrice de tests et la décision avec l’incident : une récurrence doit repartir de discriminants connus.

Conclusion

Un DNS intermittent dans AKS n’est pas une raison de redémarrer CoreDNS par défaut. C’est un chemin à décomposer : resolver et forme de requête du client, service DNS Kubernetes, endpoints CoreDNS, policy et data plane réseau, forwarding upstream, puis comportement propre à un nœud.

La décision de production est explicite. Reprenez lorsque le même contrat de résolution passe sur toutes les cohortes requises sans amplification des retries. Rollbackez la release, la configuration DNS ou la policy qui a modifié la couche défaillante. Si les preuves dépendent encore d’un restart ou d’un retry chanceux, l’incident est contenu, pas résolu.