Теоретическую основу кэширования DNS в Linux мы разбирали в первой части, где говорили про работу процесса разрешения имен — от вызова getaddrinfo() до получения IP-адреса. Вторая часть была посвящена различным уровням кэшей самой системы, приложений и языков программирования, контейнеров, прокси - а также их мониторингу и
сбросу. Теперь самое время перейти к практике.
Если вы когда-либо запускали подряд команды ping, curl, dig и
получали разные IP-адреса, вы не одиноки. Поведение DNS в Linux — не
просто вызов getaddrinfo(). Это взаимодействие множества
слоёв: от glibc и NSS до NetworkManager, systemd-resolved, dnsmasq и
облачных конфигураций. В этой части разберем практические аспекты DNS:
- почему одинаковые запросы дают разные IP
- как реально контролируется разрешение имен: что вызывает кого и зачем
- как проводить диагностику: strace, resolvectl, tcpdump
Утилиты и их пути к DNS
В Linux-системах преобразование доменных имён в IP-адреса —
фундаментальный процесс, но не все утилиты делают это одинаково. Под
капотом скрываются конкурирующие механизмы: классический стек glibc/NSS
(с его правилами из /etc/nsswitch.conf, локальным файлом /etc/hosts и
кеширующими сервисами, такими как systemd-resolved), прямые DNS-запросы
(игнорирующие системные настройки) и альтернативные библиотеки
(например, c-ares). Эти различия часто становятся источником неочевидных
расхождений в работе инструментов, особенно при диагностике сетевых
проблем.
Типичный пример — разница в выводе getent hosts и dig для одного домена:
$ getent hosts google.com
142.250.179.206 google.com
$ dig +short google.com
142.250.179.238
Здесь getent опирается на кеш systemd-resolved (через NSS), а dig обходит системные механизмы, запрашивая DNS-сервер напрямую.
Чтобы систематизировать эти нюансы, ниже представлена сводка по поведению популярных утилит.
Таким образом можно разбить утилиты на "системные" и "прямые":
- Зависимые
от NSS: ping, wget, getent, curl (без c-ares) — отражают системное
состояние (кеш, /etc/hosts, настройки nsswitch.conf). - Обходящие NSS: dig, nslookup, curl (с c-ares) — игнорируют локальные правила, обращаясь напрямую к DNS.
Практические советы:
- Если ping и curl выдают разные IP, причина обычно в кеше NSS или настройках /etc/hosts.
- При
работе с curl/wget учитывайте их зависимость от libc: расхождения могут
указывать на проблемы в NSS (например, некорректный nsswitch.conf). - Для проверки системного разрешения (включая /etc/hosts) используйте getent, host или ping.
- Для валидации работы DNS-сервера применяйте dig/nslookup — они не смотрят в локальные файлы.
Итог:
понимание внутренних механизмов разрешения имён критично при отладке
сетевых проблем. Всегда сверяйтесь с таблицей выше, чтобы выбрать
правильный инструмент: проверка локальных настроек требует NSS-зависимых
утилит, а диагностика DNS — "прямых" запросов в обход системы.
Кто на самом деле управляет resolv.conf?
Когда-то давным-давно в далекой Галактике DNS настраивался простым редактированием /etc/resolv.conf:
nameserver 8.8.8.8
nameserver 1.1.1.1
search example.com
Сейчас же файл с содержанием nameserver 127.0.0.53 и предупреждением “DO NOT EDIT” может шокировать. Причина такого изменения в эволюции DNS-инфраструктуры:
Раньше: приложения → resolv.conf → DNS-сервер
Сейчас: приложения → локальный DNS-прокси → upstream серверы
В
современных дистрибутивах /etc/resolv.conf – это чаще всего не ручная
настройка, а автоматически генерируемый конфиг, создаваемый и
поддерживаемый системными компонентами: systemd-resolved,
NetworkManager, resolvconf или их аналогами вроде openresolv. Эта
автоматизация приносит гибкость (разные DNS для разных сетей, DNSSEC,
LLMNR/mDNS), но с другой стороны и некоторые потенциальные проблемы:
- Настройки могут слетать после перезагрузки сети или обновления пакетов.
- Конфликты при подключении VPN, которые пытаются переписать DNS.
- Сложности с использованием локальных доменов или специфичных DNS-серверов.
- Затрудненная отладка: куда на самом деле идут запросы?
Так кто же главный? systemd-resolved? NetworkManager? Как вернуть себе контроль?
Давайте
разбираться в этом лабиринте: какие инструменты претендуют на
управление /etc/resolv.conf, как они взаимодействуют и какие рычаги
управления у нас есть, чтобы заставить DNS работать так, как нам нужно.
dhclient
Это
стандартный DHCP-клиент в Linux-системах, входящий в пакет ISC DHCP.
Его основная задача автоматически получать настройки сети (IP-адрес,
сетевую маску, шлюз, DNS-сервера) у DHCP-сервера и применять их
локально.
По умолчанию DHCP-клиент (программа /sbin/dhclient) не
изменяет /etc/resolv.conf напрямую. Вместо этого вызывается
вспомогательный скрипт /sbin/dhclient-script, который получает параметры
(в том числе DNS-серверы) от DHCP-сервера.
Внутри dhclient-script
есть функция make_resolv_conf(), которая формирует содержимое файла
/etc/resolv.conf и записывает новые настройки DNS.
Кроме того
используются так называемые хуки (скрипты), которые лежат в каталогах
/etc/dhcp/dhclient-exit-hooks.d/ или /etc/dhcp/dhclient-enter-hooks.d/.
Они позволяют изменить логику формирования или перезаписи
/etc/resolv.conf, переопределяя функции или добавляя свои параметры.
Диагностика:
# Проверить конфигурацию DHCP-клиента
cat /etc/dhcp/dhclient.conf
# Запустить dhclient в debug-режиме
dhclient -v -d <интерфейс> # выведет подробный лог, включая обработку DNS).
# Проверить lease-файл DHCP**
cat /var/lib/dhcp/dhclient.leases # там хранятся полученные параметры, включая DNS-серверы
resolvconf
Это
устаревший, но иногда встречающийся служебный инструмент для
централизованного управления содержимым файла /etc/resolv.conf в
UNIX-подобных системах. Он сохраняет и обновляет /etc/resolv.conf,
собирая настройки DNS с различных источников: DHCP-клиентов,
VPN-клиентов, сетевых интерфейсов, NetworkManager, позволяет нескольким
программам и сервисам вносить свои DNS-серверы, динамически формируя
итоговый конфиг для системы. Работает как демон или в виде набора
скриптов-хуков, которые вызываются при изменении сетевой конфигурации,
принимает входящие DNS-настройки через программу или скриптовые вызовы,
обновляет кэш и генерирует конечный /etc/resolv.conf.
В resolvconf используются шаблоны для формирования итогового файла DNS:
- /etc/resolvconf/resolv.conf.d/head — вставляется в начало результата.
- /etc/resolvconf/resolv.conf.d/base — база доменов и серверов.
- /etc/resolvconf/resolv.conf.d/tail — добавляется в конец.
resolvconf
может использоваться другими службами (например, NetworkManager,
dhclient, openvpn) для централизованного обновления DNS-конфига.
В
современных дистрибутивах часто заменяется или отключается в пользу
systemd-resolved или других решений, но поддерживается для обратной
совместимости.
Диагностика:
# Вывод актуальной информации
resolvconf -l
# Обновить /etc/resolv.conf
resolvconf -u
systemd-resolved
Это локальный кэширующий dns-сервер (systemd-networkd)
Процесс systemd-resolved слушает 127.0.0.53:53, и nameserver в resolv.conf указывает на 127.0.0.53 соответственно.
Файл /etc/resolv.conf обычно является symlink на /run/systemd/resolve/stub-resolv.conf
В
свою очередь /run/systemd/resolve/stub-resolv.conf — содержит
127.0.0.53, а прямой список upstream DNS можно найти в файле
/run/systemd/resolve/resolv.conf.
Диагностика:
# Основная информация о состоянии
resolvectl status
# Статистика запросов
resolvectl statistics
# Информация по конкретному интерфейсу
resolvectl status eth0
dnsmasq
Это простой DHCP/DNS сервер, который работает зачастую как локальный кэш на 127.0.0.1:53.
Обычно dnsmasq не пишет в resolv.conf самостоятельно, а читает upstream из resolv.conf, использует директиву --server= при запуске, или сервера прописаны в конфигурационном файле /etc/dnsmasq.conf.
Диагностика:
# Перезагрузка конфигурации
sudo kill -USR1 $(pidof dnsmasq)
# Проверка параметров запуска
ps aux | grep dnsmasq
NetworkManager
Это
инструмент для автоматической настройки сети в большинстве современных
десктопных и серверных дистрибутивах Linux. Был разработан компанией Red
Hat в 2004 году для упрощения современных сетевых задач. Взаимодействие
с ним настолько “упрощает” работу с сетью, что очень многие системные
администраторы отключают его сразу.
Обычно NetworkManager получает
настройки от DHCP (через внутренний DHCP-плагин или внешний dhclient),
принимая DNS-серверы из DHCP-ответа (опция option domain-name-servers), и
передает их в системную службу управления DNS.
Поведение при обработке DHCP DNS можно контролировать через параметр ipv4.ignore-auto-dns:
- ipv4.ignore-auto-dns=no (по умолчанию) - NM использует DNS-серверы, полученные от DHCP
- ipv4.ignore-auto-dns=yes - игнорировать DNS от DHCP и использовать только статически заданные серверы
Сам
NetworkManager не обрабатывает DNS-запросы и не слушает порт 53, а в
зависимости от настроек запускает dnsmasq (который слушает 127.0.0.1:53)
или взаимодействует с systemd-resolved (обычно слушает 127.0.0.53:53).
Что запускается - зависит от параметра dns в /etc/NetworkManager/NetworkManager.conf:
dns=systemd-resolved — используется только systemd-resolved.
dns=dnsmasq — запускается dnsmasq через NetworkManager.
dns=none — NetworkManager не берет на себя функции управления резолвингом DNS
dns=default — NetworkManager сам решает, что делать.
При
взаимодействии с systemd-resolved NetworkManager выступает в роли
менеджера конфигурации, передавая параметры DNS через D-Bus API
systemd-resolved, но не участвуя в обработке DNS-запросов напрямую.
Тогда у нас будет наблюдаться следующая картина:
resolvectl status # отображает текущие DNS-серверы, которые systemd-resolved получил от NetworkManager
nmcli dev show | grep DNS # показывает DNS-серверы, которые NetworkManager передал в systemd-resolved
При работе с dnsmasq NetworkManager запускает свой экземпляр dnsmasq.
В
таком случае основные настройки, такие как список апстрим серверов,
dnsmasq будет брать из сгенерированного
‘/run/NetworkManager/dnsmasq.conf’, а также в дополнительных файлах из
каталога ‘/etc/NetworkManager/dnsmasq.d/. Управление /etc/resolv.conf
при этом зависит от опции rc-manager. NM самостоятельно перезаписывает
/etc/resolv.conf (rc-manager=file), указывая в нем nameserver 127.0.0.1,
или создает symlink (значение по умолчанию, rc-manager=symlink) на свой
служебный файл /run/NetworkManager/resolv.conf, чтобы все приложения
обращались к dnsmasq.
При настройке по умолчанию NetworkManager обычно решает, что выбрать следующим способом:
Если systemd-resolved активен (systemctl is-active systemd-resolved), NM будет использовать dns=systemd-resolved.
Если
systemd-resolved не активен, и в сборке NM содержит dnsmasq, то он
запускает собственный экземпляр dnsmasq (аналог dns=dnsmasq).
Нет ни systemd-resolved, ни dnsmasq - NM сам пишет resolv.conf с DNS от DHCP/static.
Диагностика:
# Информация о DNS по интерфейсам
nmcli dev show
# Проверка конфигурации NetworkManager
cat /etc/NetworkManager/NetworkManager.conf
unbound
Это удостоверяющий рекурсивный и кеширующий DNS сервер.
Процесс
unbound по умолчанию слушает порт 53 на локальном интерфейсе
127.0.0.1:53. Настройки меняются в конфигурационном файле
/etc/unbound/unbound.conf.
Диагностика:
sudo unbound-control status # Проверка состояния
sudo unbound-control stats_noreset # Статистика запросов
journalctl -u unbound -f # Логи в реальном времени
BIND
Berkeley Internet Name Domain — это полноценный DNS-сервер.
BIND
был первым ПО, реализовавшим стандарт DNS (RFC 1034/1035), и до сих пор
остается самым распространенным DNS-сервером в интернете. Также часто
используется в корпоративных сетях.
Его системный процесс named по
умолчанию слушает порт 53 на всех интерфейсах (0.0.0.0:53). Поведение
может быть настроено в конфигурационном файле /etc/named.conf.
Конфигурации зон обычно хранятся в /var/named.
Диагностика:
sudo rndc status # Проверка работы
sudo named-checkconf # Проверка синтаксиса конфига
sudo rndc querylog on # Включение логгирования (по умолчанию выключено)
tail -f /var/log/named/queries.log # Просмотр запросов в реальном времени
journalctl -u named -f # Системные логи (если используется systemd)
cloud-init
Это
стандартный инструмент инициализации облачных инстансов (AWS, Azure,
GCP, OpenStack и др.). Выполняет начальную настройку сети на этапе
первичной загрузки виртуальной машины, включая установку DNS-серверов
(полученных через метаданные облачного провайдера или из конфигурации),
модификацию /etc/resolv.conf, интеграцию с другими сервисами
(systemd-resolved, NetworkManager). После инициализации он обычно
передает управление DNS другим сервисам (например, NetworkManager).
Диагностика:
# Проверить, активен ли cloud-init
sudo systemctl status cloud-init
# Просмотреть логи инициализации
sudo cat /var/log/cloud-init.log | grep "DNS"
# ключевые параметры в /etc/cloud/cloud.cfg:
manage_resolv_conf: true # Разрешает cloud-init менять resolv.conf
resolv_conf:
nameservers: ["8.8.1.1"] # Статические DNS (если не получены от облака)
search_domains: ["example.com"]
netplan
Это
стандартный инструмент конфигурации сети в дистрибутивах Ubuntu
(начиная с 17.10). Работает как абстрактный слой между YAML-конфигами и
низкоуровневыми бэкендами (systemd-networkd или NetworkManager).
В зависимости от опции network.renderer (networkd/NetworkManager):
Netplan
генерирует конфиг для systemd-networkd, который передает DNS-настройки в
systemd-resolved, который в свою очередь создает:
- /run/systemd/resolve/stub-resolv.conf → содержит nameserver 127.0.0.53 (символическая ссылка для /etc/resolv.conf)
- /run/systemd/resolve/resolv.conf → реальные апстрим-серверы (только для чтения)
или
Netplan генерирует конфиг в /run/NetworkManager/conf.d/netplan.conf, и NetworkManager применяет настройки:
- dns=systemd-resolved - создает /run/NetworkManager/resolv.conf → ссылка на stub-resolv.conf
- dns=dnsmasq - указывает в /etc/resolv.conf → nameserver 127.0.0.1
Диагностика:
# Проверить актуальный бэкенд
sudo netplan info
grep "renderer" /etc/netplan/*.yaml
Независимый арбитр: трассировка как инструмент истины
Когда
статический анализ конфигураций (resolv.conf, resolvectl,
NetworkManager) и состояния сервисов не дают однозначной картины или их
данные противоречат реальному поведению, на помощь приходит трассировка.
Она показывает работу системы в динамике, минуя слои абстракции и
потенциальные несоответствия конфигураций.
Это последний арбитр
для проверки гипотез, кто фактически обрабатывает DNS-запрос.
Используется для обнаружения скрытых проблем, таких как игнорирование
записей resolv.conf, некорректный выбор upstream, аудита и
документирования, куда реально уходят запросы в сложном окружении.
Для трассировки используем два мощных инструмента:
- strace - показывает взаимодействие приложения с ядром.
- tcpdump - показывает реальный сетевой трафик.
1. Системные вызовы с strace. Смотрим, что пытается сделать приложение.
# Отслеживание сетевой активности приложения:
strace -f -e trace=network curl -s http://example.com > /dev/null
# Поиск обращения к конфигурации DNS:
strace -e openat,read,open myapp 2>&1 | grep -i resolv.conf
Ключевые моменты для анализа:
- socket(AF_INET, SOCK_DGRAM, ...) – создание UDP-сокета (основной протокол DNS).
- connect() или sendto() на порт 53 – попытка отправки DNS-запроса на конкретный адрес:порт.
- Чтение /etc/resolv.conf (openat, read)
– приложение использует классический механизм, полагаясь на этот файл.
Серверы из него могут быть использованы (но не факт, см. tcpdump!). - Отсутствие
чтения /etc/resolv.conf – сильный индикатор того, что приложение
использует альтернативный механизм разрешения имен (например, напрямую
через API systemd-resolved), объясняя работу с nameserver 127.0.0.53 без
явного чтения файла.
2. Сетевой трафик с tcpdump. Куда запросы идут на самом деле?
# Общий мониторинг DNS (UDP/TCP порт 53):
sudo tcpdump -ni any port 53
# Мониторинг конкретного интерфейса:
sudo tcpdump -ni tun0 port 53
# Для чистоты эксперимента: одна консоль - tcpdump, другая - `dig example.com`
Интерпретация:
- Запросы к публичным DNS (8.8.8.8:53, 1.1.1.1:53): приложение или прокси обращается напрямую к внешним серверам.
- Важно:
если в /etc/resolv.conf указан 127.0.0.53 (или др. локальный адрес), но
запросы идут напрямую наружу – это проблема (приложение/прокси
игнорирует настройки). - Запросы только к 127.0.0.53:53 – классический признак работы через systemd-resolved.
- Запросы к 127.0.0.1:53 (или др. localhost) – указывает на другой локальный DNS-прокси (dnsmasq, unbound и т.д.).
- Отсутствие
запросов при повторных обращениях – скорее всего, сработал кэш
(локальный в приложении, в systemd-resolved или другом прокси). - Запросы к неожиданным адресам – возможно влияние VPN (если виден его DNS) или вредоносного ПО.
3. Анализ upstream-серверов. Куда смотрит локальный прокси?
Если
у вас работает локальный прокси (systemd-resolved, dnsmasq), важно
проверить, на какие фактические серверы он отправляет запросы.
# Фильтрация локального трафика между приложением и прокси:
sudo tcpdump -n port 53 and not \(host 127.0.0.53 or host 127.0.0.1\)
Что это дает:
Показывает только запросы, которые прокси отправляет внешним (upstream) DNS-серверам.
Позволяет проверить, использует ли прокси заданные серверы.
Критически
важно для обнаружения утечек DNS, особенно при использовании VPN: если
вы видите запросы к серверам вашего провайдера или публичным DNS, а не к
VPN-серверу, когда туннель активен – это утечка.
В итоге трассировка снимает все слои абстракции и показывает актуальный маршрут DNS-запроса.
Сравнение
результатов strace/tcpdump со статической конфигурацией дает полное
понимание, кто и как управляет разрешением имен в вашей системе.
Практика
Теперь,
когда мы разобрали основные компоненты и методы диагностики, перейдем к
практическому применению. Ниже попытался собрать информацию в краткий
пошаговый алгоритм, который поможет определить, какая служба управляет
DNS и где искать возможные проблемы с конфигурацией.
Reboot or not reboot? Механизмы чтения resolv.conf и необходимость перезапуска приложений
Как
мы уже поняли, файл /etc/resolv.conf является ключевым компонентом
системы разрешения DNS в Linux. При этом не только управление им бывает
разным, но и способ его обработки различными приложениями и средами
выполнения существенно отличается, что приводит к важным последствиям
при изменении DNS-конфигурации.
Существует три основных подхода к работе приложений с файлом resolv.conf:
1. Прямое чтение при каждом запросе (glibc, стандартные утилиты)
Используется системный вызов getaddrinfo() с проверкой mtime файла перед каждым чтением. Пример реализации в glibc:
if (stat("/etc/resolv.conf", &statb) == 0 &&
statb.st_mtime != _res.res_conf_mtime) {
__res_vinit(&_res, 1); // Перечитывание файла
}
Преимущества: Мгновенная реакция на изменения
Недостатки: Дополнительные системные вызовы при каждом DNS-запросе
2. Кэширование при старте (Java, Go <1.21)
Конфигурация загружается один раз при инициализации и хранится в статических переменных. Пример в Go (до версии 1.21):
var dnsConf struct {
sync.Once
conf *dnsConfig
}
func initDNS() {
dnsConf.Do(func() {
// Чтение происходит только один раз
dnsConf.conf = dnsReadConfig("/etc/resolv.conf")
})
}
Преимущества: Высокая производительность, отсутствие лишних I/O операций
Недостатки: Необходимость перезапуска приложения при изменении DNS
3. Гибридный подход (браузеры, современные языки)
Первоначальное чтение при запуске с периодической проверкой изменений через:
- inotify (Linux)
- kqueue (BSD)
- D-Bus (системные события)
// Пример в Node.js
const fs = require('fs');
const { Resolver } = require('dns');
const resolver = new Resolver();
// Функция для обновления DNS-серверов
function updateDNS() {
try {
const data = fs.readFileSync('/etc/resolv.conf', 'utf8');
const servers = data.split('\n')
.filter(line => line.startsWith('nameserver'))
.map(line => line.split(/\s+/)[1]);
resolver.setServers(servers);
console.log('DNS серверы обновлены:', servers);
} catch (err) {
console.error('Ошибка чтения resolv.conf:', err);
}
}
// Инициализация при запуске
updateDNS();
// Отслеживание изменений
fs.watch('/etc/resolv.conf', () => {
console.log('Обнаружено изменение resolv.conf!');
updateDNS();
});
В итоге, необходимость перезапуска приложения при изменении в файле resolv.conf определяется следующими факторами:
А. Уровень интеграции с системой
- Приложения, использующие glibc напрямую (C, Python через ctypes) не требуют перезапуска
- Языки со своим рантаймом (JVM, Go до 1.21) часто кэшируют конфигурацию
Б. Архитектура резолвера
- Статические реализации (традиционные прокси-серверы) требуют рестарта
- Динамические системы (Envoy, современные браузеры) могут обновляться “на лету”
Таким образом, базовый набор практических выводов можно сформулировать следующим образом.
Без перезапуска работают:
- Стандартные Linux-утилиты (ping, curl, dig)
- Скрипты на Python/Ruby с нативными биндингами
- Приложения с явной поддержкой динамического обновления (Chrome, Firefox)
- Go-программы версии 1.21 и выше (с флагом GODEBUG=netdns=go)
Требуют перезапуска:
- JVM-приложения (из-за статической инициализации и кэширования в InetAddress)
- Go-программы до версии 1.21
- Традиционные прокси-серверы (nginx без директивы resolver)
- Долгоживущие демоны без механизма релоада конфигурации
Заключение
DNS
в Linux — это многослойная система, где /etc/resolv.conf лишь верхушка
айсберга. Настройки могут диктоваться systemd-resolved, NetworkManager,
dhclient, cloud-init и другими компонентами, а поведение утилит —
зависеть от кэширования, nsswitch.conf, и даже от того, когда и как
именно приложение читает конфигурацию.
Мы увидели, что одинаковые
DNS-запросы могут давать разные результаты в зависимости от используемой
утилиты, что resolv.conf часто формируется динамически сразу
несколькими источниками, а эффективная диагностика требует не только
анализа конфигураций, но и наблюдения за реальным поведением — через
strace, tcpdump и логи прокси-серверов.
Чтобы эффективно решать DNS-проблемы:
- Однозначно определите, кто и как формирует /etc/resolv.conf в вашей системе.
- Используйте правильные инструменты для нужной цели (“системные” или “прямые”).
- Учитывайте, что не все приложения автоматически замечают изменения DNS — иногда нужен перезапуск.
- При сомнениях — трассировка всегда покажет истинные маршруты запросов.
Понимание этих механизмов не только сэкономит часы отладки, но и позволит проектировать инфраструктуру, где домены резолвятся предсказуемо — в том числе в динамически меняющихся сетях и при сложных правилах маршрутизации.
В следующей части мы погрузимся в то, как устроен DNS в контейнерных средах: Docker, Podman, Kubernetes, и как не потерять контроль над резолвингом в изолированных пространствах имён и виртуальных сетях.