---
title: 'Настроить NodeLocal DNS Cache в кластере Managed Kubernetes'
sidebar_label: 'Настроить NodeLocal DNS Cache'
description: 'Как настроить локальное кэширование DNS'
sidebar_position: 7
toc_max_heading_level: 2
---

import Formbricks from '@theme/MDXComponents/Formbricks'
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
import {TabItemLabel} from '@selectel/docux/components'

# Настроить NodeLocal DNS Cache в кластере Managed Kubernetes

Чтобы снизить нагрузку на DNS в кластере Managed Kubernetes и ускорить получение ответа от внешних и внутренних ресурсов, вы можете использовать NodeLocal DNS Cache.

NodeLocal DNS Cache — это механизм кэширования DNS-запросов, который работает на всех нодах Managed Kubernetes и предоставляет локальный кэш для резолвера DNS. Когда поды пользователя отправляют запросы, они сначала проходят через локальный кэш DNS. Если эти запросы совпадают с ранее сохраненными записями, то удается избежать дополнительных запросов к внешнему DNS-серверу. Это позволяет сократить время обработки DNS-запросов и сэкономить сетевой трафик в кластере.

Подробнее о NodeLocal DNS Cache в [документации Kubernetes](https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/).

Процесс настройки и проверки работы NodeLocal DNS Cache зависит от используемого в кластере сетевого плагина (CNI): Calico или Cilium.
Сетевой плагин в кластере можно посмотреть в [панели управления](https://my.selectel.ru/mks/default/clusters): в верхнем меню нажмите **Продукты** → **Managed Kubernetes** → страница кластера → вкладка **Настройки** → строка **CNI**.

1. [Настройте NodeLocal DNS Cache](#configure-nodelocal-dns-cache).
2. [Проверьте работу NodeLocal DNS Cache](#check-nodelocal-dns-cache-operation).

## 1. Настроить NodeLocal DNS Cache \{#configure-nodelocal-dns-cache}

<Tabs queryString="set-up-nodelocal-dns-cache">
  <TabItem value="cni-calico" default>
    <TabItemLabel>
      CNI Calico
    </TabItemLabel>

    1. Создайте YAML-файл `nodelocaldns.yaml` с манифестом:

       <details>
         <summary>Файл nodelocaldns.yaml</summary>

         ```yaml
         apiVersion: v1
         kind: ServiceAccount
         metadata:
           name: node-local-dns
           namespace: kube-system
           labels:
             kubernetes.io/cluster-service: "true"
             addonmanager.kubernetes.io/mode: Reconcile
         ---
         apiVersion: v1
         kind: Service
         metadata:
           name: kube-dns-upstream
           namespace: kube-system
           labels:
             k8s-app: kube-dns
             kubernetes.io/cluster-service: "true"
             addonmanager.kubernetes.io/mode: Reconcile
             kubernetes.io/name: "KubeDNSUpstream"
         spec:
           ports:
           - name: dns
             port: 53
             protocol: UDP
             targetPort: 53
           - name: dns-tcp
             port: 53
             protocol: TCP
             targetPort: 53
           selector:
             k8s-app: kube-dns
         ---
         apiVersion: v1
         kind: ConfigMap
         metadata:
           name: node-local-dns
           namespace: kube-system
           labels:
             addonmanager.kubernetes.io/mode: Reconcile
         data:
           Corefile: |
             cluster.local:53 {
                 errors
                 cache {
                         success 9984 30
                         denial 9984 5
                 }
                 reload
                 loop
                 bind 169.254.20.25 10.96.0.10
                 forward . __PILLAR__CLUSTER__DNS__ {
                         force_tcp
                 }
                 prometheus :9253
                 health 169.254.20.25:8080
                 }
             in-addr.arpa:53 {
                 errors
                 cache 30
                 reload
                 loop
                 bind 169.254.20.25 10.96.0.10
                 forward . __PILLAR__CLUSTER__DNS__ {
                         force_tcp
                 }
                 prometheus :9253
                 }
             ip6.arpa:53 {
                 errors
                 cache 30
                 reload
                 loop
                 bind 169.254.20.25 10.96.0.10
                 forward . __PILLAR__CLUSTER__DNS__ {
                         force_tcp
                 }
                 prometheus :9253
                 }
             .:53 {
                 errors
                 cache 30
                 reload
                 loop
                 bind 169.254.20.25 10.96.0.10
                 forward . __PILLAR__UPSTREAM__SERVERS__
                 prometheus :9253
                 }
         ---
         apiVersion: apps/v1
         kind: DaemonSet
         metadata:
           name: node-local-dns
           namespace: kube-system
           labels:
             k8s-app: kube-dns
             kubernetes.io/cluster-service: "true"
             addonmanager.kubernetes.io/mode: Reconcile
         spec:
           updateStrategy:
             rollingUpdate:
               maxUnavailable: 10%
           selector:
             matchLabels:
               k8s-app: kube-dns
           template:
             metadata:
               labels:
                 k8s-app: kube-dns
               annotations:
                 prometheus.io/port: "9253"
                 prometheus.io/scrape: "true"
             spec:
               priorityClassName: system-node-critical
               serviceAccountName: node-local-dns
               hostNetwork: true
               dnsPolicy: Default  # Do not use cluster DNS.
               tolerations:
               - key: "CriticalAddonsOnly"
                 operator: "Exists"
               - effect: "NoExecute"
                 operator: "Exists"
               - effect: "NoSchedule"
                 operator: "Exists"
               containers:
               - name: node-cache
                 image: registry.k8s.io/dns/k8s-dns-node-cache:1.22.20
                 resources:
                   requests:
                     cpu: 25m
                     memory: 5Mi
                 args: [ "-localip", "169.254.20.25,10.96.0.10", "-conf", "/etc/Corefile", "-upstreamsvc", "kube-dns-upstream" ]
                 securityContext:
                   capabilities:
                     add:
                     - NET_ADMIN
                 ports:
                 - containerPort: 53
                   name: dns
                   protocol: UDP
                 - containerPort: 53
                   name: dns-tcp
                   protocol: TCP
                 - containerPort: 9253
                   name: metrics
                   protocol: TCP
                 livenessProbe:
                   httpGet:
                     host: 169.254.20.25
                     path: /health
                     port: 8080
                   initialDelaySeconds: 60
                   timeoutSeconds: 5
                 volumeMounts:
                 - mountPath: /run/xtables.lock
                   name: xtables-lock
                   readOnly: false
                 - name: config-volume
                   mountPath: /etc/coredns
                 - name: kube-dns-config
                   mountPath: /etc/kube-dns
               volumes:
               - name: xtables-lock
                 hostPath:
                   path: /run/xtables.lock
                   type: FileOrCreate
               - name: kube-dns-config
                 configMap:
                   name: kube-dns
                   optional: true
               - name: config-volume
                 configMap:
                   name: node-local-dns
                   items:
                     - key: Corefile
                       path: Corefile.base
         ```
       </details>

    2. Примените манифест:

       ```bash
       kubectl apply -f nodelocaldns.yaml
       ```

       В ответе появится список созданных объектов:

       ```bash
       serviceaccount/node-local-dns created
       service/kube-dns-upstream created
       configmap/node-local-dns created
       daemonset.apps/node-local-dns created
       ```
  </TabItem>

  <TabItem value="cni-cilium">
    <TabItemLabel>
      CNI Cilium
    </TabItemLabel>

    1. Создайте YAML-файл `nodelocaldns.yaml` с манифестом:

       <details>
         <summary>Файл nodelocaldns.yaml</summary>

         ```yaml
         apiVersion: v1
         kind: ServiceAccount
         metadata:
           name: node-local-dns
           namespace: kube-system
         ---
         apiVersion: v1
         kind: Service
         metadata:
           name: kube-dns-upstream
           namespace: kube-system
         labels:
           k8s-app: kube-dns
           kubernetes.io/name: "KubeDNSUpstream"
         spec:
           ports:
           - name: dns
             port: 53
             protocol: UDP
             targetPort: 53
           - name: dns-tcp
             port: 53
             protocol: TCP
             targetPort: 53
           selector:
             k8s-app: kube-dns
           ---
         apiVersion: v1
         kind: ConfigMap
         metadata:
           name: node-local-dns
           namespace: kube-system
         data:
           Corefile: |
               cluster.local:53 {
                   errors
                   cache {
                       success 9984 30
                       denial 9984 5
               }
               reload
               loop
               bind 0.0.0.0
               forward . __PILLAR__CLUSTER__DNS__ {
                       prefer_udp
               }
               prometheus :9253
               health
               }
               in-addr.arpa:53 {
                   errors
                   cache 30
                   reload
                   loop
                   bind 0.0.0.0
                   forward . __PILLAR__CLUSTER__DNS__ {
                           prefer_udp
                   }
                   prometheus :9253
               }
               ip6.arpa:53 {
                   errors
                   cache 30
                   reload
                   loop
                   bind 0.0.0.0
                   forward . __PILLAR__CLUSTER__DNS__ {
                           prefer_udp
                   }
                   prometheus :9253
               }
               .:53 {
                   errors
                   cache 30
                   reload
                   loop
                   bind 0.0.0.0
                   forward . __PILLAR__UPSTREAM__SERVERS__ {
                           prefer_udp
                   }
                   prometheus :9253
               }
         ---
         apiVersion: apps/v1
         kind: DaemonSet
         metadata:
           name: node-local-dns
           namespace: kube-system
           labels:
             k8s-app: node-local-dns
         spec:
         updateStrategy:
         rollingUpdate:
           maxUnavailable: 10%
         selector:
           matchLabels:
             k8s-app: node-local-dns
         template:
           metadata:
             labels:
               k8s-app: node-local-dns
             annotations:
               prometheus.io/port: "9253"
               prometheus.io/scrape: "true"
           spec:
             priorityClassName: system-node-critical
             serviceAccountName: node-local-dns
             dnsPolicy: Default # Do not use cluster DNS.
             tolerations:
             - key: "CriticalAddonsOnly"
               operator: "Exists"
             - effect: "NoExecute"
               operator: "Exists"
             - effect: "NoSchedule"
               operator: "Exists"
             containers:
             - name: node-cache
             image: registry.k8s.io/dns/k8s-dns-node-cache:1.22.20
             resources:
               requests:
                 cpu: 25m
                 memory: 5Mi
               args: [ "-localip", "169.254.20.25,10.96.0.10", "-conf", "/etc/Corefile", "-upstreamsvc", "kube-dns-upstream", "-skipteardown=true", "-setupinterface=false", "-setupiptables=false" ]
               securityContext:
                 privileged: true
               ports:
               - containerPort: 53
                 name: dns
                 protocol: UDP
               - containerPort: 53
                 name: dns-tcp
                 protocol: TCP
               - containerPort: 9253
                 name: metrics
                 protocol: TCP
               livenessProbe:
                 httpGet:
                   path: /health
                   port: 8080
                 initialDelaySeconds: 60
                 timeoutSeconds: 5
               volumeMounts:
               - mountPath: /run/xtables.lock
                 name: xtables-lock
                 readOnly: false
               - name: config-volume
                 mountPath: /etc/coredns
               - name: kube-dns-config
                 mountPath: /etc/kube-dns
               volumes:
               - name: xtables-lock
                 hostPath:
                   path: /run/xtables.lock
                   type: FileOrCreate
               - name: kube-dns-config
                 configMap:
                   name: kube-dns
                   optional: true
               - name: config-volume
                 configMap:
                   name: node-local-dns
                   items:
                     - key: Corefile
                       path: Corefile.base
         ```
       </details>

    2. Примените манифест:

       ```bash
       kubectl apply -f nodelocaldns.yaml
       ```

       В ответе появится список созданных объектов:

       ```bash
       serviceaccount/node-local-dns created
       service/kube-dns-upstream created
       configmap/node-local-dns created
       daemonset.apps/node-local-dns created
       ```

    3. Создайте YAML-файл `nodelocaldnslrp.yaml` с манифестом:

       <details>
         <summary>Файл nodelocaldnslrp.yaml</summary>

         ```yaml
         ---
           apiVersion: "cilium.io/v2"
           kind: CiliumLocalRedirectPolicy
           metadata:
           name: "nodelocaldns"
           namespace: kube-system
           spec:
           redirectFrontend:
               serviceMatcher:
               serviceName: kube-dns
               namespace: kube-system
           redirectBackend:
               localEndpointSelector:
               matchLabels:
                   k8s-app: node-local-dns
               toPorts:
               - port: "53"
                   name: dns
                   protocol: UDP
               - port: "53"
                   name: dns-tcp
                   protocol: TCP
         ```
       </details>

    4. Примените манифест:

       ```bash
       kubectl apply -f nodelocaldnslrp.yaml
       ```

       В ответе появится список созданных объектов:

       ```bash
       ciliumlocalredirectpolicy.cilium.io/NodeLocal DNS created 
       ```
  </TabItem>
</Tabs>

## 2. Проверить работу NodeLocal DNS Cache \{#check-nodelocal-dns-cache-operation}

<Tabs queryString="check-nodelocal-dns-cache">
  <TabItem value="cni-calico" default>
    <TabItemLabel>
      CNI Calico
    </TabItemLabel>

    1. Включите режим логирования для подов NodeLocal DNS. Для этого в манифесте `nodelocaldns.yaml`, в объект `ConfigMap` добавьте строку `log`:

       ```bash
       data:
         Corefile: |
             cluster.local:53 {
               log
               errors
               cache {
                       success 9984 30
                       denial 9984 5
               }
       ```

    2. Примените манифест:

       ```bash
       kubectl apply -f nodelocaldns.yaml
       ```

    3. Запустите под с утилитами для проверки DNS:

       ```bash
       kubectl apply -f https://k8s.io/examples/admin/dns/dnsutils.yaml
       ```

       В ответе появится подтверждение, что под создан:

       ```bash
       pod/dnsutils created
       ```

    4. Убедитесь, что под в статусе `Running`:

       ```bash
       kubectl get pods dnsutils
       ```

    5. Подключитесь к поду:

       ```bash
       kubectl exec -i -t dnsutils -- bash
       ```

    6. Выполните тестовый запрос к DNS-серверу:

       ```bash
       dig @169.254.20.25 selectel.ru
       ```

       Ответ покажет, что запрос успешно выполняется:

       ```bash
       ;; ANSWER SECTION:
       selectel.ru.            30      IN      A       85.119.149.3

       ;; Query time: 437 msec
       ;; SERVER: 169.254.20.25#53(169.254.20.25)
       ;; WHEN: Wed Sep 20 15:43:46 UTC 2023
       ;; MSG SIZE  rcvd: 67
       ```

    7. Проверьте логи:

       ```bash
       kubectl logs --namespace=kube-system -l k8s-app=kube-dns -f
       ```

       В ответе появятся DNS-записи типов A и AAAA с кодом `NOERROR`:

       ```bash
       [INFO] 10.10.71.3:45929 - 55864 "A IN ru-2.cloud.api.selcloud.ru.kube-system.svc.cluster.local. udp 85 false 1232" NOERROR qr,aa,rd,ra 212 3.975488442s
       [INFO] 10.10.71.3:36099 - 21119 "AAAA IN ru-2.cloud.api.selcloud.ru.kube-system.svc.cluster.local. udp 85 false 1232" NOERROR qr,aa,rd 74 3.976010334s
       ```
  </TabItem>

  <TabItem value="cni-cilium">
    <TabItemLabel>
      CNI Cilium
    </TabItemLabel>

    1. Запустите под с утилитами для проверки DNS:

       ```bash
       kubectl apply -f https://k8s.io/examples/admin/dns/dnsutils.yaml
       ```

       В ответе появится подтверждение, что под создан:

       ```bash
       pod/dnsutils created
       ```

    2. Убедитесь, что под в статусе `Running`:

       ```bash
       kubectl get pods dnsutils
       ```

    3. Посмотрите имя ноды кластера, на которой развернут под `dnsutils`:

       ```bash
       kubectl get pod dnsutils -o wide
       ```

       В ответе появится информация о поде `dnsutils`. Например:

       ```bash
       NAME     READY  STATUS   RESTARTS  AGE  IP         NODE        NOMINATED NODE  READINESS GATES
       dnsutils  1/1   Running  0         23h  10.1.0.68  node-01     <none>          <none>
       ```

       Скопируйте значение из столбца `NODE`.

    4. Посмотрите IP-адрес пода, на котором развернут NodeLocal DNS:

       ```bash
       kubectl get pod -o wide -n kube-system | grep 'node-local.*<node_name>'
       ```

       Укажите `<node_name>` — имя ноды кластера, которое вы скопировали на шаге 3.

       В ответе появится информация о поде, на котором развернут NodeLocal DNS, скопируйте его IP-адрес. Например:

       ```bash
       node-local-dns-gv68c  1/1    Running  0         26m  10.0.0.5   node-01     <none>          <none>
       ```

       Здесь `10.0.0.5` — IP-адрес пода.

    5. Посмотрите значения метрики `coredns_dns_requests_total`:

       ```bash
       kubectl exec -i -t dnsutils -- curl http://<ip_address>:9253/metrics | grep coredns_dns_requests_total
       ```

       Укажите `<ip_address>` — IP-адрес пода, который вы скопировали на шаге 4.

       В ответе появится информация о количестве выполненных DNS-запросов. Например:

       ```bash
       # HELP coredns_dns_requests_total Counter of DNS requests made per zone, protocol and family.
       # TYPE coredns_dns_requests_total counter
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="other",zone="."} 1
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="other",zone="cluster.local."} 1
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="other",zone="in-addr.arpa."} 1
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="other",zone="ip6.arpa."} 1
       ```

    6. Выполните тестовые DNS-запросы:

       ```bash
       kubectl exec -i -t dnsutils -- nslookup kubernetes && \
       kubectl exec -i -t dnsutils -- nslookup kubernetes.default && \
       kubectl exec -i -t dnsutils -- nslookup selectel.ru
       ```

       В ответе появится результат разрешения доменных имен внутреннего сервиса кластера и внешнего ресурса. Например:

       ```bash
       Server:		10.96.0.10
       Address:	10.96.0.10#53

       Name:	kubernetes.default.svc.cluster.local
       Address: 10.96.0.1

       Server:		10.96.0.10
       Address:	10.96.0.10#53

       Name:	kubernetes.default.svc.cluster.local
       Address: 10.96.0.1

       Server:		10.96.0.10
       Address:	10.96.0.10#53
       Name:	selectel.ru
       Address: 85.119.149.3
       Name:	selectel.ru
       Address: 2a00:ab00:2000:2::1
       ```

    7. Убедитесь, что значения метрики изменились:

       ```bash
       kubectl exec -i -t dnsutils -- curl http://<ip_address>:9253/metrics | grep coredns_dns_requests_total
       ```

       Укажите `<ip_address>` — IP-адрес пода, который вы скопировали на шаге 4.

       Значения метрики в выводе должны увеличиться. Например:

       ```bash
       # HELP coredns_dns_requests_total Counter of DNS requests made per zone, protocol and family.
       # TYPE coredns_dns_requests_total counter
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="A",zone="."} 1
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="A",zone="cluster.local."} 6
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="AAAA",zone="."} 1
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="AAAA",zone="cluster.local."} 2
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="other",zone="."} 1
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="other",zone="cluster.local."} 1
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="other",zone="in-addr.arpa."} 1
       coredns_dns_requests_total{family="1",proto="udp",server="dns://0.0.0.0:53",type="other",zone="ip6.arpa."} 1 
       ```
  </TabItem>
</Tabs>

<Formbricks />
