Разборы

Сайт открывается 8 секунд, а сервер простаивает: виноват DNS

Если сайт открывается за 8-9 секунд, а сервер при этом почти не нагружен, проверьте DNS сервера. В случае, который я разбирал, виноват был не шаблон и не хостинг: сервер по 8 секунд ждал ответа от DNS-резолвера, который не работал. После правки двух файлов главная стала открываться за полсекунды.

Как это выглядело

Страницы отдавались за 8,6 секунды. Нагрузка на сервер около единицы, память свободна, база данных отвечает за миллисекунды. Сайт ничего не считал, он ждал.

Второй признак я заметил не сразу: время ответа было одинаковым. Три замера подряд дали 8,6, 8,5 и 8,6 секунды. Тяжелый шаблон или холодный кеш так себя не ведут, у них всегда разброс. Ровные 8 секунд похожи на таймаут.

Почему сломанный DNS не дает ошибку

Нерабочий DNS-сервер не отказывает сразу, он молчит. Система ждет ответа 5 секунд, потом повторяет запрос и только после этого получает ответ от другого сервера из списка. Одно разрешение имени легко стоит 8 секунд.

Платит за это каждый запрос сайта к чужому серверу по имени: облачное хранилище картинок, CRM, платежный шлюз, почта, проверка лицензии. Если CMS обращается к такому серверу при сборке страницы, каждый посетитель ждет те же 8 секунд.

Коварство в том, что ответ в итоге приходит. Ничего не падает, в логах чисто, сайт просто "всегда был медленный". Виноватым назначают шаблон или хостинг и могут месяцами оптимизировать не то.

Диагностика за одну команду

curl умеет показывать, сколько времени ушло на каждый этап запроса:

curl -s -o /dev/null -w "dns=%{time_namelookup} conn=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" https://адрес-внешнего-сервиса/

Запускать нужно с самого сервера сайта, а адрес брать того сервиса, к которому сайт обращается: облачного хранилища, CRM. Значения накопительные, смотреть надо на разницу между соседними. В моем случае вышло так:

dns=8.401  conn=8.409  tls=8.434  ttfb=8.536  total=8.581

Восемь секунд ушло на первый этап, разрешение имени. Дальше этапы занимали по 10-30 миллисекунд, то есть сеть в порядке.

Проверить каждый DNS-сервер отдельно

for s in 127.0.0.53 1.1.1.1 77.88.8.8 8.8.8.8; do
  echo "--- $s"
  ( time dig +short +tries=1 +time=3 A example.com @$s ) 2>&1 | tail -4
done

Без +tries=1 проблему не видно. dig сам сделает повтор, получит ответ и покажет "5 секунд", что выглядит как "медленно, но работает". Из-за этого такая неисправность и живет на серверах месяцами.

Рабочий резолвер отвечает за 20-40 миллисекунд. В разобранном случае серверы Google 8.8.8.8 и 8.8.4.4 с этой машины не отвечали вообще, Яндекс 77.88.8.8 отвечал за 23 мс, Cloudflare 1.1.1.1 за 36 мс.

Стоит проверить и getent hosts имя-сервиса. PHP и curl разрешают имена именно так, через системную библиотеку, а dig ходит в обход и может показать другую картину.

Откуда сервер берет адреса DNS

На современных Ubuntu и Debian работает systemd-resolved, и адреса задаются в нескольких местах:

ГдеЧто задает
/etc/systemd/resolved.conf, строка DNS=общие серверы
настройки сетевого интерфейса (DHCP, netplan)серверы для интерфейса, у них приоритет выше общих
/etc/resolv.confто, что реально читают программы

Посмотреть все сразу:

resolvectl status
ls -l /etc/resolv.conf

В выводе resolvectl status строка Current DNS Server показывает, к кому система обращается прямо сейчас. Там вполне может оказаться IPv6-адрес, до которого у сервера нет маршрута.

Исправление

Правка нужна в двух местах: одна действует сразу, вторая сохраняет настройку после перезагрузки.

# сохранить исходное состояние
ls -l /etc/resolv.conf > /root/resolv.conf.was.txt
cp /etc/systemd/resolved.conf /root/resolved.conf.backup

# 1. то, что читают программы
printf 'nameserver 77.88.8.8\nnameserver 1.1.1.1\nnameserver 77.88.8.1\noptions timeout:2 attempts:2\n' > /etc/resolv.conf.new
mv /etc/resolv.conf.new /etc/resolv.conf

# 2. чтобы пережило перезагрузку
sed -i 's/^#\?DNS=.*/DNS=77.88.8.8 77.88.8.1 1.1.1.1/' /etc/systemd/resolved.conf
systemctl restart systemd-resolved

timeout:2 attempts:2 вместо стандартных пяти секунд нужен на будущее. Если резолвер снова сломается, сайт потеряет две секунды, а не восемь.

Новый файл записывается рядом и переименовывается одной командой mv, чтобы сайт не поймал момент, когда файл пустой.

Результат

ЗамерБылоСтало
Разрешение имени8,4 с0,012 с
Запрос к облачному хранилищу8,58 с0,12 с
Главная страница8,6 с0,56 с
Каталог9,3 с1,42 с
Фоновая обработка одного файла16,5 с0,49 с

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

Что проверить после

  • Панель хостинга или cloud-init при перезагрузке могут вернуть старый /etc/resolv.conf. После каждой перезагрузки смотреть ls -l /etc/resolv.conf: сайт снова замедлится молча, без единой ошибки.
  • Если на сервере есть сервисы, которые работают только по IPv6, им нужен рабочий IPv6-резолвер. Убирая IPv6-адреса, убедитесь, что они не нужны.
  • Все прежние замеры скорости недействительны. Отчеты PageSpeed и аудиторов снимались, когда сервер ждал по 8 секунд. Замеряйте заново.

Если ваш сайт тоже стабильно медленный, начните с time_namelookup, а не с оптимизации шаблона. Если не хочется разбираться самому, напишите мне: замер займет несколько минут.

Похожая проблема на вашем сайте? Напишите в Telegram или оставьте заявку.