Mensonges du cache DNS Docker : vider le bon cache (conteneurs vs hôte)

Cet article vous a aidé ?

Vous changez un enregistrement DNS. Vous attendez. Vous faites même le rituel : « tout va bien, le TTL est faible. »
Et pourtant un conteneur continue d’appeler l’ancienne IP comme s’il vivait une relation à distance avec votre ancien équilibreur de charge.

Le piège est simple : vous videz un cache, pas le cache. Docker ajoute au moins une couche de résolveur, Linux en ajoute d’autres, et votre application peut garder des réponses comme si elle payait un loyer par requête. Si vous ne savez pas où se trouve le mensonge, vous continuerez à courir après des fantômes.

Un modèle mental pratique : où les réponses DNS peuvent rester coincées

Le dépannage DNS dans des conteneurs n’est rarement une question de « DNS en panne ». Il s’agit de temps, de contexte et de caches dans des endroits dont vous avez oublié l’existence.
La façon la plus rapide de débloquer la situation est d’arrêter de demander « quel est mon DNS ? » et de commencer à demander « qui a répondu, qui a mis en cache, et qui réutilise encore la réponse ? »

Le DNS a plusieurs « clients », pas un seul

Une application conteneurisée ne parle généralement pas directement à vos serveurs DNS d’entreprise. Elle parle à quelque chose de plus proche :

  • La logique de résolution propre à votre application (JVM, comportement de net.Resolver de Go, cache de Node, Envoy, règles de résolution en amont d’nginx).
  • libc + NSS (le résolveur de glibc, musl, /etc/nsswitch.conf, /etc/hosts, domaines de recherche, ndots, timeout/attempts).
  • Un démon de mise en cache local (nscd, dnsmasq, systemd-resolved) — parfois à l’intérieur du conteneur, souvent sur l’hôte.
  • Le DNS embarqué de Docker (souvent à 127.0.0.11) qui relaie et assure la découverte de services nommés sur les réseaux définis par l’utilisateur.
  • Les résolveurs en amont de l’hôte (DNS d’entreprise, résolveur VPC, CoreDNS, Unbound, bind).

Si vous videz le cache de l’hôte mais que l’application met en cache indéfiniment, rien ne change. Si vous redémarrez le conteneur mais que le résolveur embarqué de Docker renvoie toujours quelque chose d’étrange, rien ne change non plus.
Il faut isoler quelle couche sert des réponses périmées.

« Vider le DNS » n’est pas une seule commande

Sur Linux seulement, « vider le DNS » peut signifier :

  • redémarrer systemd-resolved ou vider ses caches,
  • redémarrer nscd ou dnsmasq,
  • redémarrer Docker (ou seulement les conteneurs / réseaux affectés),
  • redémarrer le processus applicatif pour effacer son cache interne,
  • ou vider un résolveur en amont que vous ne contrôlez pas.

Le bon vidage est celui qui modifie réellement le chemin de la requête suivante.

Un principe opérationnel qui vieillit bien : observer avant d’intervenir. Vider les caches efface des preuves. Prenez quelques instantanés « avant », puis videz le minimum nécessaire.

Une citation à garder près de votre cerveau en on-call : « L’espoir n’est pas une stratégie. » — Général Gordon R. Sullivan

Faits intéressants et un peu d’histoire (pour arrêter d’accuser la « magie Docker »)

  • Le cache DNS n’était pas toujours attendu au niveau du système. Les anciens résolveurs Unix tendaient à être de simples résolveurs stub ; la mise en cache est devenue courante à mesure que les réseaux ralentissaient et que les recherches de noms se sont multipliées.
  • Le TTL est consultatif, pas une loi universelle. Un résolveur peut plafonner les TTL (min/max), et les applications peuvent mettre en cache plus longtemps que le DNS ne l’indique — surtout quand elles veulent « aider ».
  • Docker a ajouté un DNS embarqué pour rendre la découverte de services sur réseaux définis par l’utilisateur viable. Sinon, la résolution de noms conteneur-à-conteneur devient rapidement pénible.
  • Le réseau pont par défaut de Docker copiait historiquement le comportement de resolv.conf de l’hôte. Cela faisait hériter aux conteneurs des particularités DNS de l’hôte — bonnes et mauvaises — jusqu’à ce que le DNS embarqué devienne la norme sur les réseaux personnalisés.
  • systemd-resolved a changé les attentes sur de nombreuses distributions. Il a introduit un résolveur stub local (souvent 127.0.0.53) avec DNS partagé et mise en cache, qui interagit avec Docker de façon surprenante.
  • Le comportement du résolveur d’Alpine (musl) diffère de Debian/Ubuntu (glibc). Les modes d’échec autour des domaines de recherche, ndots et des timeouts peuvent ressembler à un « DNS aléatoire ».
  • Le comportement du résolveur Go a évolué selon les versions. Selon les flags de compilation et l’environnement, il peut utiliser le résolveur Go pur ou cgo (glibc), ce qui impacte la mise en cache et le parsing de configuration.
  • Le DNS d’entreprise fait souvent sa propre mise en cache et des réécritures « utiles ». Le DNS à horizon partagé, les zones internes et les réponses basées sur des politiques font que le même nom peut résoudre différemment selon que vous êtes à l’intérieur ou à l’extérieur d’un VPN.

Ce dernier point compte parce que « ça marche sur mon laptop » peut être littéralement vrai : votre laptop est dans une vue DNS différente de celle de l’hôte du conteneur.

La pile de cache DNS : à l’intérieur du conteneur, sur l’hôte et en amont

Couche 0 : l’application elle-même (la menteuse la plus courante)

Si vous ne devez retenir qu’une chose : beaucoup d’applications mettent en cache le DNS plus longtemps que vous ne le pensez, et certaines mettent en cache effectivement pour toujours à moins d’une nouvelle résolution.
Schémas courants :

  • Les pools de connexions gardent des sockets vers d’anciennes IP. Le DNS peut changer et l’application s’en fiche tant que le pool ne se renouvelle pas.
  • Caches DNS au runtime (cache d’InetAddress de Java, certains clients HTTP, maillages de services).
  • Processus longue durée qui résolvent une fois au démarrage et jamais ensuite.

Conséquence opérationnelle : vider le DNS en dessous de l’application ne fait rien si l’application ne redemande jamais.
Votre meilleur « vidage » peut être un redémarrage progressif ou un signal qui force le rechargement (si supporté).

Couche 1 : libc, NSS et configuration du résolveur

C’est là que vivent /etc/resolv.conf, /etc/nsswitch.conf, /etc/hosts, les domaines de recherche et des options comme ndots.
Cette couche ne « met pas en cache » agressivement en elle-même (glibc ne conserve pas un grand cache partagé), mais elle peut créer des comportements qui ressemblent à de la mise en cache :

  • domaines de recherche + ndots provoquent plusieurs requêtes par recherche. L’une de ces requêtes peut réussir et rester dans les caches en amont.
  • timeout/attempts provoquent de longues attentes qui ressemblent à des pannes partielles.
  • /etc/hosts remplace entièrement le DNS, donnant un « DNS périmé » qui est en réalité un fichier statique.

Couche 2 : démons de cache (nscd, dnsmasq, systemd-resolved)

Si un nœud exécute un résolveur de cache local, les conteneurs peuvent l’interroger directement (via un resolv.conf copié) ou indirectement (Docker relaie vers lui).
Si ce démon met en cache de mauvaises données, les conteneurs continueront de les voir jusqu’à expiration du cache du démon ou jusqu’à son nettoyage.

Sur les Ubuntu modernes, systemd-resolved se place souvent sur 127.0.0.53, agissant comme un stub local et un cache.
Docker a parfois du mal si il copie 127.0.0.53 dans les conteneurs : cette adresse est dans l’espace de noms du conteneur et ne pointe pas vers le stub de l’hôte à moins qu’un routage spécial n’existe.
Ce mode d’échec n’est pas une mise en cache ; c’est un décalage d’espace de noms.

Couche 3 : DNS embarqué de Docker (127.0.0.11)

Sur les réseaux bridge définis par l’utilisateur, Docker injecte typiquement nameserver 127.0.0.11 dans le /etc/resolv.conf du conteneur.
Cette IP n’est pas le résolveur de l’hôte ; c’est le résolveur en moteur de Docker lié à l’intérieur de l’espace réseau du conteneur.

Ce qu’il fait bien :

  • résoudre les noms de conteneurs vers des IP de conteneur au sein du réseau Docker,
  • gérer les alias et la découverte de services dans Compose,
  • relayer les autres requêtes vers les résolveurs en amont configurés pour Docker/hôte.

Ce qu’il fait mal (ou du moins de façon opaque) :

  • il devient un saut supplémentaire où les timeouts et le comportement de cache peuvent être mal interprétés,
  • il masque les changements de résolveur en amont à moins que les conteneurs soient recréés ou que Docker recharge la config,
  • il rend « vider le DNS » ambigu parce que vous ne gérez pas directement ce cache comme un démon habituel.

Couche 4 : résolveurs en amont (CoreDNS, Unbound, bind, résolveurs cloud)

Les résolveurs en amont mettent en cache selon le TTL, mais ils ont aussi :

  • mise en cache négative (NXDOMAIN mis en cache pendant un certain temps),
  • préfetch et fonctionnalités serve-stale dans certaines implémentations,
  • politique / DNS partagé qui modifie les réponses selon le réseau source.

Si vous ne contrôlez pas la mise en cache en amont, le seul « vidage » fiable est d’interroger un autre résolveur (temporairement) ou d’attendre l’expiration du TTL/du TTL négatif.

Blague n°1 : DNS signifie « Définitivement Pas Synchronisé ». Ce n’est pas vrai, mais ça explique la plupart des week-ends d’astreinte.

Mode d’intervention rapide

L’objectif ici n’est pas de devenir un savant du DNS. C’est de trouver rapidement le goulot et le menteur, avec un minimum de dégâts collatéraux.

Première étape : vérifier le symptôme et la portée

  • Est-ce un seul conteneur, un seul hôte, ou tous les hôtes ? Les problèmes sur un conteneur indiquent généralement un cache applicatif, la configuration du conteneur, ou un chemin de résolveur spécifique à l’espace de noms.
  • Est-ce un seul nom ou tous les noms ? Un seul nom pointe vers un enregistrement DNS ou de la mise en cache ; tous les noms pointent vers la connectivité du résolveur ou la configuration.
  • Est-ce périmé (ancienne IP) ou échec (NXDOMAIN/timeouts) ? Périmé = cache ; timeouts = réseau/MTU/firewall ; NXDOMAIN = mise en cache négative ou DNS partagé.

Deuxième étape : identifier quel résolveur le conteneur utilise

  • Vérifiez /etc/resolv.conf dans le conteneur : 127.0.0.11 signifie DNS embarqué Docker ; tout autre chose signifie résolution directe.
  • Vérifiez le mode de réseau Docker et s’il s’agit d’un bridge défini par l’utilisateur, du réseau host, ou d’une configuration particulière.

Troisième étape : faire des requêtes côte à côte depuis le conteneur et l’hôte

  • Interrogez le nom depuis le conteneur avec dig contre le résolveur configuré.
  • Interrogez le même nom depuis l’hôte contre le(s) résolveur(s) en amont.
  • Si les réponses diffèrent, la divergence se situe entre le stub du conteneur et l’amont. Si les réponses correspondent mais que l’appli appelle toujours l’ancienne IP, le menteur est l’application ou le pool de connexions.

Quatrième étape : décider du plus petit reset qui change le comportement

  • Si l’appli met en cache : redémarrez le processus ou forcez la re-résolution (si supporté).
  • Si le cache de l’hôte est en cause : videz systemd-resolved/dnsmasq/nscd.
  • Si le chemin DNS embarqué Docker est suspect : recréez le conteneur (ou le réseau), ou ajustez les paramètres DNS du démon Docker et redémarrez Docker dans une fenêtre contrôlée.
  • Si le cache en amont est en cause : interrogez temporairement un autre résolveur, ou attendez l’expiration du TTL/du TTL négatif.

Tâches pratiques : commandes, signification des sorties et décisions (12+)

Task 1: Find the container’s configured resolver

cr0x@server:~$ docker exec -it web-1 cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0

Ce que cela signifie : Ce conteneur utilise le DNS embarqué de Docker (127.0.0.11). Vider les caches de l’hôte peut ne pas modifier directement ce qu’il voit si Docker met en cache/relaye de façon étrange.

Décision : Interrogez avec dig depuis le conteneur pour voir ce que renvoie le DNS Docker ; inspectez aussi la configuration DNS du démon Docker.

Task 2: Confirm whether the container is on a user-defined network

cr0x@server:~$ docker inspect -f '{{json .NetworkSettings.Networks}}' web-1
{"app_net":{"IPAMConfig":null,"Links":null,"Aliases":["web-1","web"],"NetworkID":"8d0e...","EndpointID":"0c4c...","Gateway":"172.20.0.1","IPAddress":"172.20.0.10","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:14:00:0a","DriverOpts":null}}

Ce que cela signifie : Réseau bridge défini par l’utilisateur (app_net) implique généralement que le DNS embarqué est actif et que la découverte de services est en jeu.

Décision : Si le problème est « le nom de service pointe vers le mauvais conteneur », vous déboguez le DNS réseau Docker, pas le DNS d’entreprise.

Task 3: Compare DNS answers inside the container using dig

cr0x@server:~$ docker exec -it web-1 sh -lc 'apk add --no-cache bind-tools >/dev/null 2>&1 || true; dig +noall +answer api.internal A'
api.internal.         30      IN      A       10.40.12.34

Ce que cela signifie : Le résolveur de Docker (ou ce vers quoi il relaie) renvoie actuellement 10.40.12.34 avec un TTL de 30 secondes.

Décision : Si vous attendiez une nouvelle IP et que le TTL est passé, quelque chose met en cache au-dessus ou en dessous. Interrogez maintenant depuis l’hôte et depuis un résolveur en amont spécifique.

Task 4: Query the same name from the host against its default resolver

cr0x@server:~$ dig +noall +answer api.internal A
api.internal.         30      IN      A       10.40.12.99

Ce que cela signifie : L’hôte voit 10.40.12.99, le conteneur voit 10.40.12.34. La divergence est réelle et locale.

Décision : Le résolveur embarqué Docker pourrait relayer vers des résolveurs amont différents de ceux de l’hôte, ou mettre en cache des données périmées.

Task 5: Find what DNS servers Docker daemon is using

cr0x@server:~$ docker info | sed -n '/DNS:/,/Registry Mirrors:/p'
DNS: 10.0.0.2
  10.0.0.3

Ce que cela signifie : Docker est configuré pour utiliser 10.0.0.2 et 10.0.0.3 comme résolveurs en amont pour le relais du DNS embarqué.

Décision : Interrogez directement ces résolveurs depuis l’hôte pour voir ce qu’ils renvoient et s’ils ont un cache périmé.

Task 6: Query Docker’s upstream resolver directly

cr0x@server:~$ dig @10.0.0.2 +noall +answer api.internal A
api.internal.         30      IN      A       10.40.12.34

Ce que cela signifie : Le résolveur en amont que Docker utilise renvoie l’ancienne IP. Docker ne ment pas nécessairement ; il répète fidèlement le mensonge de quelqu’un d’autre.

Décision : Corrigez ou videz le cache du résolveur en amont, ou pointez Docker vers les bons résolveurs.

Task 7: Detect systemd-resolved involvement on the host

cr0x@server:~$ resolvectl status | sed -n '1,35p'
Global
         Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
  resolv.conf mode: stub
Current DNS Server: 10.0.0.2
       DNS Servers: 10.0.0.2 10.0.0.3
        DNS Domain: corp.internal

Ce que cela signifie : L’hôte utilise systemd-resolved en mode stub ; il peut mettre en cache, et il peut aussi être la source du contenu de /etc/resolv.conf que Docker lit.

Décision : Si les conteneurs copient une adresse stub non atteignable dans leur espace de noms, corrigez la config DNS de Docker ; si le cache de l’hôte est mauvais, videz resolved.

Task 8: Flush systemd-resolved cache on the host (when it is the cache)

cr0x@server:~$ sudo resolvectl flush-caches
cr0x@server:~$ resolvectl statistics | sed -n '1,25p'
DNSSEC supported by current servers: no
Transactions: 1482
Cache Hits: 312
Cache Misses: 1170

Ce que cela signifie : Le cache a été vidé ; les recherches suivantes devraient être des misses jusqu’à ce qu’il se réchauffe.

Décision : Relancez les requêtes hôte/conteneur. Si le conteneur voit toujours de vieilles réponses, systemd-resolved n’était pas le goulot.

Task 9: Check whether the container can even reach the host’s stub resolver

cr0x@server:~$ docker exec -it web-1 sh -lc 'grep -E "^nameserver" /etc/resolv.conf; nc -zu -w1 127.0.0.53 53; echo $?'
nameserver 127.0.0.11
1

Ce que cela signifie : Le conteneur n’est de toute façon pas configuré pour utiliser 127.0.0.53, et même s’il l’était, cette adresse serait locale à l’espace de noms du conteneur.

Décision : Cessez d’essayer de « vider le stub de l’hôte » comme correctif pour un conteneur utilisant 127.0.0.11. Travaillez sur le DNS Docker/amont.

Task 10: Determine if the application is pinning connections to old IPs

cr0x@server:~$ docker exec -it web-1 sh -lc 'ss -tnp | head -n 10'
State  Recv-Q Send-Q Local Address:Port   Peer Address:Port  Process
ESTAB  0      0      172.20.0.10:49218   10.40.12.34:443    users:(("app",pid=1,fd=73))
ESTAB  0      0      172.20.0.10:49222   10.40.12.34:443    users:(("app",pid=1,fd=74))

Ce que cela signifie : L’application est actuellement connectée à l’ancienne IP. Même si le DNS résout maintenant vers la nouvelle IP, les sockets actifs continuent de fonctionner.

Décision : Forcer le renouvellement des connexions (reload, restart, réduire keepalive), ou corriger le comportement des pools. Vider le DNS ne détruira pas les sessions TCP établies.

Task 11: Inspect /etc/hosts inside container for “DIY DNS” surprises

cr0x@server:~$ docker exec -it web-1 cat /etc/hosts
127.0.0.1	localhost
172.20.0.10	web-1
10.40.12.34	api.internal

Ce que cela signifie : Quelqu’un a épinglé api.internal dans /etc/hosts. Ce n’est pas un cache. C’est une substitution permanente.

Décision : Supprimez l’entrée (reconstruisez l’image, corrigez l’entrypoint, ou arrêtez d’injecter des enregistrements hosts). Puis redéployez. Vider un cache DNS est sans objet tant que ceci existe.

Task 12: Check NSS order (hosts vs dns) inside container

cr0x@server:~$ docker exec -it web-1 sh -lc 'cat /etc/nsswitch.conf | sed -n "1,25p"'
passwd:         files
group:          files
hosts:          files dns
networks:       files

Ce que cela signifie : files est vérifié avant DNS. Si /etc/hosts contient une entrée, elle l’emporte.

Décision : Si vous devez utiliser /etc/hosts pour une migration ponctuelle, traitez-le comme une configuration contrôlée, pas comme un bricolage qu’on oublie.

Task 13: Observe negative caching (NXDOMAIN) behavior

cr0x@server:~$ docker exec -it web-1 sh -lc 'dig +noall +authority does-not-exist.corp.internal'
corp.internal.        300     IN      SOA     ns1.corp.internal. hostmaster.corp.internal. 2026010301 3600 600 604800 300

Ce que cela signifie : Le SOA indique un TTL négatif de 300 secondes. NXDOMAIN peut être mis en cache pendant 5 minutes par les résolveurs.

Décision : Si vous venez de créer l’enregistrement, attendre peut être la bonne option. Ou interrogez directement les serveurs autoritaires si vous le pouvez.

Task 14: Check Docker daemon config for fixed DNS settings

cr0x@server:~$ sudo cat /etc/docker/daemon.json
{
  "dns": ["10.0.0.2", "10.0.0.3"],
  "log-driver": "json-file"
}

Ce que cela signifie : Les résolveurs en amont de Docker sont épinglés. Si le DNS d’entreprise a changé, Docker ne suivra pas automatiquement la modification de l’hôte.

Décision : Mettez à jour ce fichier (via gestion de configuration), puis redémarrez Docker dans une fenêtre de maintenance, et recréez les conteneurs si nécessaire.

Task 15: Confirm what the host’s /etc/resolv.conf actually is

cr0x@server:~$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Jan  3 09:12 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf

Ce que cela signifie : L’hôte pointe vers le resolv.conf stub de systemd. Docker qui lit et copie ceci dans les conteneurs peut être catastrophique si cela aboutit à nameserver 127.0.0.53 à l’intérieur des conteneurs.

Décision : Préférez des paramètres DNS explicites pour Docker (daemon.json) ou assurez-vous que Docker utilise le fichier resolv.conf « réel » si votre distro le fournit (/run/systemd/resolve/resolv.conf) via la config du démon.

Task 16: Test the path from container to resolver and measure latency

cr0x@server:~$ docker exec -it web-1 sh -lc 'time dig +tries=1 +timeout=2 api.internal A +noall +answer'
api.internal.         30      IN      A       10.40.12.34

real	0m0.018s
user	0m0.009s
sys	0m0.004s

Ce que cela signifie : La recherche est rapide. Si votre application signale « timeout DNS », elle peut effectuer plusieurs recherches (domaines de recherche) ou utiliser un chemin de résolveur différent de votre outil de test.

Décision : Vérifiez les domaines de recherche et ndots ; inspectez le comportement du résolveur applicatif ; capturez le trafic si nécessaire.

Trois mini-récits du monde de l’entreprise (anonymisés)

Incident : la mauvaise hypothèse (« on a vidé le DNS, donc ça doit aller »)

Une société SaaS de taille moyenne a déplacé une API interne derrière une nouvelle VIP lors d’un rangement de datacenter. Ils ont abaissé le TTL des jours à l’avance, surveillé les tableaux de bord, et effectué le basculement un mardi matin parce que personne n’aime un vendredi.
La moitié du parc a migré proprement. Quelques pods d’application ne l’ont pas fait. Les erreurs ont augmenté, mais seulement pour un sous-ensemble de clients.

Le canal d’incident s’est rempli de la folklore DNS habituel. Quelqu’un a vidé systemd-resolved sur quelques nœuds. Quelqu’un d’autre a redémarré Docker sur un hôte. Un troisième a lancé dig sur son laptop, obtenu la nouvelle IP, et déclaré victoire.
Pendant ce temps, un ensemble de conteneurs workers longue durée continuait de parler à l’ancienne IP. Ils ne faisaient même plus de DNS ; ils avaient des pools de connexions maintenant des sessions TLS ouvertes pendant des heures.

L’indice est venu de l’intérieur du conteneur : ss -tnp montrait des connexions établies vers l’ancienne adresse. Le DNS était « correct » et restait néanmoins hors sujet.
Le correctif n’était pas un vidage de cache. Ce fut un redémarrage contrôlé des workers (rolling, avec vidage des files) plus un réglage de la durée de vie des connexions clients.

Les actions postmortem furent ennuyeuses mais correctes : documenter que « DNS changé » n’est pas synonyme de « le trafic a bougé », ajouter un canari qui vérifie les IPs des pairs réels, et rendre la durée de vie des connexions configurable plutôt que codée en dur.

Optimisation qui s’est retournée contre eux : « ajoutons un cache pour accélérer le DNS »

Une autre équipe avait un problème légitime : leurs hôtes effectuaient beaucoup de recherches DNS, et des pics de latence apparaissaient parfois dans les traces de requêtes.
La solution évidente fut de déployer un résolveur de cache local sur chaque nœud. Ils ont installé dnsmasq et pointé l’hôte dessus. Les recherches sont devenues plus rapides. Tout le monde a célébré.

Puis survint un incident apparemment sans lien : un déploiement blue/green a basculé un service interne vers une nouvelle IP, mais un segment de conteneurs a continué de toucher l’ancien backend bien plus longtemps que le TTL.
Le résolveur de cache avait été configuré avec des TTL minimums agressifs « pour réduire la charge ». Ce n’était pas malveillant ; c’était « efficace ».

Malheureusement, l’efficacité est la manière de créer des réponses périmées à grande échelle.
L’équipe a d’abord chassé Docker, parce que les conteneurs étaient le changement visible. Mais le vrai problème était la politique de cache sur dnsmasq combinée à la mise en cache négative pour un enregistrement qui avait disparu brièvement pendant le basculement.
Ils ont corrigé en alignant le comportement du cache avec leur processus de changement réel : arrêter d’outrepasser les TTL à moins d’être prêt à en assumer les conséquences, et ajouter une étape de runbook pour valider les réponses en amont lors des migrations.

Leçon : la mise en cache est une fonctionnalité de performance qui devient une fonctionnalité de fiabilité seulement lorsqu’elle est configurée comme si vous alliez être alerté pour elle. Sinon c’est une bombe à retardement avec une fuse configurable.

Ennuyant mais efficace : la pratique qui a sauvé la situation

Une entreprise du secteur financier exploitait des hôtes Docker sur deux réseaux : LAN d’entreprise et un segment PCI restreint.
Le DNS était à horizon partagé : le même nom pouvait résoudre différemment selon l’endroit. C’était intentionnel, audité, et pénible.

Leur équipe SRE avait l’habitude, qui ressemblait à de la bureaucratie : chaque image conteneur incluait une petite boîte à outils de diagnostic et chaque runbook d’astreinte commençait par « afficher resolv.conf et interroger le résolveur exact indiqué ».
Les gens levaient les yeux au ciel — jusqu’à une panne un jeudi soir.

Un déploiement dans le segment PCI n’a pas réussi à atteindre un nom de service interne. Depuis les laptops ça marchait. Depuis certains hôtes ça marchait. Depuis d’autres non.
Le runbook a rapidement séparé le problème : les conteneurs utilisaient le DNS embarqué Docker qui relayait vers un résolveur d’entreprise qui n’avait pas la vue PCI.
Quelqu’un avait « standardisé » les serveurs DNS dans daemon.json à travers les environnements plus tôt dans la semaine.

Comme ils avaient documenté le chemin du résolveur et conservé des étapes de diagnostic cohérentes, ils ont inversé le changement de daemon.json en quelques minutes, redémarré Docker sur un petit sous-ensemble, validé, puis déployé la correction en toute sécurité.
Pas d’héroïsme. Pas de captures de paquets à minuit. Juste de l’observation disciplinée et reproductible.

Blague n°2 : Ajouter un cache DNS pour améliorer la fiabilité, c’est comme ajouter un deuxième frigo pour régler la faim — vous allez juste stocker plus de mauvaises décisions pour plus tard.

Erreurs courantes : symptôme → cause racine → correctif

1) Symptom: “Container resolves old IP, host resolves new IP”

Cause racine : Le conteneur utilise le DNS embarqué Docker qui relaie vers des résolveurs en amont différents (daemon.json épinglé, ou Docker a choisi des serveurs différents de l’hôte).

Correctif : Comparez le DNS de docker info avec le résolveur de l’hôte ; interrogez directement les résolveurs en amont de Docker ; mettez à jour la configuration DNS du démon Docker et redémarrez Docker de manière contrôlée.

2) Symptom: “Flushing host DNS cache changed nothing”

Cause racine : L’application met en cache le DNS ou maintient des connexions persistantes ; elle ne redemande jamais.

Correctif : Redémarrez ou rechargez l’appli ; configurez la durée maximale des connexions ; réduisez keepalive si c’est sans danger ; vérifiez avec ss que l’IP du pair change.

3) Symptom: “DNS works on host, container gets timeouts”

Cause racine : Le conteneur a nameserver 127.0.0.53 copié depuis l’hôte, mais ce n’est pas joignable dans l’espace de noms du conteneur.

Correctif : Configurez Docker pour utiliser des résolveurs en amont réels (pas le stub de l’hôte), ou assurez-vous que Docker utilise le resolv.conf non-stub ; recréez les conteneurs.

4) Symptom: “One specific name always resolves ‘wrong’ inside container”

Cause racine : Entrée dans /etc/hosts dans l’image ou injectée au runtime ; NSS vérifie files avant DNS.

Correctif : Supprimez l’entrée hosts ; corrigez le build/entrypoint ; validez l’ordre et le contenu de hosts: files dns.

5) Symptom: “Intermittent NXDOMAIN after creating a record”

Cause racine : Mise en cache négative (SOA minimum/negative TTL) dans les résolveurs en amont ou caches locaux.

Correctif : Inspectez le TTL négatif du SOA ; attendez son expiration ou interrogez les serveurs autoritaires ; évitez les patterns supprimer-recréer pendant les migrations.

6) Symptom: “DNS is slow only in containers”

Cause racine : Domaines de recherche et ndots provoquant plusieurs requêtes ; ou résolveur en amont joignable depuis l’hôte mais pas depuis l’espace réseau du conteneur à cause de règles de firewall.

Correctif : Inspectez les options de /etc/resolv.conf ; réduisez les domaines de recherche ; assurez la connectivité UDP/TCP 53 depuis l’espace de noms du conteneur ; mesurez avec des dig chronométrés.

7) Symptom: “Docker Compose service names resolve inconsistently”

Cause racine : Multiples réseaux, alias, ou conteneurs périmés ; le DNS embarqué répond différemment selon l’attachement réseau.

Correctif : Inspectez les attachements réseau ; assurez-vous que les services partagent le réseau prévu ; supprimez les conteneurs orphelins et recréez le réseau du projet.

8) Symptom: “After changing corporate DNS, some hosts never pick it up”

Cause racine : Le démon Docker a des serveurs DNS épinglés ; les conteneurs continuent d’utiliser le DNS embarqué qui relaie vers les anciens serveurs.

Correctif : Mettez à jour daemon.json via gestion de configuration ; redémarrez Docker dans une fenêtre de maintenance ; redéployez les conteneurs créés sous l’ancienne config.

Listes de contrôle / plan étape par étape (ennuyeux volontairement)

Étape par étape : isoler le menteur en 15 minutes

  1. Choisissez un conteneur défaillant et un conteneur sain (si vous en avez un). Même image de préférence.
  2. Consignez la config du résolveur du conteneur : cat /etc/resolv.conf, cat /etc/nsswitch.conf, et cat /etc/hosts.
  3. Interrogez le nom avec un outil qui affiche le TTL (dig) depuis l’intérieur du conteneur.
  4. Interrogez le même nom depuis l’hôte et contre les résolveurs en amont spécifiques que Docker utilise.
  5. Si les réponses DNS diffèrent : identifiez la première couche où elles divergent (amont Docker vs amont hôte vs DNS partagé).
  6. Si les réponses DNS correspondent mais que l’appli utilise toujours l’ancienne IP : inspectez les connexions TCP existantes, les paramètres de pool, et le cache applicatif.
  7. Appliquez le plus petit changement qui force un nouveau chemin de résolution : videz le bon cache ou redémarrez le bon processus.
  8. Validez avec des preuves : montrez la nouvelle réponse DNS et montrez la nouvelle IP du pair dans ss ou les logs de requêtes.

Checklist de déploiement : éviter les surprises DNS

  • N’emballez pas des bricolages /etc/hosts dans les images sauf si vous fournissez aussi un plan de retrait.
  • Gardez la configuration DNS du démon Docker explicite par environnement ; ne supposez pas que le DNS de l’hôte est identique au DNS du conteneur.
  • Pour les services derrière un équilibrage DNS, ajustez la durée de vie des connexions clients pour que le trafic puisse réellement basculer.
  • Pendant les migrations, évitez les événements NXDOMAIN transitoires ; la mise en cache négative les fera persister.
  • Ayez un conteneur diagnostic canonique (ou une toolbox) disponible sur chaque hôte/cluster.

Plan de changement : mettre à jour le DNS Docker en toute sécurité sur une flotte d’hôtes

  1. Identifiez quels hôtes utilisent fortement le DNS embarqué Docker (réseaux définis par l’utilisateur, stacks Compose).
  2. Mettez à jour /etc/docker/daemon.json avec les bons serveurs dns et (si nécessaire) les options.
  3. Planifiez un redémarrage du démon Docker ; comprenez l’impact (les conteneurs peuvent redémarrer selon la politique).
  4. Redémarrez Docker sur un hôte canari d’abord ; validez le /etc/resolv.conf des conteneurs et les résultats de dig.
  5. Déployez sur la flotte ; recréez les conteneurs qui ont été créés avec l’ancienne config DNS si le comportement persiste.

FAQ

1) Pourquoi docker exec affiche-t-il nameserver 127.0.0.11 ?

C’est le DNS embarqué de Docker pour les réseaux définis par l’utilisateur. Il fournit la résolution de noms de conteneurs/services et relaie les autres requêtes en amont.
C’est normal, et c’est aussi la raison pour laquelle « vider le cache DNS de l’hôte » ne fait souvent pas ce que vous attendez.

2) Puis-je vider directement le cache DNS embarqué de Docker ?

Pas de manière propre « en une commande » comme systemd-resolved. Opérationnellement, vous l’influencez en changeant les résolveurs en amont, en recréant des conteneurs/réseaux, ou en redémarrant Docker.
Avant cela, prouvez que la couche embarquée est le point de divergence en comparant les réponses entre les couches.

3) Pourquoi redémarrer le conteneur corrige parfois le DNS ?

Cela peut changer plusieurs choses à la fois : le conteneur obtient un /etc/resolv.conf neuf, l’application redémarre et perd ses caches/pools internes, et Docker peut reconstruire une partie de l’état réseau.
C’est un instrument brutal. Utile, mais il masque la cause racine si vous le faites en premier.

4) Mon TTL est de 30 secondes. Pourquoi des réponses périmées ont-elles persisté pendant des minutes ?

Parce que le TTL s’applique aux caches DNS, pas aux connexions existantes de votre application, et pas nécessairement au cache applicatif. De plus, les résolveurs en amont peuvent plafonner ou imposer des TTL minimums, et la mise en cache négative a ses propres minuteries.

5) Pourquoi l’hôte résout correctement mais les conteneurs non après activation de systemd-resolved ?

Si les conteneurs se retrouvent avec nameserver 127.0.0.53, cela pointe vers eux-mêmes, pas vers le stub de l’hôte.
Configurez Docker pour utiliser des résolveurs en amont réels ou une IP de résolveur joignable, pas le stub loopback de l’hôte.

6) Dois-je exécuter un cache DNS dans chaque conteneur ?

Non, sauf si vous aimez déboguer plusieurs couches de caches sous pression d’incident.
Si vous avez besoin de mise en cache, faites-le au niveau du nœud (approprié et observable) ou via une couche de résolveur dédiée, et gardez les chemins de résolveur des conteneurs simples.

7) Quelle est la façon la plus rapide de prouver que ce n’est pas du tout un problème DNS ?

Montrez que dig renvoie la nouvelle IP, mais que ss -tnp affiche des connexions établies vers l’ancienne IP, ou que les logs de requêtes montrent l’ancien pair.
C’est du ressort du pooling de connexions / keepalive / cache applicatif.

8) Kubernetes change-t-il cette histoire ?

Oui, mais la forme est similaire : les conteneurs interrogent souvent CoreDNS, qui met en cache et relaie. Vous avez toujours la mise en cache applicative, le comportement libc, et la mise en cache en amont.
La principale différence est que le « DNS embarqué » est typiquement CoreDNS et les conventions kube-dns, pas le 127.0.0.11 de Docker.

9) Pourquoi nslookup de BusyBox n’est-il pas d’accord avec dig ?

Différents outils ont des comportements de résolveur, des sorties et parfois des paramètres par défaut différents (TCP vs UDP, comportement de recherche, retries).
Préférez dig pour la clarté (TTL, sections authority/additional) et utilisez des exécutions chronométrées pour repérer les retries.

10) Quand vider les caches est-il une mauvaise idée ?

Quand vous n’avez pas prouvé que la mise en cache est le problème. Les timeouts dus à un firewall, à la MTU, ou à une IP de résolveur injoignable ne seront pas résolus par un vidage.
De plus, vider les caches en amont peut provoquer un effet de charge si beaucoup de nœuds refont des requêtes en même temps.

Conclusion : prochaines étapes concrètes

Les problèmes DNS avec Docker ne sont rarement mystérieux. Ils sont superposés. Le mensonge se trouve généralement dans l’un de ces trois endroits :
l’application qui ne ré-résout jamais, le chemin du résolveur qui diffère entre l’hôte et le conteneur, ou un cache en amont qui fait exactement ce pour quoi il est configuré.

La prochaine fois que vous verrez un « DNS périmé » :

  • Commencez par afficher /etc/resolv.conf à l’intérieur du conteneur et arrêtez de deviner.
  • Faites des dig côte à côte depuis le conteneur et l’hôte, puis directement contre les résolveurs en amont de Docker.
  • Si le DNS est correct mais le trafic dévie, inspectez les connexions établies et redémarrez le bon processus, pas l’univers.
  • Standardisez une petite boîte à outils de diagnostic et un runbook qui impose la collecte de preuves avant de vider quoi que ce soit.

Si vous faites ces quatre choses de façon cohérente, les « mensonges du cache DNS » deviennent une nuisance gérable plutôt qu’un thème d’incident récurrent.

← Précédent
Latence de réplication MySQL vs PostgreSQL : pourquoi elle survient et comment la réduire
Suivant →
Page d’atterrissage HTML/CSS pure : Hero, Fonctionnalités, Tarifs, FAQ (style Documentation)

Laisser un commentaire