11 KiB
DNS: делегирование, кэширование и ошибки
В Consul мы получали адрес из реестра сервисов. Теперь рассмотрим DNS: как найти сервер, который знает нужную запись, и почему разные клиенты могут получать разные адреса одного имени.
Авторитетный сервер хранит записи обслуживаемой зоны. Резолвер получает ответы для клиента и может сохранять их в кэше. В опытах мы будем обращаться к обоим и сравнивать результаты.
Все команды выполняются из каталога seminar; подготовка стенда.
1. Прочитать DNS-ответ
В стенде подготовлена отдельная учебная иерархия:
| Сервер | Роль |
|---|---|
root-dns |
Обслуживает учебный корень . и делегирует зону test. |
tld-dns |
Обслуживает зону верхнего уровня (TLD) test. и делегирует lab.test. |
auth |
Хранит записи зоны lab.test. |
resolver |
Получает записи учебной зоны у auth и кэширует их |
Делегировать зону — значит указать серверы, отвечающие за неё. Учебная иерархия работает внутри Docker и не меняет публичный DNS.
docker compose up -d --wait auth resolver root-dns tld-dns
docker compose run --rm -T tools dig "@resolver" orders.lab.test A
docker compose run --rm -T tools dig "@auth" www.lab.test A +norecurse
В команде dig аргумент @сервер выбирает получателя запроса, далее идут имя и тип записи. Параметр +norecurse отключает запрос рекурсивного поиска: сервер отвечает сведениями, которыми уже располагает.
В первом ответе найдите A-запись — IPv4-адрес orders.lab.test. Во втором имя www.lab.test связано с orders.lab.test записью CNAME, за которой следует A-запись целевого имени.
При чтении вывода обращайте внимание на:
SERVER— какой сервер ответил;status— код результата;ANSWER— записи, отвечающие на запрос;- число перед
INв строке записи — TTL — сколько секунд запись ещё можно использовать из кэша.
Адреса 192.0.2.10 и 192.0.2.20 используются как учебные значения записей. Приложений по этим адресам в стенде нет.
2. Пройти цепочку делегирований
Вопрос: если корневой сервер не хранит адрес orders.lab.test, как его ответ помогает продолжить поиск?
docker compose run --rm -T tools python3 dns/delegation.py
Скрипт выводит команды и ответы: сначала трассировку dig +trace, затем отдельные запросы к корню, TLD-серверу и авторитетному серверу.
В отдельных ответах корня и TLD-сервера секция ANSWER пуста. В секции AUTHORITY находятся NS-записи — имена серверов делегированной зоны. В ADDITIONAL даны их адреса, или glue-записи, чтобы можно было обратиться к следующему серверу. Последний ответ содержит искомую A-запись.
При +trace утилита сама выполняет итеративный поиск: переходит от одного сервера к следующему по делегированиям. Обычный запрос к резолверу может целиком обслужиться из кэша. В нашем опыте resolver настроен пересылать запросы учебной зоны сразу к auth; трассировка отдельно показывает путь через корень и TLD.
Что означают флаги в ответе
aa— ответ авторитетен.ra— сервер поддерживает рекурсивные запросы.rd— клиент запросил рекурсию. Сам по себе этот флаг не означает, что сервер действительно выполнял такой поиск.
Сравнение с публичным DNS
docker compose run --rm -T tools dig wikipedia.org A
docker compose run --rm -T tools dig "@1.1.1.1" wikipedia.org A +trace +time=2 +tries=1
В конце полной трассировки должна появиться A-запись wikipedia.org. Если вывод заканчивается раньше, поиск не завершён, даже если команда вернула код 0. Публичные записи меняются, а сеть может ограничивать прямые обращения к внешним DNS-серверам. Для разбора делегирований в таком случае используйте локальную иерархию.
3. Изменить адрес и наблюдать старый TTL
Предскажите: если одновременно изменить адрес и уменьшить TTL с 60 до 5 секунд, когда изменится ответ уже заполненного кэша?
Верните исходную запись командой reset, очистите кэш командой flush и запросите адрес:
docker compose exec -T auth python3 dns/control.py reset
docker compose exec -T resolver python3 dns/control.py flush
docker compose run --rm -T tools dig "@resolver" orders.lab.test A +noall +answer
Параметры +noall +answer оставляют в выводе только записи ответа. Получим 192.0.2.10 с TTL около 60. Отсчитывая время от этого запроса, примерно через 20 секунд измените запись и сравните ответы:
docker compose exec -T auth python3 dns/control.py update --address 192.0.2.20 --ttl 5
docker compose run --rm -T tools dig "@auth" orders.lab.test A +noall +answer
docker compose run --rm -T tools dig "@resolver" orders.lab.test A +noall +answer
Авторитетный сервер уже выдаёт 192.0.2.20 с TTL 5. Резолвер продолжает возвращать 192.0.2.10 с остатком прежних 60 секунд. Уменьшение TTL на авторитетном сервере не меняет срок ранее сохранённой записи. Повторный запрос к кэшу тоже не начинает отсчёт заново.
Дождитесь истечения TTL старого ответа и повторите запрос:
docker compose run --rm -T tools dig "@resolver" orders.lab.test A +noall +answer
Теперь ожидаем 192.0.2.20 с TTL не больше 5. Если при выполнении предыдущих команд прошла минута и старый адрес уже исчез, повторите опыт с reset и flush. Для более длинного опыта можно задать reset --ttl 120.
Здесь кэш настроен без предварительного обновления и выдачи просроченных записей. После истечения TTL следующий запрос клиента заставляет резолвер получить запись заново.
Вопросы для самопроверки: когда уменьшать TTL перед переездом сервиса? Почему старый адрес нужно поддерживать некоторое время? Где ещё, кроме резолвера, могут кэшироваться адреса?
Восстановите исходную запись и очистите кэш:
docker compose exec -T auth python3 dns/control.py reset
docker compose exec -T resolver python3 dns/control.py flush
4. Различить отсутствие записи и ошибку поиска
Отсутствие адреса в ответе может означать разные вещи. Сравните четыре результата:
docker compose run --rm -T tools python3 dns/errors.py
| Запрос / условие | Результат | Что можно заключить |
|---|---|---|
missing.lab.test A |
NXDOMAIN |
Запрошенного имени нет |
orders.lab.test AAAA |
NOERROR, пустая секция ANSWER |
Имя существует, но IPv6-адреса для него нет |
name.broken.test A через резолвер |
SERVFAIL |
Резолвер не смог получить пригодный ответ |
| На запрос никто не отвечает | Тайм-аут | Ответ не получен за отведённое время |
NODATA — название ситуации, когда имя существует, но записи нужного типа нет; отдельного кода DNS с таким именем нет. В нашем примере запрос AAAA возвращает именно такой результат.
SERVFAIL в опыте возникает из-за пересылки запроса серверу, который не обслуживает нужную зону. Ни SERVFAIL, ни тайм-аут не доказывают отсутствия имени.
Вопрос: почему недавно созданное имя может некоторое время оставаться недоступным клиентам, которые запрашивали его до создания? Учтите, что ответы об отсутствии имени тоже могут кэшироваться.
Исходники: настройка серверов, изменение записи и сброс кэша, делегирование, ошибки. Справочник: BIND 9.