Configurer NodeLocal DNSCache

Ce document explique comment utiliser NodeLocal DNSCache pour réduire la latence de la résolution DNS et améliorer les performances de votre application dans votre cluster Google Kubernetes Engine (GKE).

NodeLocal DNSCache est un module complémentaire GKE qui améliore les performances du DNS en exécutant un cache DNS directement sur chaque nœud de cluster en tant que DaemonSet. Lorsque vos pods effectuent une requête DNS, celle-ci est en premier lieu envoyée au cache local exécuté sur le même nœud. Le traitement local des requêtes réduit considérablement les temps de résolution DNS moyens et diminue la charge sur le fournisseur DNS central de votre cluster, tel que kube-dns ou Cloud DNS pour GKE. Pour obtenir une explication détaillée de l'architecture DNS et des avantages de NodeLocal DNSCache, consultez À propos de la découverte de services.

Dans les clusters GKE Autopilot, NodeLocal DNSCache est activé par défaut et vous ne pouvez pas le désactiver. Dans les clusters GKE Standard exécutant la version 1.33.1 ou ultérieure, NodeLocal DNSCache est activé par défaut, mais vous pouvez le désactiver.

Ce document s'adresse aux utilisateurs de GKE, y compris les développeurs, les administrateurs et les architectes. Pour en savoir plus sur les rôles courants et les exemples de tâches dans Google Cloud, consultez Rôles utilisateur et tâches courantes de l'utilisateur dans GKE Enterprise.

Dans ce document, nous partons du principe que vous connaissez les éléments suivants :

Architecture

NodeLocal DNSCache est un module complémentaire GKE que vous pouvez exécuter en plus de kube-dns.

GKE implémente NodeLocal DNSCache en tant que DaemonSet qui exécute un cache DNS sur chaque nœud de votre cluster.

Lorsqu'un pod effectue une requête DNS, celle-ci est envoyée au cache DNS exécuté sur le même nœud que le pod. Si le cache ne peut pas résoudre la requête DNS, il la transfère vers l'un des emplacements suivants, en fonction de la destination de la requête :

  • kube-dns : toutes les requêtes pour le domaine DNS du cluster (cluster.local) sont transférées à kube-dns. Les pods node-local-dns utilisent le service kube-dns-upstream pour accéder aux pods kube-dns.
  • Domaines de simulation personnalisés ou serveurs de noms en amont : les requêtes sont transférées directement à partir des pods NodeLocal DNSCache.
  • Cloud DNS : toutes les autres requêtes sont transférées au serveur de métadonnées local qui s'exécute sur le même nœud que celui à l'origine de la requête. Le serveur de métadonnées local accède à Cloud DNS.

Chemin d'une requête DNS, comme décrit dans le paragraphe précédent.

Lorsque vous activez NodeLocal DNSCache sur un cluster existant, GKE recrée tous les nœuds de cluster exécutant la version 1.15 ou ultérieure de GKE en fonction du processus de mise à niveau des nœuds.

Après avoir recréé les nœuds, GKE ajoute automatiquement l'étiquette addon.gke.io/node-local-dns-ds-ready=true aux nœuds. Vous ne devez pas ajouter cette étiquette manuellement aux nœuds du cluster.

Avantages de NodeLocal DNSCache

NodeLocal DNSCache offre les avantages suivants :

  • Durée moyenne de résolution DNS réduite
  • Les connexions entre les pods et leur cache local ne créent pas d'entrées de table conntrack. Ce comportement évite les connexions supprimées et rejetées en raison de l'épuisement de la table conntrack et des conditions de concurrence.
  • Vous pouvez utiliser NodeLocal DNSCache avec Cloud DNS pour GKE.
  • Les requêtes DNS pour les URL externes (URL qui ne font pas référence aux ressources du cluster) sont transmises directement au serveur de métadonnées local et contournent kube-dns.
  • Les caches DNS locaux récupèrent automatiquement les domaines de simulation et les serveurs de noms en amont spécifiés dans la section Ajouter des résolveurs personnalisés aux domaines de simulation.

Conditions requises et limites

  • NodeLocal DNSCache consomme des ressources de calcul sur chaque nœud de votre cluster.
  • NodeLocal DNSCache n'est pas compatible avec les pools de nœuds Windows Server.
  • NodeLocal DNSCache nécessite la version 1.15 ou ultérieure de GKE.
  • NodeLocal DNSCache accède aux pods kube-dns à l'aide de TCP.
  • NodeLocal DNSCache accède à upstreamServers et stubDomains à l'aide des protocoles TCP et UDP sur GKE version 1.18 ou ultérieure. Le serveur DNS doit être accessible via TCP et UDP.
  • Les enregistrements DNS sont mis en cache dans les périodes suivantes :
    • Valeur TTL (Time To Live) de l'enregistrement, ou 30 secondes si la valeur TTL est supérieure à 30 secondes.
    • 5 secondes si la réponse DNS est NXDOMAIN.
  • Les pods NodeLocal DNSCache écoutent les ports 53, 9253, 9353 et 8080 sur les nœuds. Si vous exécutez un autre pod hostNetwork ou configurez un hostPorts avec ces ports, NodeLocal DNSCache échoue et des erreurs DNS se produisent. Les pods NodeLocal DNSCache n'utilisent pas le mode hostNetwork lorsqu'ils utilisent GKE Dataplane V2 et n'utilisent pas Cloud DNS pour GKE.
  • Le cache DNS local s'exécute uniquement sur les pools de nœuds exécutant les versions 1.15 et ultérieures de GKE. Si vous activez NodeLocal DNSCache dans un cluster avec des nœuds exécutant des versions antérieures, les pods sur ces nœuds utilisent kube-dns.

Avant de commencer

Avant de commencer, effectuez les tâches suivantes :

  • Activez l'API Google Kubernetes Engine.
  • Activer l'API Google Kubernetes Engine
  • Si vous souhaitez utiliser Google Cloud CLI pour cette tâche, installez puis initialisez la gcloud CLI. Si vous avez déjà installé la gcloud CLI, obtenez la dernière version en exécutant la commande gcloud components update. Il est possible que les versions antérieures de la gcloud CLI ne permettent pas d'exécuter les commandes de ce document.
  • Assurez-vous de disposer d'un cluster Autopilot ou Standard existant. Si vous en avez besoin, créez un cluster Autopilot. Pour les clusters Autopilot, NodeLocal DNSCache est activé par défaut et ne peut pas être remplacé.

Activer NodeLocal DNSCache

Pour les clusters standards, vous pouvez activer NodeLocal DNSCache à l'aide de Google Cloud CLI ou de la console Google Cloud .

gcloud

Pour activer NodeLocal DNSCache dans un cluster existant, utilisez l'option --update-addons avec l'argument NodeLocalDNS=ENABLED :

gcloud container clusters update CLUSTER_NAME \
    --location=COMPUTE_LOCATION \
    --update-addons=NodeLocalDNS=ENABLED

Remplacez les éléments suivants :

Console

Pour activer NodeLocal DNSCache sur un nouveau cluster, procédez comme suit :

  1. Accédez à la page Google Kubernetes Engine dans la console Google Cloud .

    Accéder à la page "Clusters Kubernetes"

  2. Cliquez sur le nom du cluster que vous souhaitez modifier.

  3. Sous Mise en réseau, dans le champ Fournisseur DNS, cliquez sur Modifier le fournisseur DNS.

  4. Cochez la case Activer NodeLocal DNSCache.

  5. Cliquez sur Enregistrer les modifications.

Cette modification nécessite de recréer les nœuds, ce qui peut perturber vos charges de travail en cours d'exécution. Pour en savoir plus sur cette modification spécifique, recherchez la ligne correspondante dans le tableau Modifications manuelles qui recréent les nœuds à l'aide d'une stratégie de mise à niveau des nœuds et qui respectent les règles de maintenance. Pour en savoir plus sur les mises à jour des nœuds, consultez Planifier les interruptions liées aux mises à jour des nœuds.

Vérifier que NodeLocal DNSCache est activé

Vous pouvez vérifier que NodeLocal DNSCache est en cours d'exécution en répertoriant les pods node-local-dns.

kubectl get pods -n kube-system -o wide | grep node-local-dns

Le résultat ressemble à ce qui suit :

node-local-dns-869mt    1/1   Running   0   6m24s   10.128.0.35   gke-test-pool-69efb6b8-5d7m   <none>   <none>
node-local-dns-htx4w    1/1   Running   0   6m24s   10.128.0.36   gke-test-pool-69efb6b8-wssk   <none>   <none>
node-local-dns-v5njk    1/1   Running   0   6m24s   10.128.0.33   gke-test-pool-69efb6b8-bhz3   <none>   <none>

Le résultat affiche un pod node-local-dns pour chaque nœud exécutant la version 1.15 ou une version ultérieure de GKE.

Désactiver NodeLocal DNSCache

Vous pouvez désactiver NodeLocal DNSCache à l'aide de la commande suivante :

gcloud container clusters update CLUSTER_NAME \
    --location=COMPUTE_LOCATION \
    --update-addons=NodeLocalDNS=DISABLED

Remplacez les éléments suivants :

Cette modification nécessite de recréer les nœuds, ce qui peut perturber vos charges de travail en cours d'exécution. Pour en savoir plus sur cette modification spécifique, recherchez la ligne correspondante dans le tableau Modifications manuelles qui recréent les nœuds à l'aide d'une stratégie de mise à niveau des nœuds et qui respectent les règles de maintenance. Pour en savoir plus sur les mises à jour des nœuds, consultez Planifier les interruptions liées aux mises à jour des nœuds.

Résoudre les problèmes liés à NodeLocal DNSCache

Pour obtenir des informations générales sur l'analyse des problèmes de DNS de Kubernetes, consultez la section Déboguer la résolution DNS.

NodeLocal DNSCache n'est pas activé immédiatement

Lorsque vous activez NodeLocal DNSCache sur un cluster existant, GKE peut ne pas mettre à jour immédiatement les nœuds si le cluster dispose d'une exclusion ou d'un intervalle de maintenance configuré. Pour en savoir plus, consultez la section Mise en garde à propos des intervalles de recréation et de maintenance des nœuds.

Pour éviter d'attendre, vous pouvez appliquer manuellement les modifications aux nœuds en appelant la commande gcloud container clusters upgrade et en transmettant l'argument --cluster-version avec la même version de GKE que celle exécutée par le pool de nœuds. Vous devez utiliser Google Cloud CLI pour contourner ce problème.

NodeLocal DNSCache avec Cloud DNS

Si vous utilisez NodeLocal DNSCache avec Cloud DNS, le cluster utilise l'adresse IP du serveur de noms 169.254.20.10, comme indiqué dans le schéma suivant :

Architecture NodeLocal DNSCache avec Cloud DNS.

Par conséquent, l'adresse IP du service kube-dns peut être différente de l'adresse IP du serveur de noms utilisé par vos pods. Cette différence d'adresses IP est normale, car l'adresse IP du serveur de noms 169.254.20.10 est requise pour que Cloud DNS fonctionne correctement.

Pour vérifier les adresses IP, exécutez les commandes suivantes :

  1. Affichez l'adresse IP du service kube-dns :

    kubectl get svc -n kube-system kube-dns -o jsonpath="{.spec.clusterIP}"
    

    Le résultat est l'adresse IP de kube-dns, par exemple 10.0.0.10.

  2. Ouvrez une session shell dans le pod :

    kubectl exec -it POD_NAME -- /bin/bash
    
  3. Dans la session shell du pod, lisez le contenu du fichier /etc/resolv.conf :

    cat /etc/resolv.conf
    

    Le résultat est 169.254.20.10.

Règle de réseau avec NodeLocal DNSCache

Si vous utilisez une règle de réseau avec NodeLocal DNSCache, et que vous n'utilisez pas Cloud DNS ou GKE Dataplane V2, vous devez configurer des règles pour autoriser vos charges de travail et les pods node-local-dns à envoyer des requêtes DNS.

Utilisez une règle ipBlock dans votre fichier manifeste pour autoriser la communication entre vos pods et kube-dns.

Le fichier manifeste suivant décrit une règle de réseau qui utilise une règle ipBlock :

spec:
  egress:
  - ports:
    - port: 53
      protocol: TCP
    - port: 53
      protocol: UDP
    to:
    - ipBlock:
        cidr: KUBE_DNS_SVC_CLUSTER_IP/32
  podSelector: {}
  policyTypes:
    - Egress

Remplacez KUBE_DNS_SVC_CLUSTER_IP par l'adresse IP du service kube-dns. Vous pouvez obtenir l'adresse IP du service kube-dns à l'aide de la commande suivante :

kubectl get svc -n kube-system kube-dns -o jsonpath="{.spec.clusterIP}"

Problèmes connus

Cette section liste les problèmes connus liés à NodeLocal DNSCache.

Délai avant expiration DNS dans ClusterFirstWithHostNet dnsPolicy lors de l'utilisation de NodeLocal DNSCache et GKE Dataplane V2

Sur les clusters qui utilisent GKE Dataplane V2 et NodeLocal DNSCache, les pods dont le champ hostNetwork est défini sur true et le champ dnsPolicy est défini sur ClusterFirstWithHostNet ne peuvent pas atteindre les backends DNS du cluster. Les journaux DNS peuvent contenir des entrées semblables à ce qui suit :

dnslookup: write to 'a.b.c.d': Operation not permitted

;; connection timed out; no servers could be reached

Le résultat indique que les requêtes DNS ne peuvent pas atteindre les serveurs de backend.

Pour contourner ce problème, définissez les champs dnsPolicy et dnsConfig pour les pods hostNetwork :

spec:
 dnsPolicy: "None"
 dnsConfig:
   nameservers:
     - KUBE_DNS_UPSTREAM
   searches:
     - NAMESPACE.svc.cluster.local
     - svc.cluster.local
     - cluster.local
     - c.PROJECT_ID.internal
     - google.internal
   options:
     - name: ndots
       value: "5"

Remplacez les éléments suivants :

  • NAMESPACE : espace de noms du pod hostNetwork.
  • PROJECT_ID : ID de votre projet Google Cloud .
  • KUBE_DNS_UPSTREAM : ClusterIP du service kube-dns en amont. Vous pouvez obtenir cette valeur à l'aide de la commande suivante :

    kubectl get svc -n kube-system kube-dns-upstream -o jsonpath="{.spec.clusterIP}"
    

Les requêtes DNS du pod peuvent désormais atteindre kube-dns et contourner NodeLocal DNSCache.

Erreurs de délai avant expiration de NodeLocal DNSCache

Sur les clusters sur lesquels NodeLocal DNSCache est activé, les journaux peuvent contenir des entrées semblables à ce qui suit :

[ERROR] plugin/errors: 2 <hostname> A: read tcp <node IP: port>-><kubedns IP>:53: i/o timeout

Le résultat inclut l'adresse IP du service IP du cluster kube-dns-upstream. Dans cet exemple, la réponse à une requête DNS n'a pas été reçue par kube-dns après deux secondes. Ce problème peut être dû à l'une des raisons suivantes :

  • un problème de connectivité réseau sous-jacent
  • Augmentation considérable des requêtes DNS de la charge de travail ou du scaling à la hausse du pool de nœuds

Par conséquent, les pods kube-dns existants ne peuvent pas traiter toutes les requêtes à temps. La solution consiste à augmenter le nombre d'instances répliquées kube-dns en effectuant un scaling vertical de kube-dns.

Augmenter la capacité kube-dns

Vous pouvez utiliser une valeur inférieure pour le champ nodesPerReplica afin de vous assurer de la création d'un plus grand nombre de pods kube-dns au fur et à mesure du scaling à la hausse des nœuds du cluster. Nous vous recommandons vivement de définir une valeur max explicite pour vous assurer que la machine virtuelle (VM) du plan de contrôle GKE n'est pas submergée par le grand nombre de pods kube-dns qui surveillent l'API Kubernetes.

Vous pouvez définir le champ max sur le nombre de nœuds du cluster. Si le cluster comporte plus de 500 nœuds, définissez le champ max sur 500.

Pour les clusters standards, vous pouvez modifier le nombre d'instances dupliquées kube-dns en modifiant le fichier ConfigMap kube-dns-autoscaler. Cette configuration n'est pas compatible avec les clusters Autopilot.

kubectl edit configmap kube-dns-autoscaler --namespace=kube-system

Le résultat ressemble à ce qui suit :

linear: '{"coresPerReplica":256, "nodesPerReplica":16,"preventSinglePointFailure":true}'

Le nombre de répliques kube-dns est calculé à l'aide de la formule suivante :

replicas = max(ceil(cores * 1/coresPerReplica), ceil(nodes * 1/nodesPerReplica))

Si vous définissez les champs min et max dans le ConfigMap, les répliques sont limitées par ces valeurs. Pour effectuer un scaling à la hausse, remplacez la valeur du champ nodesPerReplica par une valeur plus petite et incluez une valeur pour le champ max :

linear: '{"coresPerReplica":256, "nodesPerReplica":8,"max": 15,"preventSinglePointFailure":true}'

La configuration crée un pod kube-dns pour chacun des huit nœuds du cluster. Un cluster de 24 nœuds comporte trois instances dupliquées, tandis qu'un cluster de 40 nœuds en comporte cinq. Si le cluster dépasse 120 nœuds, le nombre d'instances dupliquées kube-dns ne dépasse pas 15, qui correspond à la valeur max.

Pour garantir un niveau de disponibilité du DNS de base dans votre cluster, définissez un nombre minimal d'instances dupliquées pour kube-dns.

Le résultat du ConfigMap kube-dns-autoscaler qui comporte un champ min défini ressemblerait à ce qui suit :

linear: '{"coresPerReplica":256, "nodesPerReplica":8,"max": 15,"min": 5,"preventSinglePointFailure":true}'

Étapes suivantes