Два файла, которые решают, куда пойдёт твой трафик: /etc/hosts и resolv.conf
Как Linux превращает имя сайта в IP-адрес: что делает /etc/hosts, за что отвечает resolv.conf, кто из них главнее и почему иногда сервер стучится не туда, куда ты думаешь.
Есть два маленьких текстовых файла, которые целиком решают, куда пойдёт трафик с твоей машины. Оба лежат в /etc, оба открываются любым редактором, в обоих по три строчки. Речь про /etc/hosts и /etc/resolv.conf. Штуки простые, но именно на них держится вся логика превращения имени в адрес — и мне давно хотелось разложить её по полкам в одном месте.
Их часто путают или сваливают в одну кучу «что-то там про DNS». Но задачи у них разные. Один хранит имена, которые ты прописал руками. Второй говорит системе, у кого спрашивать про все остальные. А порядок, в котором система к ним обращается, задаёт третий файл, про который вспоминают редко. Дальше разберу все три и то, как они складываются в единую картину.
Сначала — зачем это вообще нужно
Компьютеры между собой общаются по IP-адресам, не по именам. Когда ты набираешь github.com, где-то по пути это имя должно превратиться в что-то вроде 140.82.121.3. Процесс называется резолвингом имени, и до того, как твой пакет уйдёт в сеть, система обязана понять, какой за именем стоит адрес.
github.com ──► [ РЕЗОЛВИНГ ] ──► 140.82.121.3 ──► пакет уходит в сеть
Вопрос в том, откуда система берёт ответ. И вот тут в игру вступают наши два файла плюс библиотека, которая ими рулит.
/etc/hosts — твоя личная записная книжка
Самый простой из двух. Это обычный список: слева IP, справа одно или несколько имён.
127.0.0.1 localhost
127.0.1.1 my-laptop
10.0.0.5 db.internal db
192.168.1.50 registry.local
Строчку читаешь буквально: «имя db.internal (и короткое db) — это адрес 10.0.0.5». Никакой сети, никаких запросов наружу. Система просто заглядывает в файл, находит совпадение и берёт готовый адрес.
Здесь почти всегда живёт localhost, привязанный к 127.0.0.1, — без этой строчки половина софта начнёт вести себя странно. А всё остальное ты дописываешь сам, под свои задачи.
Пользы от него больше, чем кажется на первый взгляд. Несколько рабочих сценариев, где /etc/hosts незаменим:
- Заглушить домен. Прописываешь
0.0.0.0 ads.example.com— и обращения к нему уходят в никуда. На этом, по сути, держатся простые блокировщики рекламы уровня системы. - Потыкать новый сервер до переключения DNS. Домен ещё смотрит на старую машину, а ты уже прописал у себя новый IP и проверяешь, что там всё поднялось. Все остальные видят старый сайт, ты — новый.
- Ходить к сервису по внутреннему имени, которого нет ни в одном публичном DNS. Внутри компании так живут десятки адресов.
Но у этой простоты есть обратная сторона, о которой стоит помнить. /etc/hosts — это ручная правка, которая живёт вечно и молча. Захардкодил IP «на пять минут, проверить», забыл убрать — и через полгода приложение упрямо ходит на старый адрес, хотя в конфигах и DNS всё давно поменяли. Файл никак себя не проявляет, просто тихо переопределяет всё остальное. Поэтому первым делом при странном резолвинге я заглядываю именно сюда.
/etc/resolv.conf — к кому идти за всем остальным
Записать руками весь интернет в hosts нельзя. Поэтому для всего, чего там нет, система идёт к DNS-серверу — и адрес этого сервера лежит в /etc/resolv.conf.
Внутри тоже ничего страшного:
nameserver 8.8.8.8
nameserver 1.1.1.1
search internal.example.com
Разберём по строчкам.
nameserver — это IP DNS-сервера, которому уходят запросы. Их может быть несколько: если первый не ответил, система идёт ко второму. Именно сюда попадает адрес твоего роутера, корпоративного резолвера или публичного 8.8.8.8 от Google.
search — список доменов, которые дописываются к коротким именам. Набрал ping db — а система, не найдя такого имени, попробует db.internal.example.com. Штука удобная, но иногда подкидывает сюрпризы, когда короткое имя вдруг разрешается совсем не туда, куда ты ждал.
Важный нюанс: этот файл почти никогда не стоит править руками. На большинстве систем его перезаписывают автоматически — NetworkManager, systemd-resolved, DHCP-клиент, в контейнере — рантайм Docker или Kubernetes. Впишешь свой nameserver, перезагрузишься — а файл снова сгенерирован заново, как будто правки и не было. Наверху обычно стоит честное предупреждение вроде «не редактируй, я генерируюсь автоматически» — и это ровно так и работает. Настраивать DNS нужно там, откуда файл берётся, а не в самом файле.
Кто из них главнее и в каком порядке
Вот тут самое интересное. Ни hosts, ни resolv.conf не решают сами, к кому обращаться первым. За порядок отвечает третий, менее заметный файл — /etc/nsswitch.conf. В нём есть строчка примерно такая:
hosts: files dns
files — это /etc/hosts. dns — это то, что настроено в resolv.conf. Порядок слов — это буквально порядок поиска: сначала загляни в файл, и только если там пусто — иди в DNS.
Собранная вместе картина выглядит так:
приложению нужен адрес для "db.internal"
│
▼
┌─────────────────────────────┐
│ 1. /etc/hosts (files) │
│ нашёл совпадение? │
└─────────────┬───────────────┘
│
да ────────┴──────── нет
│ │
▼ ▼
берём адрес ┌─────────────────────────┐
из файла, │ 2. DNS-сервер из │
в сеть не │ /etc/resolv.conf │
ходим вообще │ спрашиваем у него │
└───────────┬─────────────┘
│
▼
адрес из ответа DNS
Отсюда следует главное правило, которое стоит держать в голове. Запись в /etc/hosts перебивает DNS. Всегда. Пропишешь там 1.2.3.4 github.com — и никакой публичный DNS уже не переубедит систему, для неё github.com теперь живёт по 1.2.3.4, точка. Правишь зону в DNS, ждёшь обновления, а «ничего не меняется» — потому что на машине лежит строчка в hosts, которая молча выигрывает у любого DNS-сервера мира.
Как это выглядит на практике
Пара команд, которые стоит держать под рукой, когда «сайт открывается не тот» или «сервис не находит соседа».
Проверить, куда вообще резолвится имя и через какой путь:
$ getent hosts db.internal
10.0.0.5 db.internal
getent хорош тем, что ходит ровно тем же путём, что и приложения, — через nsswitch.conf. То есть учитывает и hosts, и DNS в том самом порядке. А вот привычные dig и nslookup этого не делают: они лезут напрямую в DNS и /etc/hosts игнорируют. Отсюда классическая ловушка — dig показывает один адрес, приложение ходит на другой, и ты не понимаешь, кто врёт. Никто не врёт: dig смотрит только в DNS, а приложение сначала заглянуло в hosts.
Посмотреть, к какому DNS-серверу реально уходят запросы:
$ cat /etc/resolv.conf
nameserver 127.0.0.53
search internal.example.com
Кстати, если увидел там 127.0.0.53 — это не ошибка. Так себя прописывает systemd-resolved: он ставит локальную заглушку, а настоящие DNS-серверы прячет у себя внутри. Тогда смотреть надо через resolvectl status, а не в самом файле.
Что из этого унести с собой
Если совсем коротко, вся логика умещается в три мысли.
/etc/hosts — это ручной справочник, который бьёт по имени и всегда выигрывает у DNS. Отлично подходит для локальных обходов и тестов, но легко забывается и потом тихо переопределяет всё остальное.
/etc/resolv.conf — это адрес того, у кого спрашивать всё остальное. Править руками почти бесполезно: его перепишут за тебя. Настраивать нужно там, откуда он генерируется.
А порядок между ними задаёт nsswitch.conf, и именно из-за этого порядка «правильная» запись в DNS может проигрывать строчке в hosts. Когда трафик уходит куда-то не туда, эти три файла — первое, куда стоит смотреть, ещё до конфигов самого приложения. В доброй половине случаев ответ находится именно там.