MAATRIX / Блог / Кеш отрицательных ответов держал сайт мёртвым ещё час после починки

Кеш отрицательных ответов держал сайт мёртвым ещё час после починки

MAATRIX

Сайт починили за пять минут, а жалобы шли ещё час — причём не от всех, а выборочно, по регионам и провайдерам. Это один из самых обманчивых сценариев в DNS: проблема на вашей стороне уже решена, дежурный видит зелёные метрики, а часть аудитории продолжает получать NXDOMAIN. Разбираем, почему кеш отрицательных ответов живёт своей жизнью и как не наступать на эти грабли повторно.

Что сломалось: сайт «лежит» не для всех

Ночью прошёл плановый деплой с обновлением DNS-записи — IP основного сервера поменялся после переезда на новую ноду. Скрипт автоматизации обновил A-запись через API авторитативного DNS-сервера. Через несколько минут в чат посыпались сообщения: у части пользователей сайт не открывается, браузер показывает DNS_PROBE_FINISHED_NXDOMAIN, у части — открывается как обычно.

Первая реакция дежурного — паника пополам с недоумением: сервер отвечает, curl по IP работает, TLS-сертификат валиден, nginx отдаёт 200. При этом мониторинг из одних регионов зелёный, из других — красный, причём не географически логично (не «вся Азия лежит», а вперемешку — то один провайдер, то другой).

Разница между «сервер не работает» и «часть резолверов считает, что домена не существует» на старте была не очевидна. Именно эта путаница и стала центром расследования.

Что показывали логи и метрики

Первым делом сверили таймлайн: когда именно скрипт обновил A-запись и когда пошли первые жалобы.

# лог деплой-скрипта
02:14:07  delete rrset A example.com -> old_ip
02:14:08  create rrset A example.com -> new_ip
02:14:41  первая жалоба в поддержку: сайт не открывается

Между delete и create прошла примерно секунда — казалось бы, мелочь. Но за эту секунду зона на авторитативном сервере успела ответить NXDOMAIN на пришедшие в этот момент запросы. Проверили сразу несколько точек:

# запрос напрямую к авторитативному NS — всё чисто, отдаёт новый IP
dig @ns1.example.com example.com A +short

# запрос через публичный резолвер, с которого жаловался пользователь
dig @resolver.ispcompany.example example.com A +short
;; ANSWER: (пусто)
;; AUTHORITY SECTION: example.com. 3600 IN SOA ns1.example.com. ...

Второй запрос возвращал пустой ANSWER и SOA-запись в AUTHORITY — классический признак закешированного отрицательного ответа (NXDOMAIN или NODATA), который резолвер отдаёт из своего кеша, даже не обращаясь повторно к авторитативному серверу.

Дальше подняли логи мониторинга по регионам: там, где зонды использовали резолверы, успевшие поймать окно с ошибкой, статус был DOWN до момента, пока их локальный кеш не истёк. Там, где зонды использовали резолверы, которые не успели попасть в это окно (запрос пришёл чуть раньше или чуть позже), всё было UP с самого начала. Это и объясняло «пятнистую» картину — не сбой инфраструктуры целиком, а результат того, кто именно и когда успел получить пустой ответ.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Гипотезы, которые отбросили

По ходу расследования проверили и отбросили несколько версий, каждая — с конкретным аргументом:

  • Хостинг лежит целиком. Опровергли: другие домены на том же сервере и по тому же IP открывались нормально, статус-страница хостера зелёная, ping и curl по IP работали без нареканий.
  • Сертификат протух. Проверили сроком действия и мониторингом сертификатов — до истечения оставалось больше 60 дней, ошибок TLS в логах не было.
  • Фаервол или гео-блокировка режут трафик по региону. Не подтвердилось: в access-логах nginx не было даже попыток подключения с проблемных IP в момент жалоб — запросы не доходили до сервера вообще, а значит, обрывались раньше, на уровне резолвинга имени, а не на уровне сети или приложения.
  • DDoS или аномальный всплеск трафика. WAF и логи запросов чистые, никакого всплеска нагрузки не зафиксировано ни до, ни во время инцидента.
  • Классическая «DNS ещё не распространился, подождите 24–48 часов». Эта гипотеза не подтвердилась по двум причинам: TTL на самой A-записи был выставлен в 300 секунд заранее, и проблема не тянулась равномерно у всех — она резко прекращалась у конкретных пользователей ровно тогда, когда истекал TTL именно отрицательного кеша, а не позитивной записи.

Последний пункт — ключевой. Миф про «сутки на распространение DNS» почти всегда не про то. Обычно дело не в TTL записи, которую вы поменяли, а в TTL совершенно другого механизма — кеша отрицательных ответов.

Настоящая причина: слишком большой TTL для отрицательного кеша

Корень проблемы — не в том, что запись обновилась «неправильно», а в том, что на короткое окно между delete и create зона ответила «такой записи нет», и это «нет» закешировалось резолверами на срок, никак не связанный с TTL самой A-записи.

Разберём по шагам:

  1. Скрипт деплоя обновлял rrset не атомарно: сначала DELETE, затем CREATE двумя отдельными вызовами API. Между ними — окно в доли секунды, но его достаточно, чтобы часть резолверов успела получить пустой ответ.
  2. Резолвер, получивший пустой ответ (NXDOMAIN или NODATA), закешировал его согласно правилам отрицательного кеширования (RFC 2308), а не согласно TTL позитивной A-записи.
  3. TTL отрицательного кеша определяется не тем, что вы выставили на записи example.com. 300 IN A ..., а минимумом между TTL самой SOA-записи и полем MINIMUM в SOA — либо собственным потолком (max negative TTL), который резолвер применяет независимо от зоны.
  4. У части резолверов этот потолок оказался равен 3600 секундам — то есть ровно час. Отсюда и «мёртвым ещё час после починки»: не потому что чинили медленно, а потому что уже закешированное «домена нет» жило ровно столько, сколько был выставлен этот параметр на стороне резолвера.
example.com.  IN  SOA  ns1.example.com. hostmaster.example.com. (
                  2026082901 ; serial
                  7200       ; refresh
                  3600       ; retry
                  1209600    ; expire
                  3600 )     ; minimum <- именно это поле влияет на TTL отрицательного кеша

Важный нюанс: поле MINIMUM в SOA исторически (до RFC 2308) трактовалось как TTL по умолчанию для всей зоны. После RFC 2308 оно означает именно TTL отрицательного кеширования — то, на какой срок резолвер вправе запомнить «такой записи нет» или «такого домена нет». Это разные вещи, и путаница между ними — частая причина, почему администратор трогает TTL записи, а проблема с «зависшей недоступностью» не уходит.

Как устроено отрицательное кеширование DNS и почему оно кусается

Отрицательное кеширование — полезный механизм: без него каждый опечатавшийся в адресе пользователь генерировал бы лишний трафик на авторитативные серверы при каждом повторном запросе. Но у механизма есть обратная сторона — он одинаково хорошо кеширует и «домена никогда не было», и «домен на секунду пропал из-за гонки в скрипте деплоя».

Несколько практических фактов, которые стоит держать в голове:

  • TTL отрицательного ответа — это не TTL вашей записи. Он берётся из SOA-записи зоны (поле MINIMUM) и потолка, который настроен на самом резолвере.
  • У популярных резолверов есть собственный потолок (max negative TTL). Например, у PowerDNS Recursor и у Unbound по умолчанию это значение исторически равно 3600 секундам (один час) — то есть даже если в SOA стоит больше, резолвер всё равно не будет кешировать отрицательный ответ дольше своего потолка. Значения по умолчанию отличаются между версиями и дистрибутивами, поэтому для конкретного резолвера в проде их стоит сверять с актуальной документацией, а не полагаться на память.
  • Флаш кеша работает только там, где вы администратор резолвера. Если у вас свой рекурсивный резолвер (например, PowerDNS Recursor или Unbound перед вашими же серверами), можно принудительно сбросить запись из кеша. На стороне пользователей, у которых свой ISP-резолвер или корпоративный DNS, вы это сделать не можете — только ждать истечения TTL.
  • Инструмент диагностики — не «подождать», а dig с явным указанием SOA и AUTHORITY-секции. Пустой ANSWER с SOA-записью в AUTHORITY — верный признак, что перед вами именно закешированный отрицательный ответ, а не реальная проблема с записью на авторитативном сервере.
# сброс конкретной записи из кеша своего резолвера
# PowerDNS Recursor:
rec_control wipe-cache example.com

# Unbound:
unbound-control flush_negative example.com

# BIND9 (в некоторых сборках):
rndc flushname example.com

Эти команды помогают только для инфраструктуры, которую вы контролируете. Для резолверов сторонних провайдеров единственный рабочий сценарий — минимизировать вероятность самого инцидента и заранее закладывать короткий TTL отрицательного кеша перед плановыми изменениями.

Что изменили после инцидента

По итогам разбора внесли три группы изменений — в автоматизацию, в конфигурацию DNS и в мониторинг.

В автоматизации деплоя. Убрали пару DELETE + CREATE и заменили на одну атомарную операцию замены rrset через PATCH-запрос к API DNS-провайдера, где смена значения A-записи происходит одним вызовом без промежуточного состояния «записи нет вообще»:

curl -X PATCH https://dns-api.example.com/api/v1/servers/localhost/zones/example.com. \
  -H "X-API-Key: $DNS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rrsets": [{
      "name": "example.com.",
      "type": "A",
      "ttl": 300,
      "changetype": "REPLACE",
      "records": [{"content": "203.0.113.10", "disabled": false}]
    }]
  }'

Замена rrset одним вызовом убирает то самое окно в доли секунды, когда авторитативный сервер мог ответить «записи нет».

В конфигурации зоны. Пересмотрели значение MINIMUM в SOA — снизили с 3600 до 300 секунд на постоянной основе, а перед плановыми изменениями (переезд, смена IP, миграция) стали заранее, за сутки, временно опускать его ещё ниже — до 60–120 секунд — и возвращать обратно после того, как изменения подтверждены и стабилизировались.

Сравнение подходов к TTL отрицательного кеша:

СценарийMINIMUM в SOAРиск при сбоеКогда уместно
Обычный режим, без изменений в ближайшее время300–900 сНизкий, быстрый цикл переопросаПостоянная работа зоны
Заранее перед плановой миграцией60–120 сМинимальный, но чуть больше нагрузка на NSЗа 24 ч до и несколько часов после смены записи
Значение «по умолчанию из коробки», никто не трогал3600 с и большеВысокий — любая гонка при обновлении «застревает» на час и дольшеНежелательно как постоянная настройка

В мониторинге. Добавили отдельную проверку, которая опрашивает домен не только с точки зрения самого сервера, но и через несколько независимых публичных резолверов (например, 8.8.8.8, 1.1.1.1, 9.9.9.9) и сверяет ответ с тем, что отдаёт авторитативный NS напрямую. Если авторитативный сервер отвечает корректно, а один из публичных резолверов возвращает NXDOMAIN — это отдельный алерт с пометкой «вероятно, кеш отрицательных ответов», а не общий «сайт недоступен», чтобы дежурный сразу понимал направление диагностики и не тратил время на проверку сервера, который на самом деле в порядке.

#!/usr/bin/env bash
# упрощённый пример проверки расхождения между авторитативным ответом и публичными резолверами
DOMAIN="example.com"
AUTH_NS="ns1.example.com"
RESOLVERS=("8.8.8.8" "1.1.1.1" "9.9.9.9")

auth_ip=$(dig @"$AUTH_NS" "$DOMAIN" A +short)

for r in "${RESOLVERS[@]}"; do
  ip=$(dig @"$r" "$DOMAIN" A +short)
  if [ -z "$ip" ] && [ -n "$auth_ip" ]; then
    echo "ALERT: резолвер $r не отдаёт запись, хотя авторитативный NS отвечает $auth_ip — похоже на закешированный отрицательный ответ"
  fi
done

Такую проверку удобно повесить и как отдельный шаг post-deploy checklist сразу после любой правки DNS-записей — если проблемы с DNS после переезда уже случались в вашей практике, разница между «ещё не обновилось» и «закешировалось как отсутствующее» экономит часы разбирательств. Если хочется глубже понять сам путь резолвинга и то, через кого именно проходит запрос перед тем, как попасть на авторитативный сервер, полезно освежить в памяти, как DNS-запрос обходит полмира — это упрощает чтение AUTHORITY-секции в выводе dig.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Почему у части пользователей сайт открывался сразу, а у других не открывался ещё час?

Потому что не все резолверы успели получить пустой ответ в то самое окно, когда зона на секунду отвечала «записи нет». Кому не «повезло» попасть в это окно — у тех кеш зафиксировал отрицательный ответ на срок, заданный TTL отрицательного кеша (в этом случае — около часа), а остальные резолверы просто ни разу не столкнулись с пустым ответом и продолжали отдавать актуальную запись.

Можно ли было ускорить восстановление, не дожидаясь истечения кеша?

Только там, где вы администратор резолвера — например, сбросить конкретную запись командой rec_control wipe-cache (PowerDNS Recursor) или unbound-control flush_negative (Unbound). Для резолверов конечных пользователей и корпоративных сетей сторонних провайдеров такой возможности нет — остаётся только ждать истечения TTL отрицательного кеша.

Разве низкий TTL на самой A-записи не должен был решить проблему?

Нет — это разные механизмы. TTL на A-записи управляет тем, как долго кешируется позитивный ответ («вот такой IP»). А TTL отрицательного кеша управляет тем, как долго кешируется ответ «такой записи нет», и берётся из поля MINIMUM в SOA-записи зоны либо из собственного потолка резолвера. Можно смело выставить TTL записи в 60 секунд и всё равно закешировать «домена нет» на час, если MINIMUM в SOA не тронуть.

Как понять, что именно происходит — реальная проблема с записью или закешированный отрицательный ответ?

Сравните ответ авторитативного сервера напрямую (dig @ns1.example.com example.com A) с ответом через резолвер, на который жалуются (dig @resolver-ip example.com A). Если авторитативный сервер отдаёт корректный IP, а резолвер — пустой ANSWER с SOA-записью в секции AUTHORITY, это почти всегда именно кеш отрицательных ответов, а не проблема с самой записью.

Стоит ли постоянно держать MINIMUM в SOA очень низким, например 60 секунд?

Для повседневной работы это избыточно — вырастет число повторных запросов к авторитативным серверам от резолверов, которые уже получали отрицательный ответ на несуществующие поддомены или опечатки. Разумный компромисс — держать в спокойном режиме значение в диапазоне нескольких минут, а перед плановыми изменениями DNS временно опускать его ещё ниже и возвращать обратно после стабилизации.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →