новости
статьи
.sysadmin

кэширующие рекурсивные DNS или чем плох bind9

чем плох bind9

До поры до времени я, как и многие, полагал, что bind - это хороший DNS-сервер, и искать ему замену смысла нет. Но выяснилось, что под большой нагрузкой в качестве кэширующего рекурсора bind9 работает просто отвратительно. Раз в cleaning-interval минут он чистит свой кеш от записей с истекшим TTL. Процедура это на редкость ресурсоемка - на кэше размером 256 Mb она занимает около минуты времени CPU (реального времени получается больше). И в течение этого времени он не отвечает на запросы. Оценить количество потерянных запросов можно, например, по счетчику "dropped due to full socket buffers" в netstat -s -p udp.

Почитал архивы bind-users@ - оказалось, что проблема эта известная. Рекомендации по решению этой проблемы были такие:

1. Собрать bind с ISC_MEM_USE_INTERNAL_MALLOC=1

2. Уменьшить DNS_CACHE_CLEANERINCREMENT в lib/dns/cache.c

1-й пункт - это использование встроенного аллокатора bind вместо системного malloc(). Попробовал. Ничего не изменилось. Очистка кэша осталось такой же неоправданно ресурсоемкой операцией, как и с системным malloc().

2-й пункт. Сам процесс очистки записей разбит на итерации. То есть проверяются не все записи подряд, а по DNS_CACHE_CLEANERINCREMENT штук, по умолчанию это 1000. В перерывах между отдельными итерациями он что то еще делает, возможно даже отвечает на часть запросов, но в целом они идут практически одна за другой. Пробовал уменьшать это значение до 200. Потерянных запросов стало меньше, но все равно терялось больше половины. Увеличил вдобавок к этому net.inet.udp.recvspace - количество потерь еще немного уменьшилось - во время чистки кэша теряется порядка половины запросов.

тестирование

В связи с этим решил протестировать другие серверы: PowerDNS Recursor и MaraDNS.

Тест проводился так: было сделано 3 файла по 65024 IP-адресов в каждом (все адреса разные). И потом эти три файла одновременно ресолвились утилитой ip2host и наблюдалось, сколько ресурсов кушает DNS-сервер.

ip2host < ip_list_1 > /dev/null&
ip2host < ip_list_2 > /dev/null&
ip2host < ip_list_3 > /dev/null&


И потом второй проход для получения того же, но уже из кэша.

Результаты получились такие:



То есть при нормальной работе лучшим оказался bind, немного похуже pdns recursor и совсем плохо MaraDNS, что вполне логично, если учитывать модели их работы.

модель обработки соединений

Bind использует FSM (конечный автомат). Для всех запросов к внешним серверам используется один и тот же сокет. Что позволяет в минимальном варианте держать постоянно открытыми всего два сокета - на 53-м порту, чтобы принимать запросы от клиентов и второй сокет для запросов к внешним серверам. Для их опроса используется select(), который при всего двух дескрипторах работает эффективно (если их много, select неэффективен). Pdns recursor тоже использует FSM, но на каждый исходящий запрос открывает новый сокет. Это делает его более устойчивым к отравлению кэша, но требует немного больше ресурсов. Для опроса сокетов под FreeBSD используется kqueue().

MaraDNS использует тредовую модель (!). Сколько одновременных выполняемых запросов, столько и потоков. Вполне логично, что это сопряжено с большими накладными расходами.

очистка кеша

Bind очищает кэш, как описано выше, то есть очень плохо, что подтверждается практикой.

Pdns recursor - раз в 10 минут проходится 10000 записей. Проверить в синтетических тестах это сложно, но по всей видимости процесс очистки кэша не должен вызывать проблем, так как за раз проверяется не весь кэш, а только 10 тысяч записей (то есть сравнительно небольшая часть кэша). MaraDNS на практике также не тестировалось, но модель хорошая - никаких периодических чисток нет. Вместо этого, когда не хватает места для новой записи, просто удаляется несколько старых записей. Если они отсортированы по "time to die" то это требует очень мало ресурсов. Модель очень хорошая (так, например, делает memcached), но реализована, насколько я понял, не очень хорошо.

поддержка стандартов

bind
- кроме того, что это самый распространенный DNS сервер это еще и reference implementation, т. е. если где то стандарт можно толковать по разному, то делать надо так как это сделано в bind. Поддерживает кучу разных нужных и не очень расширений протокола DNS (в папке doc лежит 96 RFC).

Pdn recursor - серьезных проблем не замечено. Есть настройки которые делают его поведение не полностью стандартным, но это вполне осмыслено.

MaraDNS - похоже у автора излишне творческое отношение к стандартам. В частности если прописано несколько одинаковых записей он (dns roud robin) он отдает не все, а только часть из них (из за ограничений структур данных).

что хочется

А хочется кэширующий DNS сервер который использует модель обработки соединений как в bind и организацию кэша подобно тому как это сделано в memcached: при запросе если запись есть в кэше проверять её TTL - если не истек отдавать клиенту и обновлять метку last used, если истек то удалять и заново спрашивать у удаленных серверов. Если в кеше не хватает места удалять по LRU - т. е. удалять те записи, которые давно никто не спрашивал.



Anton Yuzhaninov
обсудить статью
© сетевые решения
.
.