Aller au contenu
English
Conteneurs & Orchestration medium

CoreDNS, DNS interne de Kubernetes

35 min de lecture

CoreDNS est le DNS par défaut de Kubernetes depuis la version 1.13. Il permet aux Pods de résoudre les noms de Services (my-service.my-namespace.svc.cluster.local) en adresses IP ClusterIP. Sans DNS fonctionnel, la communication inter-Pods par nom de Service ne fonctionne pas.

Ce guide couvre CoreDNS pour la certification CKA, où le dépannage DNS représente une compétence clé du domaine Troubleshooting (30%).

  • Comment fonctionne la résolution DNS dans Kubernetes
  • La syntaxe des noms de Services (service.namespace.svc.cluster.local)
  • Comment configurer CoreDNS via le Corefile
  • Les techniques de dépannage DNS pour la CKA
  • Les erreurs courantes et leurs solutions

Kubernetes attribue des adresses IP éphémères : un Pod recréé change d'IP, un Service en change à chaque suppression. Le DNS interne est la couche d'indirection qui rend cette instabilité invisible aux applications, en associant un nom stable à l'adresse du moment. CoreDNS est le composant qui produit et sert ces enregistrements, à partir des objets Service et EndpointSlice de l'API Kubernetes.

Trois éléments distincts interviennent, et les confondre complique le dépannage : le fichier /etc/resolv.conf injecté dans chaque Pod par le kubelet, le Service kube-dns qui porte une ClusterIP fixe, et les Pods CoreDNS qui répondent réellement aux requêtes. Le nom kube-dns du Service est un héritage historique : il pointe bien vers CoreDNS.

Trois Pods dont le fichier /etc/resolv.conf pointe vers l'IP du service DNS envoient leurs requêtes au Service kube-dns, porteur de la ClusterIP 10.96.0.10, qui les transmet aux Pods CoreDNS gérés par un Deployment

Suivre une requête de bout en bout donne la carte des points de panne : chacune des cinq étapes peut échouer indépendamment, et tout le dépannage consiste à déterminer laquelle. Retenez surtout que CoreDNS ne stocke rien : il lit l'état de l'API Kubernetes et le traduit en réponses DNS.

Quand un Pod veut contacter my-service.default.svc.cluster.local :

  1. Le Pod lit /etc/resolv.conf, qui pointe vers le Service kube-dns
  2. La requête part vers ce Service, souvent 10.96.0.10 sur un cluster kubeadm
  3. CoreDNS reçoit la requête par le plugin kubernetes, qui publie les enregistrements à partir des objets du cluster (Services, EndpointSlices)
  4. CoreDNS renvoie la ClusterIP du Service
  5. Le Pod contacte enfin le Service

Kubernetes suit une convention de nommage stricte, identique sur tous les clusters : connaître ce format permet de deviner l'adresse d'un Service sans consulter le cluster. Le domaine du cluster vaut cluster.local par défaut, mais il est configurable à l'installation. Trois catégories d'objets ont leurs propres règles : les Services classiques, les Pods et les Services headless.

Les formes courtes fonctionnent grâce à la ligne search du /etc/resolv.conf du Pod, qui complète automatiquement les noms incomplets. Un nom court ne résout donc que depuis un Pod, jamais depuis un nœud ni une machine extérieure.

Type de nomFormatExemple
FQDN complet<service>.<namespace>.svc.<cluster-domain>nginx.default.svc.cluster.local
Dans le même namespace<service>nginx
Cross-namespace<service>.<namespace>nginx.production

Ces trois écritures ne sont pas trois adresses : depuis un Pod du namespace default, nginx, nginx.default et nginx.default.svc.cluster.local renvoient la même ClusterIP, vérifié sur le cluster de cette formation. La forme courte est confortable au clavier, la forme complète est la seule qui fonctionne partout : elle ne dépend d'aucune ligne search, donc ni du namespace d'où vous l'appelez, ni de l'outil qui la résout. En diagnostic, écrivez toujours le nom complet.

Kubernetes peut publier des enregistrements DNS pour les Pods, mais la résolution exacte dépend du DNS du cluster et de sa configuration. Côté CoreDNS, tout tient au plugin kubernetes et à ses options, notamment pods insecure.

Format si activé :

<pod-ip-avec-tirets>.<namespace>.pod.cluster.local

Exemple : 10-244-0-5.default.pod.cluster.local

Un Service headless (clusterIP: None) ne reçoit aucune adresse virtuelle et n'équilibre pas la charge. CoreDNS renvoie alors directement les IPs des Pods, et attribue en plus un nom stable à chacun. C'est ce qui permet à un client de s'adresser à une instance précise, besoin typique des bases de données répliquées :

<pod-name>.<service>.<namespace>.svc.cluster.local

Utile pour les StatefulSets : mysql-0.mysql.default.svc.cluster.local

Avant de soupçonner une configuration, vérifiez que la chaîne existe : le Deployment, ses Pods, et le Service qui les expose. Trois commandes de quelques secondes éliminent les causes les plus grossières, comme des Pods CoreDNS bloqués faute de CNI fonctionnel.

Le Deployment coredns déclare deux répliques par défaut sur un cluster kubeadm. La colonne AVAILABLE doit égaler READY : un écart signale des Pods qui ne passent pas leur sonde de disponibilité.

Fenêtre de terminal
kubectl get deployment coredns -n kube-system
NAME READY UP-TO-DATE AVAILABLE AGE
coredns 2/2 2 2 10d

Le label de sélection reste k8s-app=kube-dns alors que les Pods s'appellent coredns : c'est ce label, et non le nom, qu'attendent les commandes de journaux et de diagnostic. Un compteur RESTARTS qui grimpe trahit souvent une boucle de forwarding détectée par le plugin loop.

Fenêtre de terminal
kubectl get pods -n kube-system -l k8s-app=kube-dns
NAME READY STATUS RESTARTS AGE
coredns-5644d7b6d9-abcde 1/1 Running 0 10d
coredns-5644d7b6d9-fghij 1/1 Running 0 10d

C'est l'adresse de ce Service que le kubelet écrit dans chaque Pod : elle doit correspondre exactement au nameserver de leur /etc/resolv.conf. Le port 9153 sert aux métriques Prometheus, et les deux entrées sur le port 53 exposent le DNS en UDP et en TCP.

Fenêtre de terminal
kubectl get svc kube-dns -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 10d

CoreDNS est configuré via une ConfigMap nommée coredns dans kube-system. Elle contient un fichier unique, le Corefile, qui décrit une chaîne de plugins exécutés dans un ordre fixe, celui du code de CoreDNS et non celui du fichier. Modifier cette ConfigMap suffit à changer le comportement du DNS de tout le cluster, sans redéployer quoi que ce soit.

Lisez toujours la configuration réelle plutôt que de supposer celle de votre distribution : k3s, RKE2 et les fournisseurs cloud en livrent chacun une variante.

Fenêtre de terminal
kubectl get configmap coredns -n kube-system -o yaml

Le Corefile varie selon la distribution, la version et les personnalisations. Celui-ci est relevé le 2026-09-13 sur un cluster Kubernetes 1.37, servi par CoreDNS 1.14.6 :

.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30 {
disable success cluster.local
disable denial cluster.local
}
loop
reload
loadbalance
}

Les deux lignes disable méritent qu'on s'y arrête, parce qu'elles changent ce que vous observerez en dépannage : les noms internes du cluster ne sont pas mis en cache. Un changement d'adresse de Service se voit donc immédiatement, et si un Pod résout une adresse périmée, la cause est ailleurs, dans le cache du système ou de l'application. Le cache de trente secondes ne s'applique qu'aux noms externes, ceux que forward est allé chercher.

L'option pods insecure est ce qui rend la résolution des Pods effective, et elle est présente dans le Corefile par défaut de kubeadm. Concrètement, un Pod d'adresse 10.244.1.183 dans le namespace demo répond au nom 10-244-1-183.demo.pod.cluster.local, mesuré sur le cluster de cette formation. Le mot insecure n'est pas un avertissement de sécurité réseau, il décrit ce que CoreDNS s'autorise : il n'interroge pas Kubernetes pour répondre, il recopie l'adresse contenue dans le nom. Interrogé sur 10-244-9-99.default.pod.cluster.local alors qu'aucun Pod ne porte cette adresse, il répond 10.244.9.99 sans broncher. Une réponse positive ne prouve donc pas que le Pod existe, ce qui en fait un mauvais test de présence. Le namespace, lui, est bien vérifié : nommez-en un qui n'existe pas et vous obtenez un NXDOMAIN.

Deux plugins font l'essentiel : kubernetes répond pour les noms internes, forward transmet tout le reste aux résolveurs du nœud. Les autres apportent observabilité, cache et robustesse. Le plugin reload mérite une mention : c'est lui qui fait qu'une modification du Corefile est prise en compte sans redémarrer les Pods.

PluginRôle
errorsLog les erreurs
healthExpose /health sur le port 8080
readyExpose /ready sur le port 8181
kubernetesRésout les noms du cluster Kubernetes
prometheusExpose les métriques sur le port 9153
forwardForwarde les requêtes externes vers les DNS upstream
cacheCache les réponses externes, 30 secondes ; pas les noms en cluster.local
loopDétecte les boucles de forwarding
reloadRecharge la config quand la ConfigMap change
loadbalanceRépartit les réponses DNS (round-robin)

Deux besoins reviennent constamment en entreprise : joindre un domaine interne servi par un DNS maison, et résoudre un nom d'hôte qui n'existe dans aucun DNS. Le Corefile répond au premier avec un bloc de serveur dédié, au second avec le plugin hosts. Dans les deux cas, une erreur de syntaxe casse la résolution de tout le cluster : gardez une copie de la ConfigMap avant de la modifier.

Pour résoudre *.corp.example.com via un DNS interne d'entreprise :

Fenêtre de terminal
kubectl edit configmap coredns -n kube-system

Ajoutez un bloc avant le bloc principal :

corp.example.com:53 {
errors
cache 30
forward . 10.0.0.53
}
.:53 {
# ... config par défaut
}

Utilisez le plugin hosts pour des résolutions statiques :

.:53 {
errors
health
hosts {
192.168.1.100 legacy.internal
192.168.1.101 oldserver.internal
fallthrough
}
kubernetes cluster.local in-addr.arpa ip6.arpa {
# ...
}
# ...
}

Un problème DNS se manifeste rarement comme tel : l'application signale un hôte introuvable, une connexion qui expire ou une lenteur inexpliquée. La méthode consiste à tester la résolution depuis un Pod, à comparer avec le comportement attendu, puis à remonter la chaîne jusqu'à CoreDNS. Cette compétence est directement évaluée à l'examen CKA, dans le domaine Troubleshooting.

Le test doit partir d'un Pod, jamais d'un nœud : seul un Pod dispose du /etc/resolv.conf pointant vers CoreDNS. Le nom kubernetes.default est la cible idéale, ce Service existe sur tous les clusters.

  1. Lancez un Pod de test

    Fenêtre de terminal
    kubectl run dnsutils \
    --image=registry.k8s.io/e2e-test-images/agnhost:2.39@sha256:7e8bdd271312fd25fc5ff5a8f04727be84044eb3d7d8d03611972a6752e2e11e \
    --command -- sleep infinity
  2. Testez la résolution

    Fenêtre de terminal
    kubectl exec -it dnsutils -- nslookup kubernetes.default

    Résultat attendu :

    Server: 10.96.0.10
    Address: 10.96.0.10#53
    Name: kubernetes.default.svc.cluster.local
    Address: 10.96.0.1
  3. Testez un Service spécifique

    Fenêtre de terminal
    kubectl exec -it dnsutils -- nslookup my-service.my-namespace
  4. Nettoyez

    Fenêtre de terminal
    kubectl delete pod dnsutils

Ce fichier est écrit par le kubelet au démarrage du Pod et n'est plus modifié ensuite : un Pod démarré avant un changement de configuration DNS garde donc l'ancienne valeur jusqu'à sa recréation. Vérifiez surtout que le nameserver correspond bien à la ClusterIP du Service kube-dns.

Fenêtre de terminal
kubectl exec <pod-name> -- cat /etc/resolv.conf

Contenu attendu :

nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

Trois situations couvrent la majorité des incidents DNS, et une seule question les distingue : qu'est-ce qui résout, et qu'est-ce qui échoue ? Plus rien du tout oriente vers CoreDNS ou le réseau, l'interne seul fonctionne pointe vers le forwarding, tout fonctionne mais lentement met en cause ndots.

Aucune résolution, interne comme externe, signifie que le Pod n'atteint pas CoreDNS ou que CoreDNS ne répond pas. La cause est presque toujours en dessous du DNS : plugin réseau défaillant, ou politique réseau qui bloque le port 53.

Symptôme :

Fenêtre de terminal
kubectl exec mypod -- nslookup google.com
# timeout ou "server can't find"

Diagnostic :

Fenêtre de terminal
# 1. CoreDNS fonctionne ?
kubectl get pods -n kube-system -l k8s-app=kube-dns
# 2. Service kube-dns existe ?
kubectl get svc kube-dns -n kube-system
# 3. Logs CoreDNS
kubectl logs -n kube-system -l k8s-app=kube-dns

Solutions possibles :

  • Redémarrez CoreDNS : kubectl rollout restart deployment coredns -n kube-system
  • Vérifiez les NetworkPolicies bloquant le port 53
  • Vérifiez que le CNI est installé et fonctionnel

CoreDNS fonctionne, la panne est donc en aval : le plugin forward ne parvient pas à joindre les résolveurs qu'il utilise. Par défaut il reprend le fichier /etc/resolv.conf du nœud, ce qui déplace le diagnostic hors du cluster.

Symptôme :

Fenêtre de terminal
kubectl exec mypod -- nslookup kubernetes.default
# OK
kubectl exec mypod -- nslookup google.com
# timeout

Diagnostic :

Fenêtre de terminal
# Vérifiez le forward dans le Corefile
kubectl get configmap coredns -n kube-system -o yaml | grep forward

Solution : Vérifiez que /etc/resolv.conf du nœud contient des DNS valides, ou configurez un forward explicite :

forward . 8.8.8.8 8.8.4.4

Résolution externe lente : quatre requêtes au lieu d'une

Section intitulée « Résolution externe lente : quatre requêtes au lieu d'une »

Avec ndots:5, tout nom comportant moins de cinq points est d'abord essayé avec chacun des suffixes de la ligne search, et le nom demandé n'est interrogé tel quel qu'en dernier. Le coût n'est donc pas un blocage, c'est une multiplication des allers-retours, et il se mesure.

Relevé sur le cluster de cette formation, en activant le plugin log de CoreDNS et en résolvant un seul nom externe :

Nom résoluRequêtes reçues par CoreDNS
exemple.org4 : trois suffixes de la liste search, puis le nom lui-même
exemple.org.1

Le point final déclare le nom absolu et court-circuite toute la liste. Le compte exact dépend de cette liste : certains clusters y ajoutent les suffixes de recherche du nœud lui-même, ce qui allonge encore la série. Chacune de ces requêtes supplémentaires est une réponse NXDOMAIN parfaitement inutile, payée à chaque appel de votre application.

Solution : Pour une application qui fait beaucoup de requêtes externes, ajoutez un point final :

Fenêtre de terminal
# Lent (essaie google.com.default.svc.cluster.local, etc.)
nslookup google.com
# Rapide (requête directe)
nslookup google.com.

Ou configurez le Pod avec un dnsConfig personnalisé :

apiVersion: v1
kind: Pod
metadata:
name: fast-dns
spec:
dnsConfig:
options:
- name: ndots
value: "2"
containers:
- name: app
image: nginx:1.28@sha256:146adea4768b83c607d0bdfa4188464e3da6e0a3ad4475db1d1d8f64f27c29cc

Tout ce qui précède concerne le serveur ; deux champs du manifeste de Pod agissent du côté client. dnsPolicy choisit quel résolveur le Pod interroge, dnsConfig permet d'ajuster ou de remplacer entièrement le contenu de son fichier /etc/resolv.conf. Ces réglages sont fixés à la création du Pod et exigent une recréation pour changer.

Le champ dnsPolicy contrôle la configuration DNS du Pod :

ValeurComportement
ClusterFirst (défaut)Utilise CoreDNS pour tout
DefaultUtilise le resolv.conf du nœud
ClusterFirstWithHostNetPour les Pods avec hostNetwork: true
NoneAucune config, à définir via dnsConfig

Le cas hostNetwork se reconnaît à un symptôme précis, vérifié sur le cluster de cette formation : avec ClusterFirst, le Pod hérite du /etc/resolv.conf du nœud, son nameserver n'est donc pas la ClusterIP de kube-dns, et un nom de Service rend un SERVFAIL, pas un NXDOMAIN. La nuance oriente le diagnostic : NXDOMAIN signifie qu'un serveur compétent a répondu « ce nom n'existe pas », SERVFAIL qu'on interroge un serveur qui ne connaît tout simplement pas la zone. Passer à ClusterFirstWithHostNet restaure la liste search du cluster et la résolution fonctionne.

Avec dnsPolicy: "None", Kubernetes n'injecte plus aucune configuration : le bloc dnsConfig devient obligatoire et doit tout déclarer, résolveurs compris. Ce Pod perd l'accès aux noms de Services, sauf à ajouter la ClusterIP de kube-dns dans nameservers.

apiVersion: v1
kind: Pod
metadata:
name: custom-dns
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 8.8.8.8
- 8.8.4.4
searches:
- ns1.svc.cluster.local
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
containers:
- name: app
image: nginx:1.28@sha256:146adea4768b83c607d0bdfa4188464e3da6e0a3ad4475db1d1d8f64f27c29cc

CoreDNS expose des métriques Prometheus sur le port 9153. Les surveiller donne un signal d'alerte avant que les utilisateurs ne remontent quoi que ce soit : une hausse des SERVFAIL ou un effondrement du taux de cache précèdent en général l'incident visible. Le port-forward ci-dessous les rend consultables sans collecteur.

Fenêtre de terminal
kubectl port-forward -n kube-system svc/kube-dns 9153:9153
curl http://localhost:9153/metrics

Quatre métriques suffisent à qualifier la santé du service : le volume de requêtes, la répartition des codes de réponse, la latence et l'efficacité du cache.

MétriqueDescription
coredns_dns_requests_totalNombre total de requêtes
coredns_dns_responses_totalRéponses par code (NOERROR, NXDOMAIN, SERVFAIL)
coredns_dns_request_duration_secondsLatence des requêtes
coredns_cache_hits_totalHits du cache

Deux répliques suffisent à un cluster modeste, mais la charge DNS croît avec le nombre de Pods et la fréquence de leurs requêtes, pas avec le nombre de nœuds. Une latence qui monte pendant que les Pods CoreDNS saturent leur CPU est le signal qu'il faut en ajouter. Descendre sous deux répliques est à proscrire : une seule instance coupe le DNS à chaque redémarrage.

Pour les grands clusters, augmentez le nombre de replicas :

Fenêtre de terminal
kubectl scale deployment coredns -n kube-system --replicas=4

Ou utilisez l'Autoscaler DNS :

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: coredns
namespace: kube-system
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: coredns
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70

Le temps est la contrainte principale de l'examen : ces cinq commandes couvrent le parcours complet de diagnostic sans rien rédiger. La dernière, kubectl run --rm -it, crée un Pod de test et le supprime automatiquement à la sortie, ce qui évite de laisser des ressources derrière soi.

Fenêtre de terminal
# Vérifier l'état de CoreDNS
kubectl get deployment,pods,svc -n kube-system -l k8s-app=kube-dns
# Voir la configuration
kubectl get configmap coredns -n kube-system -o yaml
# Logs CoreDNS
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50
# Test DNS rapide (image officielle de debug)
# --command est indispensable : sans lui, l'argument est traité comme une
# sous-commande d'agnhost, qui n'en a aucune pour interroger le DNS.
kubectl run test-dns --rm -it --restart=Never \
--image=registry.k8s.io/e2e-test-images/agnhost:2.39@sha256:7e8bdd271312fd25fc5ff5a8f04727be84044eb3d7d8d03611972a6752e2e11e \
--command -- nslookup kubernetes.default
# Redémarrer CoreDNS
kubectl rollout restart deployment coredns -n kube-system

Pour les grands clusters ou les applications sensibles à la latence DNS, Kubernetes propose NodeLocal DNSCache. Il place un cache DNS sur chaque nœud via un DaemonSet, réduisant la charge sur CoreDNS et les temps de résolution.

Consultez la documentation NodeLocal DNSCache pour la mise en place.

Ce quiz reprend les questions CoreDNS du programme CKA, où le dépannage DNS relève du domaine Troubleshooting. Un score inférieur à 70 % indique les sections à reprendre avant de passer à la pratique.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
5 min.
80% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  1. CoreDNS est le DNS par défaut de Kubernetes depuis la v1.13
  2. Service kube-dns expose CoreDNS (IP variable selon le Service CIDR, souvent 10.96.0.10 sur kubeadm)
  3. Format DNS : <service>.<namespace>.svc.cluster.local
  4. Corefile dans la ConfigMap coredns du namespace kube-system
  5. Plugin forward gère les requêtes DNS externes
  6. Test rapide avec l'image agnhost ou un Pod dnsutils
  7. ndots:5 peut ralentir les requêtes externes (ajouter un . final)
  8. hostNetwork nécessite ClusterFirstWithHostNet pour résoudre les Services
  • Gateway API : L'exposition qui succède à Ingress, et les noms d'hôtes qu'elle fait résoudre depuis l'extérieur du cluster.
  • Network Policies : Comprendre pourquoi une règle d'isolation mal écrite coupe le trafic vers CoreDNS sur le port 53.
  • Troubleshooting cluster : La méthode de diagnostic complète quand le problème dépasse le seul service DNS.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn