kai.kzLab

DNS не резолвит в Linux: Temporary failure in name resolution, dig, resolv.conf и systemd-resolved

Команда KAI Lab · · 8 мин чтения

Если в Linux не резолвится имя, разделите проблему на две части: getent hosts имя проходит тот же путь, что и приложения, — через /etc/nsswitch.conf, /etc/hosts и /etc/resolv.conf, — а dig имя спрашивает DNS-сервер напрямую. Статус в ответе dig сразу подсказывает, где искать: NXDOMAIN — такой записи нет, SERVFAIL — сервер не смог получить ответ, таймаут — до DNS-сервера не достучаться.

Приложения сообщают об этом по-разному: curl: (6) Could not resolve host, ping: api.example.com: Temporary failure in name resolution или Name or service not known. Две последние строки — тексты ошибок glibc, и разница между ними полезна. «Temporary failure» обычно значит, что сервер недоступен или вернул ошибку. «Name or service not known» — что имя не нашлось.

Как Linux превращает имя в адрес

  1. Приложение вызывает getaddrinfo() из glibc.
  2. glibc смотрит строку hosts: в /etc/nsswitch.conf: в каком порядке спрашивать источники. Обычно это files dns — сначала /etc/hosts, потом DNS.
  3. Для DNS берутся серверы из /etc/resolv.conf. На Ubuntu и многих других дистрибутивах там стоит 127.0.0.53 — локальная заглушка systemd-resolved.
  4. systemd-resolved пересылает запрос настоящим DNS-серверам, полученным от DHCP, netplan или NetworkManager, и кэширует ответ.

dig и nslookup ходят в обход шагов 1–2: они читают из /etc/resolv.conf только адрес сервера и не смотрят ни в /etc/hosts, ни в nsswitch.conf. Поэтому ситуация «dig резолвит, а приложение нет», и наоборот, — нормальная подсказка, а не мистика.

Симптом → команда → что смотреть

СимптомКомандаЧто смотреть
Приложение не видит имяgetent hosts имячто видит система с учётом /etc/hosts и nsswitch
Нужен ответ DNS-сервераdig имяstatus: — NOERROR, NXDOMAIN или SERVFAIL; секция ANSWER и TTL
Подозрение на свой DNS-серверdig @1.1.1.1 имясовпадает ли ответ публичного резолвера с локальным
Запросы висят и падают по таймаутуdig имяcommunications error ... timed out: сервер из resolv.conf недоступен
Непонятно, какие серверы используютсяresolvectl statusCurrent DNS Server, DNS Servers, DNS Domain
dig и getent отвечают по-разномуgrep имя /etc/hostsзабытую статическую запись
После смены записи приходит старый IPdig +noall +answer имясколько секунд TTL осталось у ответа в кэше
Короткое имя без домена не находитсяcat /etc/resolv.confстроки search и options ndots

Шаг 1. Что видит система: getent

$ getent hosts api.example.com
10.0.0.15       api.example.com
$ dig +short api.example.com
203.0.113.10

Система и DNS отвечают по-разному. Раз dig получает 203.0.113.10, а getent — 10.0.0.15, адрес берётся не из DNS. Первый подозреваемый — /etc/hosts:

$ grep api.example.com /etc/hosts
10.0.0.15   api.example.com
$ grep ^hosts /etc/nsswitch.conf
hosts:          files dns

При files dns запись в /etc/hosts побеждает DNS. Такие строки остаются после переездов и отладки: кто-то когда-то прописал адрес руками, сервис переехал, а запись осталась. Если getent ничего не вернул, код выхода будет 2 — имя не найдено ни в одном источнике, и пора идти к DNS.

Шаг 2. Что отвечает DNS: dig

Главное в выводе dig — строка со статусом:

$ dig api.example.com | grep status
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 30412
$ dig @1.1.1.1 api.example.com | grep status
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1734

Что означают статусы:

  • NOERROR — ответ есть. Если секция ANSWER при этом пустая, имя существует, но записи нужного типа нет: например, есть A, а спрашивали AAAA.
  • NXDOMAIN — такого имени нет. Проверьте опечатку, домен поиска и то, в той ли зоне вы ищете: внутреннее имя публичный резолвер не знает.
  • SERVFAIL — резолвер не смог получить ответ: недоступны авторитетные серверы зоны, сломан DNSSEC или сам резолвер. Если публичный резолвер отвечает, а ваш — нет, как в примере выше, проблема в вашем резолвере или на пути к нему.
  • REFUSED — сервер отказался отвечать: вас нет в его списке разрешённых клиентов.

Если сервер не отвечает вовсе, dig покажет:

$ dig @10.0.0.2 api.example.com
;; communications error to 10.0.0.2#53: timed out
;; communications error to 10.0.0.2#53: timed out
;; communications error to 10.0.0.2#53: timed out

; <<>> DiG 9.18.28-0ubuntu0.24.04.1-Ubuntu <<>> @10.0.0.2 api.example.com
; (1 server found)
;; global options: +cmd
;; no servers could be reached

Это уже сетевая проблема: сервер выключен, не слушает порт 53 или трафик режет firewall. Когда проблема в зоне у провайдера, dig +trace имя пройдёт цепочку делегирования от корневых серверов и покажет, на каком шаге она рвётся.

nslookup делает похожую работу. Сервер для проверки указывается вторым аргументом: nslookup api.example.com 1.1.1.1, а отсутствие имени выглядит как ** server can't find ...: NXDOMAIN.

Шаг 3. Какие серверы на самом деле используются

$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Aug 12 10:02 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
$ grep -v '^#' /etc/resolv.conf
nameserver 127.0.0.53
options edns0 trust-ad
search corp.example

Если /etc/resolv.conf — ссылка на stub-resolv.conf, всё идёт через systemd-resolved, а настоящие серверы надо смотреть у него:

$ resolvectl status
Global
         Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
  resolv.conf mode: stub

Link 2 (eth0)
    Current Scopes: DNS
         Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 10.0.0.2
       DNS Servers: 10.0.0.2
        DNS Domain: corp.example

Править такой /etc/resolv.conf руками бесполезно: его перезапишут. Серверы меняют там, откуда они приходят, — в netplan, NetworkManager, DHCP или resolved.conf.

Если systemd-resolved не используется и в /etc/resolv.conf перечислены серверы напрямую, помните, как работает резолвер glibc. Он опрашивает серверы по порядку, ждёт ответа 5 секунд и делает 2 попытки. Если первый сервер в списке мёртв, каждое обращение к DNS тормозит на секунды, хотя второй сервер жив. Серверов в списке учитывается не больше трёх.

Шаг 4. Короткие имена: search и ndots

Строка search corp.example позволяет писать db вместо db.corp.example: резолвер сам допишет домен. Правило задаёт ndots: если в имени точек меньше этого числа (по умолчанию 1), сначала пробуются домены поиска.

Отсюда две ловушки. Первая — короткое имя не находится на машине, где search другой или пустой. Вторая — лишние запросы: в подах Kubernetes по умолчанию ndots:5 и несколько доменов поиска, поэтому обращение к внешнему api.example.com сначала перебирает кластерные домены. Если в конце имени поставить точку — api.example.com., — поиск не выполняется.

Шаг 5. TTL и кэш: почему старый адрес

Каждый ответ DNS живёт в кэше столько секунд, сколько указано в TTL. Сменили запись у DNS-провайдера — клиенты увидят новый адрес, только когда у них истечёт старый ответ:

$ dig +noall +answer api.example.com
api.example.com.	74018	IN	A	198.51.100.20
$ dig +noall +answer @ns1.example.com api.example.com
api.example.com.	86400	IN	A	203.0.113.10

Авторитетный сервер уже отдаёт новый адрес, а резолвер помнит старый ещё 74 018 секунд — больше двадцати часов. Потому что TTL был сутки. Правило для переездов: снизить TTL до нескольких минут заранее, хотя бы за один старый TTL до смены записи.

Кэшей по дороге несколько, и сбросить можно только свой:

  • sudo resolvectl flush-caches чистит кэш systemd-resolved на этой машине;
  • кэш корпоративного или провайдерского резолвера вы не сбросите — ждите TTL;
  • приложения тоже кэшируют: JVM держит адреса у себя, а nginx резолвит имена из proxy_pass при запуске и перечитывании конфигурации.

Отрицательные ответы тоже кэшируются. Если запросили имя до того, как запись создали, NXDOMAIN какое-то время будет возвращаться и после создания — срок задаёт SOA-запись зоны.

Что сломалось чаще всего

  • Забытая строка в /etc/hosts — выдаёт себя разницей между getent и dig.
  • Недоступный или мёртвый DNS-сервер в настройках сети — таймауты в dig, долгие зависания у приложений.
  • Внутреннее имя спрашивают у публичного резолвера, например после ручной правки настроек на 8.8.8.8, — NXDOMAIN.
  • Запись поменяли, а TTL большой — у одних клиентов новый адрес, у других старый.
  • В контейнере свой resolv.conf: у Docker в пользовательских сетях встроенный DNS-сервер 127.0.0.11, у подов Kubernetes — адрес кластерного DNS.

Когда имя резолвится верно, а подключиться всё равно не получается, дальше по пути запроса — порт и сертификат: разборы порт уже занят и истёк SSL-сертификат. А вопрос «что происходит, когда вы набираете адрес в браузере» почти всегда звучит на собеседовании по Linux — эта статья покрывает его DNS-часть.

В первом месяце симулятора DevOps KAI Lab есть тикет «Объясни продакту, как работает сайт»: с него удобно начать, если путь запроса от имени до ответа сервера пока не сложился в голове. Дальше те же шаги встречаются в тикетах и на дежурствах, а консоль отвечает реальными выводами команд.

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

Почему dig резолвит, а приложение — нет? dig спрашивает DNS-сервер напрямую и не смотрит в /etc/hosts и nsswitch.conf. Приложения идут через glibc и эти файлы. Сравните с getent hosts имя: если ответы разные, причина в /etc/hosts или в порядке источников в nsswitch.conf.

Чем NXDOMAIN отличается от SERVFAIL? NXDOMAIN — сервер уверен, что такого имени нет. SERVFAIL — сервер не смог получить ответ: недоступны авторитетные серверы, ошибка DNSSEC или проблема самого резолвера.

Как сбросить DNS-кэш в Linux? При systemd-resolved — sudo resolvectl flush-caches. Это чистит только локальный кэш: вышестоящие резолверы будут отдавать старый ответ, пока не истечёт TTL.

Можно ли редактировать /etc/resolv.conf? Если это ссылка на файл systemd-resolved или файл, которым управляет NetworkManager, — нет, изменения перезапишут. Меняйте настройки там, откуда приходят серверы: в netplan, NetworkManager, DHCP или resolved.conf.

Что значит Temporary failure in name resolution? Это текст ошибки glibc, когда DNS не дал окончательного ответа: сервер недоступен, ответил SERVFAIL или запрос истёк по таймауту. Проверьте dig имя и resolvectl status.

Источники

Читать дальше