Прим. перев.: Проблема DNS в Kubernetes, а точнее — настройки параметра ndots
, — на удивление популярна, причём уже не первый год. В очередной заметке по этой теме её автор — DevOps-инженер из крупной брокерской компании в Индии — в весьма простой и лаконичной манере рассказывает, о чём полезно знать коллегам, эксплуатирующим Kubernetes.
Одно из главных преимуществ развёртывания приложений в Kubernetes — беспроблемное обнаружение приложений. Внутрикластерное взаимодействие сильно упрощается благодаря концепции сервиса (Service), которая представляет собой виртуальный IP, поддерживающий набор IP-адресов pod'ов. Например, если сервис vanilla
желает связаться с сервисом chocolate
, он может обратиться напрямую к виртуальному IP для chocolate
. Возникает вопрос: кто в данном случае разрешит DNS-запрос к chocolate
и как?
Разрешение имен DNS настраивается в кластере Kubernetes с помощью CoreDNS. Kubelet прописывает pod с CoreDNS в качестве сервера имен в файлах /etc/resolv.conf
всех pod'ов. Если посмотреть на содержимое /etc/resolv.conf
любого pod'а, оно будет выглядеть примерно следующим образом:
search hello.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.152.183.10
options ndots:5
Эта конфигурация используется DNS-клиентами для перенаправления запросов на DNS-сервер. В файле resolv.conf
содержится следующая информация:
- nameserver: cервер, на который будут направляться DNS-запросы. В нашем случае это адрес сервиса CoreDNS;
- search: определяет путь поиска определенного домена. Любопытно, что
google.com
илиmrkaran.dev
не являются FQDN (полными доменными именами). Согласно стандартному соглашению, которому следуют большинство resolver'ов DNS, полными (FDQN) доменами считаются только те, которые заканчиваются точкой «.», представляющей корневую зону. Некоторые resolver'ы умеют добавлять точку самостоятельно. Таким образом,mrkaran.dev.
— полное доменное имя (FQDN), аmrkaran.dev
— нет; - ndots: Самый интересный параметр (эта статья именно о нем).
ndots
задает пороговое число точек в имени запроса, при достижении которого оно рассматривается как «полное» доменное имя. Подробнее об этом мы поговорим позже, когда будем анализировать последовательность DNS-поиска.
Давайте посмотрим, что происходит, когда мы запрашиваем mrkaran.dev
в pod'е:
$ nslookup mrkaran.dev
Server: 10.152.183.10
Address: 10.152.183.10#53
Non-authoritative answer:
Name: mrkaran.dev
Address: 157.230.35.153
Name: mrkaran.dev
Address: 2400:6180:0:d1::519:6001
Для данного эксперимента я установил уровень логирования CoreDNS на all
(что делает его весьма многословным). Посмотрим на логи pod'а coredns
:
[INFO] 10.1.28.1:35998 - 11131 "A IN mrkaran.dev.hello.svc.cluster.local. udp 53 false 512" NXDOMAIN qr,aa,rd 146 0.000263728s
[INFO] 10.1.28.1:34040 - 36853 "A IN mrkaran.dev.svc.cluster.local. udp 47 false 512" NXDOMAIN qr,aa,rd 140 0.000214201s
[INFO] 10.1.28.1:33468 - 29482 "A IN mrkaran.dev.cluster.local. udp 43 false 512" NXDOMAIN qr,aa,rd 136 0.000156107s
[INFO] 10.1.28.1:58471 - 45814 "A IN mrkaran.dev. udp 29 false 512" NOERROR qr,rd,ra 56 0.110263459s
[INFO] 10.1.28.1:54800 - 2463 "AAAA IN mrkaran.dev. udp 29 false 512" NOERROR qr,rd,ra 68 0.145091744s
Фух. Две вещи здесь привлекают внимание:
- Запрос проходит по всем этапам поиска до тех пор, пока ответ не будет содержать код
NOERROR
(DNS-клиенты его понимают и хранят как результат).NXDOMAIN
означает, что для данного доменного имени запись не найдена. Посколькуmrkaran.dev
не является FQDN-именем (в соответствии сndots=5
), resolver смотрит на поисковый путь и определяет порядок запросов; - Записи
А
иАААА
поступают параллельно. Дело в том, что разовые запросы в/etc/resolv.conf
по умолчанию настроены таким образом, что производится параллельный поиск по протоколам IPv4 и IPv6. Отменить подобное поведение можно, добавив опциюsingle-request
вresolv.conf
.
Примечание: glibc
можно настроить на последовательную отправку этих запросов, а musl
— нет, так что пользователям Alpine следует принять это к сведению.
Экспериментируем с ndots
Давайте еще немного поэкспериментируем с ndots
и посмотрим, как себя ведет этот параметр. Идея проста: ndots
определяет, будет ли DNS-клиент считать домен абсолютным или относительным. Например, как в случае простого google DNS-клиент узнает, является ли этот домен абсолютным? Если задать ndots
равным 1, клиент скажет: «О, в google
нет ни одной точки; пожалуй, пробегусь по всему списку поиска». Однако если запросить google.com
, список суффиксов будет целиком проигнорирован, поскольку запрошенное имя удовлетворяет порогу ndots
(имеется хотя бы одна точка).
Давайте убедимся в этом:
$ cat /etc/resolv.conf
options ndots:1
$ nslookup mrkaran
Server: 10.152.183.10
Address: 10.152.183.10#53
** server can't find mrkaran: NXDOMAIN
Логи CoreDNS:
[INFO] 10.1.28.1:52495 - 2606 "A IN mrkaran.hello.svc.cluster.local. udp 49 false 512" NXDOMAIN qr,aa,rd 142 0.000524939s
[INFO] 10.1.28.1:59287 - 57522 "A IN mrkaran.svc.cluster.local. udp 43 false 512" NXDOMAIN qr,aa,rd 136 0.000368277s
[INFO] 10.1.28.1:53086 - 4863 "A IN mrkaran.cluster.local. udp 39 false 512" NXDOMAIN qr,aa,rd 132 0.000355344s
[INFO] 10.1.28.1:56863 - 41678 "A IN mrkaran. udp 25 false 512" NXDOMAIN qr,rd,ra 100 0.034629206s
Поскольку в mrkaran
нет ни одной точки, поиск проводился по всему списку суффиксов.
Примечание: на практике максимальное значение ndots
ограничено 15; по умолчанию в Kubernetes оно равно 5.
Применение в production
Если приложение выполняет множество внешних сетевых вызовов, DNS может стать узким местом в случае активного трафика, поскольку при разрешении имени выполняется множество лишних запросов (прежде чем система доберется до нужного). Приложения обычно не добавляют корневую зону к доменным именам, однако это вполне тянет на хак. То есть вместо того, чтобы запрашивать api.twitter.com
, вы можете за'hardcode'ить api.twitter.com.
(с точкой) в приложении, что побудит DNS-клиентов выполнять авторитетный поиск сразу в абсолютном домене.
Кроме того, начиная с версии Kubernetes 1.14, расширения dnsConfig
и dnsPolicy
получили статус стабильных. Таким образом, при развертывании pod'а можно уменьшить значение ndots
, скажем, до 3 (и даже до 1!). Из-за этого каждое сообщение внутри узла должно будет включать полный домен. Это один из классических компромиссов, когда приходится выбирать между производительностью и переносимостью. Мне кажется, что переживать об этом стоит только в случае, если сверхнизкие задержки жизненно важны для вашего приложения, поскольку результаты DNS также кэшируются внутри.
Ссылки
Впервые об этой особенности я узнал на K8s-meetup'е, прошедшем 25 января. Там шла речь, в том числе, и об этой проблеме.
Вот несколько ссылок для дальнейшего изучения:
- Объяснение, почему ndots=5 в Kubernetes;
- Отличный материал о том, как изменение ndots отражается на производительности приложения;
- Расхождения между resolver'ами musl и glibc.
Примечание: Я предпочел не использовать dig
в этой статье. dig
автоматически добавляет точку (идентификатор корневой зоны), делая домен «полным» (FQDN), не прогоняя его предварительно через список поиска. Писал об этом в одной из предыдущих публикаций. Тем не менее, довольно удивителен тот факт, что, в общем-то, для стандартного поведения приходится задавать отдельный флаг.
Хорошего DNS'инга! До скорого!
P.S. от переводчика
Читайте также в нашем блоге:
- «Calico для сети в Kubernetes: знакомство и немного из опыта»;
- «CoreDNS — DNS-сервер для мира cloud native и Service Discovery для Kubernetes»;
- «Иллюстрированное руководство по устройству сети в Kubernetes»: части 1 и 2 (сетевая модель, оверлейные сети), часть 3 (сервисы и обработка трафика).
Автор: Юлия Шарафитдинова