Разрешение имён DNS
Разрешение имён следует строке hosts: в nsswitch — сначала files, потом dns. /etc/hosts проверяется первым; /etc/resolv.conf (или systemd-resolved на 127.0.0.53) указывает DNS-сервер. getent следует nsswitch; dig обходит его и говорит с сервером напрямую.
dig google.com разрешается нормально, но curl https://api.internal падает с «Name or service not known». Или наоборот: запись в /etc/hosts игнорируется. Эти сбои почти всегда связаны с незнанием полного пути разрешения имён: ядро не обращается к DNS напрямую — оно спрашивает библиотеку C (glibc), которая следует файлу политик (nsswitch.conf), который может говорить «сначала проверь /etc/hosts, потом DNS». А «DNS» на современном Ubuntu означает локальный заглушку на 127.0.0.53, запущенную systemd-resolved, а не сервер из /etc/resolv.conf напрямую. Зная, где живёт каждый элемент и в каком порядке срабатывает, «имя не найдено» превращается в двухминутную диагностику.
После этого урока ты сможешь проследить полный путь разрешения имён на Linux-хосте, читать /etc/nsswitch.conf для понимания порядка разрешения, проверять и переопределять /etc/resolv.conf, понимать роль systemd-resolved как заглушки на 127.0.0.53, использовать getent hosts для тестирования разрешения так, как видит его приложение, и использовать dig для тестирования DNS напрямую.
Путь разрешения начинается с nsswitch.conf — не с DNS.
/etc/nsswitch.conf — файл политик, который сообщает glibc, какие источники использовать для разрешения имён и в каком порядке.
grep ^hosts /etc/nsswitch.conf
# hosts: files dns
# Значение:
# 1. files → проверить /etc/hosts первым
# 2. dns → затем запросить DNS (через /etc/resolv.conf или systemd-resolved)
# На Ubuntu 22.04+ с systemd-resolved:
# hosts: files mdns4_minimal [NOTFOUND=return] dns
# mdns4_minimal обрабатывает .local-имена (Avahi/mDNS)
# [NOTFOUND=return] означает: если mdns говорит "не найдено" — остановиться,
# НЕ переходить к dns. Вот почему *.local-имена никогда не достигают DNS-сервера.Приложение вызывает getaddrinfo("api.internal"). glibc читает nsswitch.conf и поочерёдно вызывает каждый источник, пока один не вернёт результат или авторитетное «не найдено».
/etc/hosts — первый источник в цепочке.
/etc/hosts — статический файл, проверяемый до любого сетевого запроса (когда files стоит первым в nsswitch.conf). Каждая строка сопоставляет IP одному или нескольким именам.
cat /etc/hosts
# 127.0.0.1 localhost
# 127.0.1.1 host-a
# ::1 localhost ip6-localhost ip6-loopback
# 10.0.0.20 db-primary db-primary.internal
# Добавить временное переопределение (полезно до распространения DNS):
echo '10.0.0.100 staging-api.internal' | sudo tee -a /etc/hosts
# Проверить немедленно (getent следует nsswitch, /etc/hosts побеждает):
getent hosts staging-api.internal
# 10.0.0.100 staging-api.internal
# Удалить запись когда закончишь — /etc/hosts не DNS,
# устаревшие записи сбивают с толку спустя месяцы/etc/hosts — самое быстрое возможное переопределение имени. ОС читает его при каждом поиске — никакого кэша для сброса. Риск: записи накапливаются и конфликтуют с реальными DNS-записями спустя месяцы.
/etc/resolv.conf — какой DNS-сервер использовать.
Когда срабатывает источник dns, glibc читает /etc/resolv.conf для нахождения адреса сервера имён.
cat /etc/resolv.conf
# nameserver 127.0.0.53 ← заглушка systemd-resolved (Ubuntu по умолчанию)
# options edns0 trust-ad
# search internal.example.com ← добавляется к неквалифицированным именам
# Или на Debian без systemd-resolved:
# nameserver 10.0.0.1 ← роутер/DNS-ретранслятор
# Директива search:
# При запросе "db-primary" (без точек) резолвер добавляет домен поиска:
# → сначала пробует "db-primary.internal.example.com"
# Если не находит — пробует "db-primary" как есть
# Проверить используемый резолвер:
resolvectl status # на системах с systemd-resolved
# или
cat /run/systemd/resolve/resolv.conf # реальная конфигурация upstreamРаспространённая ловушка: /etc/resolv.conf является симлинком (lrwxrwxrwx /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf) на Ubuntu. Если инструмент вроде dhclient или openvpn перезапишет его обычным файлом, ты обходишь systemd-resolved и теряешь кэширование и функции split-DNS, не замечая этого.
systemd-resolved: заглушка на 127.0.0.53.
На Ubuntu 18.04+ и Debian 12+ systemd-resolved запускает локальную DNS-заглушку на 127.0.0.53:53. Все DNS-запросы от glibc идут туда; resolved обрабатывает кэширование, DNSSEC и маршрутизацию split-DNS к разным upstream по интерфейсу.
# Проверить статус:
systemctl status systemd-resolved
# Запросить заглушку напрямую:
dig @127.0.0.53 example.com
# Посмотреть upstream по интерфейсу:
resolvectl status
# Global
# DNS Servers: 10.0.0.1
# Link 2 (ens3)
# Current DNS Server: 10.0.0.1
# DNS Servers: 10.0.0.1
# DNS Domain: internal.example.com
# Сбросить кэш resolved (полезно при смене DNS-записи):
sudo resolvectl flush-caches
# Проверить разрешение имени и его источник:
resolvectl query db-primary.internal
# db-primary.internal: 10.0.0.20
# -- Information acquired via protocol DNS ...Resolved — не просто прокси; он ведёт таблицу маршрутизации DNS по интерфейсам. VPN-туннели могут подключать DNS-серверы для .vpn.internal без замены глобального резолвера — поэтому resolvectl status показывает разные DNS для разных интерфейсов.
getent hosts vs dig: ключевое различие.
# getent hosts следует полной цепочке nsswitch — тот же путь, что у приложения
getent hosts db-primary
# 10.0.0.20 db-primary
# dig говорит напрямую с сервером имён — обходит /etc/hosts и nsswitch полностью
dig db-primary
# ;; ANSWER SECTION:
# db-primary. 0 IN A 10.0.0.20
# (или NXDOMAIN, если имя только в /etc/hosts — dig его не найдёт)
# Практическое правило:
# getent hosts — проверить то, что увидит приложение
# dig — проверить чистый DNS (обходя локальные переопределения)
# dig с явным сервером:
dig @127.0.0.53 db-primary.internal # тест заглушки systemd-resolved
dig @10.0.0.1 db-primary.internal # тест upstream напрямую
# Тип запроса (по умолчанию A/AAAA):
dig MX example.com
dig TXT example.comКлассическое расхождение: dig api.internal возвращает NXDOMAIN, но приложение работает, потому что имя в /etc/hosts. Или наоборот: dig api.internal возвращает IP, но приложение получает NXDOMAIN из-за устаревшей записи в /etc/hosts, которая перекрывает DNS.
Диагностика «Name or service not known» для внутреннего хоста.
Приложение на host-a (10.0.0.10) не может достичь db-primary.internal. Ошибка: getaddrinfo: Name or service not known.
# Шаг 1: разрешает ли getent? (тестирует полный путь nsswitch)
getent hosts db-primary.internal
# (пусто — не найдено ни через files, ни через dns)
# Шаг 2: есть ли в /etc/hosts?
grep db-primary /etc/hosts
# (пусто)
# Шаг 3: разрешает ли dig? (чистый тест DNS)
dig @127.0.0.53 db-primary.internal
# ;; ANSWER SECTION:
# db-primary.internal. 60 IN A 10.0.0.20
# DNS знает это имя
# Шаг 4: но getent упал — почему? Проверить nsswitch
grep ^hosts /etc/nsswitch.conf
# hosts: files mdns4_minimal [NOTFOUND=return] dns
# mdns4_minimal с [NOTFOUND=return] блокирует .internal до dns
# mdns4_minimal говорит NOTFOUND, [NOTFOUND=return] останавливает цепочку
# dns никогда не достигается
# Исправление: добавить запись в /etc/hosts как временный обход:
echo '10.0.0.20 db-primary.internal' | sudo tee -a /etc/hosts
getent hosts db-primary.internal
# 10.0.0.20 db-primary.internal — теперь работает▸Частая ошибка
/etc/resolv.conf перезаписывается незаметно. Инструменты вроде dhclient, openvpn, resolvconf и NetworkManager хотят управлять /etc/resolv.conf. На системе, где он должен быть симлинком на заглушку systemd-resolved, любой из них может заменить симлинк обычным файлом с другим сервером имён — и ты теряешь кэширование, DNSSEC и split-DNS без сообщений об ошибках. Проверь через ls -la /etc/resolv.conf: если это обычный файл вместо симлинка — что-то его заменило. Восстановить: sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf.
Ты запускаешь dig api.internal и получаешь корректную A-запись. Затем запускаешь getent hosts api.internal и не получаешь ничего. Каково наиболее вероятное объяснение?
Разрешение имён на Linux — не только DNS. getaddrinfo() следует строке hosts: в nsswitch.conf, проверяя files (/etc/hosts) до dns. DNS на современном Ubuntu/Debian означает заглушку systemd-resolved на 127.0.0.53, которая кэширует, проверяет DNSSEC и маршрутизирует запросы к upstream по интерфейсу. /etc/resolv.conf указывает glibc адрес DNS-резолвера — на Ubuntu это симлинк на заглушку; перезапись его обычным файлом ломает resolved незаметно. getent hosts тестирует полную цепочку разрешения так, как видит её приложение. dig обходит nsswitch полностью и говорит напрямую с сервером — используй для чистого теста DNS. Ловушка [NOTFOUND=return] в nsswitch — самая частая причина того, что DNS работает, но getent/приложение — нет.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.