Категории: DevOps

Конфигурирование DNS в кластере Kubernetes

Процесс резолвинга имен

Каждый раз, когда создается новый под, k8s делает запись о новом поде в DNS-сервере, а также прокидывает настройки DNS в под, создавая файл /etc/resolv.conf. Он указывает на IP-адрес DNS- сервера, который необходимо использовать. Этот файл реплицируется с настроек ноды, на которой был создан под.

Записи для подов на DNS сервере создаются в dash-формате: он создает новое имя хоста, заменяя точки тире в IP-адресе подов, например 10-244-2-5.

Для общения подов между собой в пределах одного неймспейса, можно использовать неполное квалифицированное имя, а вот если поды находятся в разных пространствах имен, то необходимо указывать полное имя FQDN.

Такой же принцип адресации используют и сервисы. На самом деле, взаимодействие в кластере всегда происходит через сервисы, которые создают используют endpoints с IP адресами для адресации подов, которые стоят за этими сервисами. Формат FQDN имени таков:

Для сервисов: svcname.namespace.type.rootDomain
Для подов: hostname.namespace.type.rootDomain

Пример:

For services: test-service.default.svc.cluster.local
For pods: 10-244-2-5.default.pod.cluster.local

DNS-сервер в кластере

Разрешение имен DNS настраивается в кластере Kubernetes через сервис CoreDNS. Kubelet настраивает файл /etc/resolv.conf каждого пода на использование пода CoreDNS в качестве сервера имен. Поды взаимодействуют через сервисы в кластере k8s, и CoreDNS содержит записи для этих сервисов (по умолчанию записи подов отключены, но вы можете включить их в файле CoreDNS).

Хотя CoreDNS и устаревший Kube-dns в конечном итоге выполняют одну и ту же задачу, существуют некоторые ключевые различия в реализации, которые влияют на потребление ресурсов и производительность. Вы можете подробно прочитать об этом в официальной документации CoreDNS.

CoreDNS доступен в Kubernetes начиная с версии 1.9 и является дефолтным начиная с версии 1.12+. Это быстрый и гибкий DNS-сервер. Ключевое слово «гибкий» здесь означает, что если какой-то функционал не предусмотрен из коробки, его можно добавить, написав плагин. Он написан на языке Go.

CoreDNS развертывается как deployment в пространстве имен kube-system в кластере с сервисом с именем «kube-dns». Сервис представляет собой два пода как часть ReplicaSet для резервирования. На самом деле они представляют собой набор реплик внутри деплоймента.

Файл конфигурации CoreDNS

Для CoreDNS требуется файл конфигурации. Он использует файл с именем Corefile, расположенный в /etc/coredns.  В этом файле может быть настроено несколько плагинов. Плагины настроиваются для обработки ошибок, составления отчетов о работоспособности, мониторинга показателей, кеша и т. д.

Мы передаем Corefile в виде ConfigMap, чтобы он оставался отделенным от деплоймента CoreDNS . У него уже настроены плагины по-умолчанию. Вы можете посмотреть список цепочек плагинов здесь.

Вот пример Corefile:

.:53 {
        errors
        log
        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
        cache 30
        loop
        reload
        loadbalance
    }

Плагин, который обеспечивает работу CoreDNS с Kubernetes — это плагин Kubernetes (8я строка в коде выше). Здесь задается доменное имя верхнего уровня для кластера. В данном случае cluster.local. Таким образом, каждая запись в DNS-сервере подпадает под этот домен. В плагине Kubernetes есть несколько опций.

Параметр pods, который вы видите здесь, отвечает за создание записи для подов в кластере. Помните, мы говорили о создании записи для каждого пода путем преобразования их IP-адресов в дефис-формат, который по умолчанию отключен. Но его можно включить здесь с помощью этой записи.

Любая запись, которую этот DNS-сервер не может разрешить, например, под пытается связаться с www.google.com, она перенаправляется на сервер имен, указанный в файле /etc/resolv.conf самого CoreDNS по директиве forward.

Плагин kubernetes отслеживает конечные точки через API Discovery.EndpointSlices. Когда CoreDNS запускается с включенным плагином kubernetes, он задерживает обслуживание DNS на срок до 5 секунд, пока тот не сможет подключиться к Kubernetes API и синхронизировать все отслеживания за объектами. Если этого не произойдет в течение 5 секунд, CoreDNS начнет обслуживать DNS, в то время как плагин kubernetes продолжает пытаться подключиться и синхронизировать все объекты. CoreDNS ответит SERVFAIL на любой запрос к записи Kubernetes, которая еще не была синхронизирована.

Настройка плагина kubernetes

Внутри плагина kubernetes есть множество опций, которые вы можете использовать. Рассмотрим опции, которые мы используем в приведенном выше базовом файле. Общий формат плагин kubernetes таков:

kubernetes [ZONES...] {
   endpoint URL
   tls CERT KEY CACERT
   kubeconfig KUBECONFIG [CONTEXT]
   namespaces NAMESPACE...
   labels EXPRESSION
   pods POD-MODE
   endpoint_pod_names
   ttl TTL
   noendpoints
   fallthrough [ZONES...]
   ignore empty_service
}
  • pods POD-MODE устанавливает режим обработки А-записей подов на основе IP, например 10-244-2-5.default.pod.cluster.local. в А 10.244.2.5. Эта опция предусмотрена для облегчения использования сертификатов SSL при прямом подключении к модулям. Значение для POD-MODE, которое мы использовали «insecure» всегда возвращает запись A подов.
  • Fallthrough [ZONES…] Если запрос в зонах, для которых плагин является авторитетным, он либо возвращает результат, либо возвращает NXDOMAIN для запроса. Ответы NXDOMAIN создаются, когда в DNS нет списка запрошенного домена. Когда включен fallthrough, вместо возврата NXDOMAIN, когда запись не найдена, плагин передает запрос вниз по цепочке плагинов, которая может включать другой плагин для обработки запроса.
  • ttl позволяет вам установить собственный срок жизни для ответов. По умолчанию — 5 секунд. Минимальное допустимое значение TTL составляет 0 секунд, а максимальное ограничено 3600 секундами. Установка TTL на 0 предотвратит кэширование записей.

Описание других параметров можно посмотреть в официальной документации.

Настройка подов

Следующий шаг — настроить клиентские поды на использование сервера CoreDNS. Когда мы развертываем CoreDNS, оно также создает сервис, который делает его доступным для других компонентов в кластере. По умолчанию служба называется kube-dns. IP-адрес этой службы настраивается как сервер имен на всех подах.

Конфигурация DNS на поде выполняется Kubernetes автоматически при его создании — за это отвечает kubelet. Если вы посмотрите файл конфигурации кублета, то увидите в нем IP-адрес DNS-сервера и домена.

Настройки кублета можно увидеть или в файле описании системного сервиса kubelet /lib/systemd/system/kubelet.service. Там в секции [Service] передается IP DNS сервера и домен:

[Service]
ExecStart=/usr/bin/kubelet \
  --allow-privileged=true \
  --cloud-provider= \
  --cluster-dns=10.96.0.10  \
  --cluster-domain=cluster.local \

Важно, чтобы cо всех нод, на которых крутятся поды, был открыт 53 порт в направлении ноды, на которой расположены поды CoreDNS.

В minikube конфигурация кублета расположена в /var/lib/kubelet/config.yaml

При настройке кластера через kubeadm также можно указать yaml файл с конфигурацией KubeletConfiguration и передать его через kubeadm ... --config some-config-file.yaml

Кублет создает на подах локальных файл резолвинга /etc/resolv.conf со примерно следующей конфигурацией:

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

В нем

nameserver: куда перенаправляются DNS-запросы. В нашем случае это адрес службы CoreDNS.

search: список доменов для поиска полного имени домена (FQDN). представляет путь поиска для определенного домена. Стандартное соглашение, которому следуют большинство преобразователей DNS, заключается в том, что если домен заканчивается на . (представляющий корневую зону), домен считается полным доменным именем. Некоторые преобразователи пытаются действовать умно и добавляют расширение . сами себя. Итак, hww.ru. является полным доменным именем, а hww.ru — нет.

options ndots: Это самое интересное значение, и вокруг него возникает много проблем. ndots представляет собой пороговое значение количества точек в имени запроса, позволяющее считать его «полным» доменным именем.

Дефолтное значение ndots в кубер-кластере — 5. Зачастую, это может приводить к снижению производительности DNS и даже к таймаутам в ответах при обращении к ресурсам за пределами кластера. Так например, если обратиться к hww.ru, то поскольку имя не содержит достаточного числа точек и точки в конце, то резолвер пойдет подставлять суффиксы из search листа, чтобы определить FQDN.

Запрос повторяется по всем путям поиска до тех пор, пока ответ не будет содержать код NOERROR (который DNS-клиенты понимают и сохраняют его как результат). NXDOMAIN просто указывает, что для этого доменного имени не найдено ни одной записи.

А вот если значение ndots сделать равным ndots:1, то уже увидев одну точку в имени hww.ru, он будет считать его FQDN-ом. Это значительно ускоряет форвардинг запросов на внешние по отношению к кластеру DNS сервера, но и может привести к проблемам с внутренними именами, например, в других неймспейсах. Так, имя hello.svc резолвер будет считать полным и не будет искать его по листу в других неймспейсах, поэтому придется указывать его в манифестах в полной форме.

Кастомный dnsConfig

Для отдельного пода можно настроить ndots через его манифест:

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsConfig:
    options:
      - name: ndots
        value: "1"

stubDomains и upstreamNameservers

Домен-заглушка (stubDomain) или зона-заглушка — это своего рода динамическая условная переадресация. Зона-заглушка автоматически отражает изменения в записях NS удаленных DNS-серверов путем пересылки запросов на них.

Ниже мы используем плагин forward  для реализации stubDomain, который пересылает example.local на сервер имен 10.100.0.10:53. Также настроен восходящий сервер имен 8.8.8.8:53, который будет использоваться для разрешения имен, не попадающих в cluster.local или example.local.

cluster.local:53 {
    kubernetes cluster.local
}
example.local {
    forward . 10.100.0.10:53
}
. {
    forward . 8.8.8.8:53
}

Мониторинг DNS запросов

Помимо того, что при включении полного логирования в CoreDNS можно увидеть все итерации попыток резолвинга доменов, можно также обратиться к метрикам.

В конфигурации Corefile выше мы видели плагин prometheus — он позволяет экспортировать метрики в популярном формате.

DNS резолвинг в AWS EKS

Дефолтный IP службы AWS Route53 169.254.169.253, но это не тот адрес, который используется в EKS кластере. В EKS по умолчанию используется 10.100.0.10 или 172.20.0.10, и его можно поменять при желании.

Если вы используется кастомные диапазоны сабнетов VPC для EKS, то DNS резолвинг с серверами по-умолчанию у вас работать не будет. Передать конфигурацию кластерного DNS можно через настройки node groups. Для этого есть несколько способов:

  • в launch templates для EC2 нод можно указать user data, которая используется cloud-init скриптом при бутстрапе ноды. В частности, скрипту /etc/eks/bootstrap.sh можно передать параметр —dns-cluster-ip со значением локального DNS сервера EKS сабнета, которое всегда заканчивается на .10.  Чтобы узнать CIDR сабнета, выполните команду
    aws eks describe-cluster --query "cluster.kubernetesNetworkConfig.serviceIpv4Cidr" --output text --name my-cluster --region region-code
  • можно использовать eksctl для конфигурации кублета. Файлы конфигурации принимают строковое поле с именем clusterDNS с IP-адресом используемого DNS-сервера. Это будет передано кубелету, который, в свою очередь, передаст его подам через файл /etc/resolv.conf.
    apiVersion: eksctl.io/v1alpha5
    kind: ClusterConfig
    
    metadata:
      name: cluster-1
      region: eu-north-1
    
    nodeGroups:
      - name: ng-1
        kubeletExtraConfig:
          clusterDNS: ["169.254.20.10","172.20.0.10"]
  • если для создания и автоскейлинга нод используется Karpenter, то свойства кублета можно поменять через провиженер:
    apiVersion: karpenter.sh/v1alpha5
    kind: Provisioner
    metadata:
      name: default
    spec:
      kubeletConfiguration:
        clusterDNS: ["10.160.0.10"]
        containerRuntime: containerd

 

[Посещений: 1 280, из них сегодня: 2]
Share
Опубликовал
Александр Дудкин

Свежие посты

Процессы зомби, демоны и сироты в Linux

Процессы и программы Программа в Unix — это последовательность исполняемых инструкций на диске. Вы можете…

12 октября 2024

Изучаем сертификаты, приватные ключи и keystore

Существует большое разнообразие форматов, в которых создаются сертификаты и приватные ключи для них. Часто они…

20 июля 2024

Восстановление доступа к Docker Hub

Все известно, что Докерхаб закрыл доступ для пользователей из санкционных стран, включая РФ и РБ.…

30 мая 2024

Как посмотреть сертификат хоста через командную строку

Зачастую бывает необходимо проверить, а какой SSL сертификат отдает тот или иной хост на определенном…

21 февраля 2024

Использование choco через прокси

Choco - лучший пакетный менеджер для Windows. Чтобы использовать его в корпоративной среде за прокси,…

21 февраля 2024

Обзор SSD диска XrayDisk

В России становится все больше малоизвестных китайских товаров, поэтому сегодня у нас на обзоре китайский…

3 декабря 2023