company_banner

DNS-поиск в Kubernetes

Автор оригинала: Karan Sharma
  • Перевод
Прим. перев.: Проблема 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 января. Там шла речь, в том числе, и об этой проблеме.

Вот несколько ссылок для дальнейшего изучения:


Примечание: Я предпочел не использовать dig в этой статье. dig автоматически добавляет точку (идентификатор корневой зоны), делая домен «полным» (FQDN), не прогоняя его предварительно через список поиска. Писал об этом в одной из предыдущих публикаций. Тем не менее, довольно удивителен тот факт, что, в общем-то, для стандартного поведения приходится задавать отдельный флаг.

Хорошего DNS'инга! До скорого!

P.S. от переводчика


Читайте также в нашем блоге:

Флант
Специалисты по DevOps и Kubernetes

Комментарии 6

    +2

    Добавлю что ndots=1 скорее всего не поломает резолв внутри кластера. Сначала будет сделан запрос без добавления элементов из search и, если он не вернет результатов, будет использоваться путь поиска.
    ndots=5 сделали чтобы не перегружать корневые dns сервера и чтобы избежать потенциальной mitm атаки.


    Но по мне красивее было бы логику поиска перекинуть на локальный dns server (где есть информация о ресурсах) и тем самым избежать лишнего сетевого трафика.

      +1
      Имеешь в виду, что нужно на машине непосредственно использовать ДНС сервер (BIND9 например)?
      Тут много нюансов может возникнуть, например нужно будет дублировать ДНС сервера на всех нодах кластера, на случай отказа.
      Второй момент, что нужно будет следить за всеми ДНС серверами и поддерживать их конфиги в одинаковом состоянии, что уже сложнее, чем обновлять конфиги пода с CoreDNS.
      Третьим будет такой момент, что при ошибке обновления образуется состояние при котором будем получать разные ответы от разных ДНС. Короче надо будет еще пилить алгоритм консенсуса между ними и выбор мастера.
        0

        Зачем на поднимать на машине? Уже есть CoreDNS.
        Как сделано сейчас:
        для сервиса foo в namespace hello создается одна запись:
        foo.hello.svc.cluster.local.
        В подах в resolv.conf такое:
        search hello.svc.cluster.local svc.cluster.local cluster.local.


        • foo 1 запрос.
        • foo.hello 2 запроса
        • foo.hello.svc 3 запроса
        • mrkaran.dev 4 запроса (3 лишних).

        Варианты уменьшения лишних запросов.
        1) Добавлять записи днс для каждого пути из search. (самый простой и некостыльный)
        Для foo в namespace hello создавать записи в днс.


        • foo.hello.svc.cluster.local.
        • foo.hello.svc.
        • foo.hello.

        Search при этом обрезать до search hello.svc.cluster.local


        • foo 1 запрос.
        • foo.hello 2 запроса
        • foo.hello.svc 2 запроса
        • mrkaran.dev 2 запроса (1 лишний).
          Уже значительно легче для приложений которые интенсивно ходят в интернет. Без сильного оверхеда.

        2) отказаться от search полностью. В дополнение предыдущему варианту добавлять запись в днс без namespace. Но тут надо либо поднимать инстанс DNS в каждом namespace, либо пилить костыли опирающиеся на сеть (source ip, destination ip).

          0

          Upd: первый вариант + ndots:1 решает все проблемы.

            +1
            Понял, меня смутила фраза
            локальный dns server
            . Подумал, что локальный по отношению к хосту на котором под расположен.
      0

      Также если хотите ужаснуться — в гугл клаудде в кубере в search 5 записей по умолчанию (3 куберовские, 2 от гуглового проекта)

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое