Chaque pod Kubernetes reçoit par défaut un resolv.conf configuré avec ndots:5 : tout nom à résoudre contenant moins de 5 points est d’abord essayé avec chacun des domaines de recherche du cluster, avant d’être tenté tel quel. Ce détail, invisible en fonctionnement normal, multiplie silencieusement le nombre de requêtes DNS pour toute résolution vers un domaine externe.

Ce que resolv.conf contient réellement dans un pod

cat /etc/resolv.conf
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5

ndots:5 signifie : si le nom à résoudre contient moins de 5 points, essayer d’abord chaque suffixe de la liste search, dans l’ordre, avant d’essayer le nom tel quel. api.example.com ne contient que 2 points, largement sous le seuil de 5 : la résolution tente donc successivement api.example.com.default.svc.cluster.local, api.example.com.svc.cluster.local, api.example.com.cluster.local, chacune une requête DNS distincte qui échoue, avant d’essayer enfin api.example.com tel quel.

Le coût réel : jusqu’à 4 requêtes DNS ratées avant la bonne

Pour un nom de service interne (backend.default.svc.cluster.local, 5 points), ce mécanisme fonctionne comme prévu et résout dès la première tentative. Pour tout nom externe (une API tierce, un service SaaS), chaque appel paie le prix de 3 à 4 requêtes DNS qui échouent systématiquement avant que la bonne ne réussisse, une latence ajoutée à chaque résolution, invisible tant qu’elle reste de l’ordre de quelques millisecondes, mais qui s’accumule sur un service à fort volume d’appels externes.

# Résolution d'un domaine externe avec ndots:5 :
# 4 requêtes DNS inutiles avant la bonne
api.example.com.default.svc.cluster.local  → NXDOMAIN
api.example.com.svc.cluster.local          → NXDOMAIN
api.example.com.cluster.local              → NXDOMAIN
api.example.com                            → succès

CoreDNS absorbe le trafic, mais ne l’élimine pas

Chacune de ces requêtes ratées atteint bien CoreDNS, qui doit y répondre (par un NXDOMAIN) avant que le résolveur du pod ne passe à la tentative suivante. Sur un cluster avec un volume élevé d’appels sortants vers des domaines externes, ce trafic DNS multiplié par 4 ou 5 devient une charge CoreDNS mesurable, parfois suffisante pour expliquer une latence DNS intermittente qu’aucun autre composant du cluster n’explique.

Deux corrections, deux compromis différents

Ajouter un point final au nom résolu (api.example.com.) force une résolution absolue, ignorant entièrement la liste search : la requête va directement au domaine externe, sans jamais tenter les suffixes internes. Cette correction doit se faire dans le code applicatif, à chaque appel, une contrainte qui ne convient pas toujours.

# Alternative au niveau du pod : ajuster ndots directement,
# sans modifier une seule ligne de code applicatif
apiVersion: v1
kind: Pod
spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"

Réduire ndots au niveau du pod (via dnsConfig) change le seuil global : un nom externe à 2 points ou moins saute directement à la résolution absolue, sans passer par la liste search. La contrepartie : un nom de service interne raccourci (sans son suffixe complet .svc.cluster.local) risque de ne plus être résolu correctement si son nombre de points tombe sous le nouveau seuil, un compromis à valider selon les conventions de nommage réellement utilisées dans le cluster.

À retenir

Le ndots:5 par défaut de Kubernetes fait essayer jusqu’à 4 domaines de recherche internes avant toute résolution DNS externe, un coût invisible en fonctionnement normal mais qui s’accumule en latence et en charge CoreDNS sur un service à fort volume d’appels sortants. Forcer une résolution absolue (point final sur le nom) ou ajuster ndots via dnsConfig corrigent le problème par deux voies différentes, chacune avec son propre compromis (changement de code contre risque sur les noms internes raccourcis). Un des détails DNS qui comptent dès qu’une migration Kubernetes fait dialoguer une application avec des services externes à fort volume.