До поры до времени я как и многие полагал что bind это хороший DNS
сервер и искать ему замену смысла нет, к тому же во FreeBSD он в base
system. Но выяснилось, что под большой нагрузкой в качестве
кэширующего рекурсора 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 в каждом (все ip
разные). И потом эти три файла одновременно ресолвились утилитой
ip2host и наблюдалась сколько ресурсов кушает dns-сервер.
И потом второй проход для получения того же, но уже из кеша.
Результаты получились такие:
Сервер cpu time (секунд) при получении записей которых нет в кэше cpu
time (секунд) при получении записей из кэша Занимаемая память (RES), Mb
total user system total user system
bind9.3.2 61 48.01 12.99 18.81 14.01 4.8 118
PowerDNS recursor 3.1.4 88.01 70.86 17.15 33.35 27.45 5.9 46
MaraDNS 1.2.12.04 103.01 87.05 15.96 60.22 54.86 5.36 95
Т. е. при нормальной работе лучшем оказался 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 - т. е. удалять те записи, которые
давно никто не спрашивал.
P.S. djbdns пока не смотрел, но зная другие творения доктора
Бернштейна, ничего хорошего ждать не приходится.
================
pdnsd пока не смотрел, но он тредовый. А тредовые приложения как
правило работают хуже FSM (и имеют больше проблем с надежностью).
Другое дело что на FSM не все задачи можно положить (без титанических
усилий), но рекурсивный DNS хорошо реализуется в виде FSM. Что и
сделано в bind и Powerdnsd. Будет время и pdnsd протестирую. Но пока
кандидаты production это powerdns recursor и maradns (он хоть и
тредовый но с кешем по описанию хорошо работает).
Что касается djbdnsd то он уже 5 лет не поддерживается и у него
дурацкая лицензия, которая не позволяет распространять измененный код.
Часть его недостатков описано тут
Пока кандидаты в production это PowerDNS recursor и MaraDNS, но
требуется дополнительное тестирование, изучение их исходного кода и
изучение степени соответствия стандартам. bind используется
практически повсеместно, и если что то не может проресолвить bind это
не сможет проресолвить большинство. При этом вполне могут существовать
хосты которые не смогут корректно проресолить Power и Mara, т. к. они
сравнительно редко используются и такие проблемы могут быть долго не
замеченными. И это необязательно могут быть ошибки в софте, это может
быть и какое ни будь сочетание настроек разных удаленных DNS серверов.
Что касается bind то есть еще такой негативный с моей точки зрения
момент - доступ к CVS только платный, открытого доступа к RT (bug
tracker) нет и не планируется. Т. е. постороннему человеку
подключиться к процессу разработки очень сложно.
=================
Правильно приготовленная FSM масштабируется очень хорошо. Да, в случае
SMP чтобы использовать все процессоры нужно либо форкать несколько
процессов (как это делает nginx) или создавать несколько потоков (как
это умеет bind9, но пока работа в тредовом режиме там реализована
плохо).
Что касается того, что не параллелится то параллелить нужно только
тогда когда есть блокирующиеся операции. Сокеты умеют работать в
неблокирующемся режиме, дисковые операции кэширующему dns не нужны,
задач которые требуют длительной работы CPU при правильном дизайне
тоже быть не должно.
Безусловно у FSM есть свои недостатки, но в такой задаче как
кэширующий dns проявляется только один из них (и то не в полной мере)
- более ысокая сложность реализации, по сравнению с тредовой моделью.
В принципе можно так на пальцах объяснить почему на такой задаче FSM
масштабируется лучше:
При обработке рекурсивного dns-запроса тредовым приложением поток
посылает запрос к удаленному серверу, потом ждет ответа (или таймауа).
когда получит ответ пишет его в кэш и отвечает клиенту. В итоге
получается, что большинство потоков у нас ничего не делают и находятся
в состоянии ожидания ответа от удаленного сервера. Потоки конечно
гораздо более легкий объект чем процесс, но с ними все равно сопряжены
определенные накладные расходы. Я где то в рассылках freebsd видел
результаты тестов, по которым на однопроцессорной машине bind9 с двумя
потоками работает чуть ли на в полтора раза медленнее чем с одним.
Применительно к модели по которой стоит писать кэширующий dns весьма
кстати такая цитата:
"A computer is a state machine. Threads are for people who can't
program state machines."
-- Alan Cox
1051 Прочтений • [Кэширующие рекурсивные DNS или чем плох bind9 (bind dns cache tune speed optimization recursive)] [08.05.2012] [Комментариев: 0]