Не так давно мы запустили Kubernetes 1.9 на AWS с помощью Kops. Вчера, во время плавного выкатывания нового трафика на самый большой из наших Kubernetes кластеров, я начал замечать необычные ошибки разрешения имен DNS, залогированные нашим приложением.
На GitHub довольно долго об этом говорили, поэтому я тоже решил разобраться. В итоге я понял, что в нашем случае это вызвано повышенной нагрузкой на kube-dns
и dnsmasq
. Самым интересным и новым для меня оказалась сама причина значительного увеличения трафика DNS-запросов. Об этом и о том, что с этим делать, мой пост.
Разрешение DNS внутри контейнера — как и в любой системе Linux — определяется конфигурационным файлом /etc/resolv.conf
. По умолчанию Kubernetes dnsPolicy
это ClusterFirst
, что означает, что любой DNS-запрос будет перенаправлен на dnsmasq
, запущенный в поде kube-dns
внутри кластера, который, в свою очередь, перенаправит запрос в приложение kube-dns
, если имя заканчивается суффиксом кластера, или, в противном случае, к DNS серверу более высокого уровня.
Файл /etc/resolv.conf
внутри каждого контейнера по умолчанию будет выглядеть так:
nameserver 100.64.0.10
search namespace.svc.cluster.local svc.cluster.local cluster.local
eu-west-1.compute.internal
options ndots:5
Как можно заметить, тут три директивы:
- Сервер имен — это IP сервиса
kube-dns
- Указано 4 локальных поисковых домена
search
- Есть опция
ndots:5
Интересной частью этой конфигурации является то, как локальные поисковые домены и настройки ndots:5
уживаются вместе. Чтобы это понять, необходимо разобраться, как работает разрешение DNS для неполных имен.
Что такое полное имя?
Полностью определенное имя — это имя, для которого не будет выполняться локальный поиск, и имя будет считаться абсолютным во время разрешения имен. По соглашению, программное обеспечение DNS считает имя полностью определенным, если оно заканчивается точкой (.), И не полностью определенным в противном случае. То есть google.com.
полностью определено, а google.com
— нет.
Как обрабатывается неполное имя?
Когда приложение подключается к удаленному хосту, указанному в имени, разрешение имен DNS обычно выполняется с помощью системного вызова, например, getaddrinfo()
. А вот если имя неполное (не заканчивается на .), интересно, попытается ли системный вызов сначала разрешить имя как абсолютное, или сначала пройдет через локальные поисковые домены? Это зависит от опции ndots
.
Из мануала по resolv.conf
:
ndots:n
устанавливает порог для количества точек, которые должны появиться в имени, прежде чем будет сделан начальный абсолютный запрос. Значение по умолчанию для n равно 1, что означает, что если в имени есть какие-либо точки, имя будет сначала опробовано как абсолютное имя, прежде чем к нему будут добавлены какие-либо элементы списка поиска.
Это означает, что если для ndots
задано значение 5, а имя содержит менее 5 точек, системный вызов попытается разрешить его последовательно, сначала пройдя по всем локальным поисковым доменам, и, в случае неудачи, в конце концов разрешит его как абсолютное имя.
Почему же ndots:5
может негативно сказаться на производительность приложения?
Как вы понимаете, если ваше приложение использует много внешнего трафика, для каждого установленного TCP-соединения (или, точнее, для каждого разрешенного имени) оно будет выдавать 5 DNS-запросов, прежде чем имя будет правильно разрешено, потому что оно сначала пройдет через 4 локальных поисковых домена, а в конце выдаст запрос разрешения абсолютного имени.
На следующей диаграмме показан суммарный трафик на наших 3 модулях kube-dns до и после того, как мы переключили несколько имен хостов, настроенных в нашем приложении, на полностью определенные.
На следующей диаграмме показана задержка приложения до и после того, как мы переключили несколько имен хостов, настроенных в нашем приложении, на полные (вертикальная синяя линия это развертывание):
Решение #1 — использовать полностью определенные имена
Если у вас мало статических внешних имен (т. е. определенных в конфигурации приложения), к которым вы создаете большое количество соединений, возможно, самое простое решение — переключить их на полностью определенные, просто добавив. в конце.
Это не окончательное решение, но помогает быстро, пусть и не чисто, улучшить ситуацию. Этот патч мы применили для решения нашей проблемы, результаты чего были показаны на скриншотах выше.
Решение #2 — кастомизация ndots
в dnsConfig
В Kubernetes 1.9 в альфа режиме появился функционал (бета-версия v1.10), который позволяет лучше контролировать параметры DNS через свойство пода в dnsConfig
. Среди прочего, он позволяет настроить значение ndots
для конкретного пода, т.е.
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: dns-example
spec:
containers:
- name: test
image: nginx
dnsConfig:
options:
- name: ndots
value: "1"
Источники
Также читайте другие статьи в нашем блоге:
Комментарии (2)
gecube
20.08.2019 19:10Интересная статья, с практическим эффектом. Наглядно. Вопрос только в том — как это матчится с практикой все завимости сервисов определять как некое имя внутри кубернетес кластера (ресурс типа Service).
inkvizitor68sl
При чём тут докер вообще? Или вы теперь будете все статьи о linux переписывать по новой, добавив слово «kubernetes»?
inky@onyxia:~$ man resolv.conf | grep ' ndots:n' -A4
ndots:n
Sets a threshold for the number of dots which must appear in a name given to res_query(3) (see resolver(3)) before an
initial absolute query will be made. The default for n is 1, meaning that if there are any dots in a name, the name
will be tried first as an absolute name before any search list elements are appended to it. The value for this option
is silently capped to 15.