open atlas
↑ К треку
Linux: операционная система LIN · 09 · 03

Разрешение имён DNS

Разрешение имён следует строке hosts: в nsswitch — сначала files, потом dns. /etc/hosts проверяется первым; /etc/resolv.conf (или systemd-resolved на 127.0.0.53) указывает DNS-сервер. getent следует nsswitch; dig обходит его и говорит с сервером напрямую.

LIN Middle ◷ 20 min
Уровень
ОсновыJuniorMiddleSenior

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 напрямую.

1

Путь разрешения начинается с 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 и поочерёдно вызывает каждый источник, пока один не вернёт результат или авторитетное «не найдено».

2

/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-записями спустя месяцы.

3

/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, не замечая этого.

4

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 для разных интерфейсов.

5

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-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources1
expand
  1. 01

Trademarks belong to their respective owners. Editorial reference only.