Немного лирики:

Не так давно я сменил место работы и теперь мои приключения в области веб-технологий продолжаются после почти годового прозябания в роли офисного админа. Хотя надо признать, что "прозябание" было достаточно интересным - мне удалось в достаточно глубокой мере изучить системы на базе процессоров ARM и программную составляющую в лице ubuntu с различными ядрами под них. Это было поучительно и даже где-то интересно.

Возвращаясь к теме, одна из первых вещей, которую я узнал: оказывается, референсная реализация протокола DNS в лице сервера ISC BIND работает не самым лучшим образом. Справедливости ради надо отметить, что BIND - очень хороший DNS-сервер, многие хуже.

Например, когда дело касается разного рода подписей пакетов, обмена какими-то секъюрными данными, на фоне того же PowerDNS BIND начинает значительно проседать, в десятки раз, а если нагрузка заметная (публичный DNS|DNS провайдера) то и того больше. Второй момент - это чистка кэша, у BIND есть некий сборщик мусора и когда наступает время почистить кэш от просроченных записей количество необработанных dns-запросов возрастает весьма внушительно (если чистится много записей, например пара сотен тысяч, то вплоть до 100%).

Вот, например, пара ссылок на разного рода соображения по поводу производительности этих серверов:
<a href="http://www.opennet.ru/base/net/bind_problem.txt.html">http://www.opennet.ru/base/net/bind_problem.txt.html</a>
<a href="http://wiki.sirmax.noname.com.ua/index.php/Dns_tests">http://wiki.sirmax.noname.com.ua/index.php/Dns_tests</a>

В dnsmasq, pdnsd, dnrd, как в рекурсивных кэширующих dns-ках, я разочаровался давно. Некоторые из них довольно глючные, как dnsmasq, который иногда просто забывает дать ответ на запрос или даёт его, когда все мыслимые таймауты уже прошли, другие, как pdnsd случается, что залипают и перестают давать ответы вообще. Однако их всех объединяет одно: при веб-сёрфинге по тяжёлым сайтам они, даже отдавая ответы из кэша отдают их с большим разбросом по времени отклика, что говорит о не очень удачной архитектуре приложения (напр. pdnsd вообще построен (о ужос!) на тредах, как апач с threaded-mpm).

Я давненько искал альтернативу тому самому dnsmasq, который так любит запускать NetworkManager. А то без локального dns-кэша как-то не всегда удобно работать. И вот не жданно не гаданно я такую альтернативу нашёл.

Собственно, чем примечателен PowerDNS? А тем, что приложение разрабатывается в 2-х формах - рекурсивный сервер и авторитативный сервер. Рекурсор умеет рекурсивные запросы, а авторитативный умеет их направлять к рекурсивному серверу. Но авторитативный сервер умеет доставать информацию о своих зонах из разных бэкэндов(Sql-базы, LDAP и проч.), в том числе из файлов зон (на минуточку) сервера BIND и это круто! Меня заинтересовал рекурсор, его можно запускать даже без конфиг-файла, передавая нужные параметры прямо в ком. строке.

Итак, мы подошли к постановке задачи: Нужно, чтобы при присоединении к сети, при получении параметров сети, после подъёма интерфейса поднять локальный кэширующий dns-сервер, прописать ему апсримы и тыкнуть в него систему.

В качестве системы настройщика сети я уже давненько использую NetworkManager из-за его простоты для пользователя. (Да-да-да, я знаю: NM-это не айс, он содержит массу багов и вообще ужасен, но для простой задачи поднятия сети - вполне сойдёт)

Что нам известно о NM? в случае наличия в /etc/NetworkManager/NetworkManager.conf в секции [main] параметра dns=dnsmasq, NM в файле /etc/resolv.conf прописывает nameserver 127.0.0.1 - это нам подходит, но вместе с тем пытается запустить dnsmasq, а вот это нам не нужно (учитывая, что у меня на целевой системе установлена Slackware, а там по-умолчанию ставится в систему dmsmasq). Можно, конечно, его грохать, но вроде бы NM этот момент отслеживает и пытается его запустить повторно. Вариант номер два: убрать или закоментировать dns=dnsmasq, в этом случае NM в /etc/resolv.conf пишет DNS-серверы, переданные dhcp-клиентом (либо то, что записано в настройках конкретной сети) и (самое важное) не пытается запустить dnsmasq - это нам подходит больше. Чтобы NM не смог ничего поделать с файлом /etc/resolv.conf, на него нужно применить расширенный атрибут фс под названием immutable:

chattr +i /etc/resolv.conf

Между делом следует отметить, что некоторые дистрибутивы используют пэкэдж resolvconf, который подменяет /etc/resolv.conf на симлик, котоый указывает на /var/run/NetworkManager/resolv.conf или, в более новых версиях, в /run/NetworkManager/resolv.conf. Самый неприятный нюанс заключается не в наличии ссылки как таковой, а в том, что каталог /run бывает примонтирован как tmpfs, а она как правило спец. атрибутов не поддерживает, то есть chattr тут не прокатит. Но в случае со Slackware такого не наблюдается.

Далее, нужно, чтобы NM выполнял некий хук после старта и настройки сети, в следствии которого поднимался бы PowerDNS Recursor с нужными параметрами. И такой мех-м есть у NM, для этого имеется специальный каталог:

/etc/NetworkManager/dispatcher.d

кладём туда определённый скриптец и обязательно ставим на него (на скрипт) owner root:root и права 700, иначе NM скрипты не запустит. Следует заметить, что NM при запуске скриптов выставляет переменные окружения со всеми возможными параметрами, полученными от dhcp-клиента (либо из настроек NM-коннекта).
Вот такой черновой скрипт я наваял в качестве хука, поднимающего PowerDNS:

#!/usr/bin/perl

use strict;
use warnings "all";

my $exec_str = '/usr/sbin/pdns_recursor \\
--local-address=127.0.0.1 \\
--allow-from=127.0.0.1/32 \\
--client-tcp-timeout=2 \\
--daemon \\
--entropy-source=/dev/urandom \\
--export-etc-hosts \\
--max-tcp-clients=128 \\
--max-tcp-per-client=65535 \\
--quiet=on \\
--log-common-errors=off \\
--max-cache-ttl=60 \\
--packetcache-ttl=20';

my $upstreams = '';
if (defined($ENV{'DHCP4_DOMAIN_NAME_SERVERS'})){
    my @DNS_LIST = split( / /, $ENV{'DHCP4_DOMAIN_NAME_SERVERS'});

    foreach (@DNS_LIST){
    $upstreams .= "--forward-zones=.=$_ ";
    }
}

my $domain_name;
$domain_name = $ENV{'DHCP4_DOMAIN_NAME'} or $domain_name = 'local';
my $search_domain = "--export-etc-hosts-search-suffix='$domain_name'";

my $resolver_pid = `/sbin/pidof pdns_recursor`;
`kill -9 $resolver_pid` if ($? == 0);
`$exec_str $upstreams $search_domain`;
exit 0;

Следует отметить, что по-умолчанию в Slackware нет пэкэджа PowerDNS, зато он есть в одном из официальных комьюнити реп:

<a href="http://slackbuilds.org/repository/14.1/network/pdns-recursor/?search=powerdns">http://slackbuilds.org/repository/14.1/network/pdns-recursor/?search=powerdns</a>

Всё!

Next Post