Вы вводите в браузере example.com и через долю секунды видите страницу. Но компьютеры не умеют соединяться по имени — им нужен числовой адрес вроде 93.184.216.34. Кто-то должен на лету превратить понятное человеку имя в этот адрес. Этим и занимается DNS (Domain Name System) — служба, которая работает как телефонная книга интернета: вы знаете имя, она возвращает номер.
Для backend-разработчика DNS — это не только «браузер открывает сайт». Ваш сервис ходит к базе по имени db.internal, к соседнему сервису по имени, к внешнему API по домену. Каждый такой вызов начинается с похода в DNS. И когда что-то ломается на этом шаге, симптомы бывают коварные — вплоть до «у меня всё работает, а в проде нет». Разберёмся, как это устроено.
DNS как телефонная книга интернета
Представьте старую бумажную телефонную книгу: слева имена, справа номера. Хотите позвонить — находите имя, читаете номер. DNS делает ровно то же самое, только для сети: на входе имя api.example.com, на выходе IP-адрес 203.0.113.10.
Разница в одном: единой книги не существует. Имён в мире миллиарды, и держать их в одном файле невозможно. Поэтому справочник распределён — разбит на части, за которые отвечают разные серверы по всему миру. Когда вам нужен адрес, система обходит несколько таких серверов и по кусочкам собирает ответ. Со стороны это выглядит мгновенно, но внутри — небольшое путешествие.
Путь запроса: от резолвера до авторитетного сервера
Допустим, вашему коду нужен адрес api.example.com. Вот что происходит по шагам:
- Рекурсивный резолвер. Ваша машина спрашивает не весь интернет, а один сервер-посредник — рекурсивный резолвер (обычно это DNS вашего провайдера или публичный вроде
8.8.8.8). Его задача — сбегать за ответом вместо вас и вернуть готовый IP. - Корневые серверы. Резолвер не знает адрес сразу, поэтому идёт по цепочке сверху вниз. Сначала он спрашивает корневой сервер: «Кто отвечает за зону
.com?» Корень не знает конкретный адресapi.example.com, но знает, куда обратиться дальше. - TLD-серверы. Корень отправляет резолвер к серверам зоны
.com(TLD — top-level domain, домен верхнего уровня). Те тоже не знают финальный адрес, но знают, кто отвечает заexample.com. - Авторитетный сервер. Наконец резолвер приходит к авторитетному серверу домена
example.com— это тот сервер, который держит настоящие записи этого домена. Он и отдаёт финальный ответ:api.example.com → 203.0.113.10.
Аналогия: вы ищете человека в огромном здании. На входе (корень) вам говорят: «Такие — на 7 этаже». На этаже (TLD) — «Кабинет 712». В кабинете (авторитетный сервер) наконец сидит тот, кто знает точный ответ. Резолвер проходит этот путь за вас и запоминает дорогу, чтобы в следующий раз не бегать заново.
Типы записей: A, AAAA, CNAME, MX, TXT
У авторитетного сервера хранится не один адрес, а набор записей разных типов. Каждая отвечает на свой вопрос. Основные:
- A — сопоставляет имя с IPv4-адресом. Самая частая запись:
example.com → 93.184.216.34. - AAAA — то же самое, но для IPv6-адреса (
2606:2800:220:1:248:1893:25c8:1946). Читается «quad-A». - CNAME — псевдоним: имя указывает не на адрес, а на другое имя. Например,
www.example.com— это CNAME наexample.com. Удобно, когда несколько имён должны вести на один узел: меняете адрес в одном месте, остальные подтягиваются. - MX — куда доставлять почту для домена (mail exchange). Когда вы пишете на
user@example.com, почтовый сервер смотрит MX-запись, чтобы узнать, какой сервер принимает письма. - TXT — произвольный текст. Используется для верификации владения доменом и настроек почты (SPF, DKIM) — например, чтобы доказать, что письма от вашего имени отправляете именно вы.
Так выглядит фрагмент зоны на авторитетном сервере:
example.com. 3600 IN A 93.184.216.34
www.example.com. 3600 IN CNAME example.com.
example.com. 3600 IN MX 10 mail.example.com.
example.com. 3600 IN TXT "v=spf1 include:_spf.example.com ~all"
Число 3600 в начале — это TTL, о нём дальше.
Посмотреть записи можно самому. Утилита dig показывает ответ детально:
$ dig api.example.com A +short
203.0.113.10
А nslookup — попроще, для быстрой проверки:
$ nslookup example.com
Name: example.com
Address: 93.184.216.34
TTL и кэширование: почему изменения не мгновенны
Бегать по всей цепочке резолвинга на каждый запрос было бы медленно и накладно. Поэтому ответы кэшируются — запоминаются на время. И вот тут ключевую роль играет TTL (Time To Live) — то самое число 3600 из записи. Оно говорит: «этот ответ можно считать актуальным столько-то секунд». 3600 — значит один час.
Пока не истёк TTL, резолверы и ваша машина отдают запомненный адрес, не переспрашивая авторитетный сервер. Это быстро и снимает нагрузку. Но у этого есть обратная сторона: когда вы меняете адрес домена, старый ответ ещё живёт в кэшах по всему миру ровно столько, сколько разрешает TTL. Отсюда фраза «изменения DNS распространяются не мгновенно» — на самом деле ничего никуда не «распространяется», просто по всей планете постепенно истекают закэшированные ответы.
Практический вывод: если вы планируете переезд сервиса на новый адрес, заранее снизьте TTL записи (например, до 60 секунд) за сутки до переезда. Тогда в момент смены старый адрес протухнет в кэшах за минуту, а не за час. После переезда TTL можно вернуть обратно.
/etc/hosts: локальное переопределение
До всякого DNS операционная система сначала заглядывает в маленький локальный файл — /etc/hosts (на Windows — C:\Windows\System32\drivers\etc\hosts). Это простой список «имя — адрес»:
127.0.0.1 localhost
127.0.0.1 api.example.com
Если имя нашлось здесь — система берёт адрес отсюда и в DNS вообще не идёт. Это удобно для локальной разработки: можно заставить api.example.com указывать на вашу машину, чтобы тестировать код против локального сервиса под «настоящим» именем.
Но эта же удобная штука — источник классической путаницы. Строчка, добавленная в hosts пару месяцев назад и забытая, продолжает молча перенаправлять имя. Отсюда ситуации «у меня работает, у коллеги нет»: у вас в hosts живёт переопределение, о котором вы не помните. Если резолвинг ведёт себя странно — загляните в hosts в первую очередь.
DNS внутри Kubernetes
В Kubernetes DNS — это основа того, как сервисы находят друг друга. Вместо того чтобы прописывать IP-адреса подов (которые постоянно меняются при перезапусках), сервисы обращаются друг к другу по имени. Внутри кластера работает собственный DNS (обычно CoreDNS), и каждый Service автоматически получает имя.
Например, Service с именем orders в пространстве имён default доступен другим подам просто как orders или полным именем orders.default.svc.cluster.local. Ваш код пишет запрос на http://orders/..., кластерный DNS превращает имя в актуальный адрес, а трафик балансируется между живыми подами. Это и называется service discovery — обнаружение сервисов по имени. Тот же принцип «имя вместо адреса», что и в большом интернете, только внутри кластера.
Прикладной угол: DNS-кэш в клиентах и приложениях
Кэшируют не только резолверы в сети — кэшируют и сами приложения. Разные среды делают это по-своему: JVM, например, по умолчанию держит результаты DNS-резолвинга в памяти процесса; у HTTP-клиентов в Go, Node.js и Python — свои кэши и настройки. Если внешний адрес поменялся, а ваш сервис уже успел его закэшировать, он продолжит стучаться по старому адресу, даже когда DNS давно отдаёт новый. Перезапуск процесса сбрасывает этот кэш — отсюда народное «помогло перезапустить».
У каждой среды свой способ управлять временем жизни этого кэша (в JVM это, например, networkaddress.cache.ttl, и в старых версиях по умолчанию было «кэшировать навсегда», что особенно больно при переездах внешних API). Похожие внутренние кэши есть и у HTTP-клиентов, connection-пулов и sidecar-прокси. Так что когда после смены адреса «сервис A всё ещё ходит на старый B», подозреваемых несколько: кэш ОС, кэш резолвера, кэш приложения. Тот же механизм даёт эффект «работает у меня»: на вашей машине закэширован свежий адрес, а на другой — ещё старый, или наоборот.
Мораль простая: DNS — не мгновенный переключатель, а слоистая система кэшей с задержками. Планируя смену адресов, закладывайте эти задержки и помните про TTL на каждом уровне.
Где это применяется
DNS — первый шаг почти любого сетевого взаимодействия, поэтому он всплывает в отладке постоянно. Не открывается сайт, сервис не видит базу, интеграция «отвалилась после переезда» — прежде чем винить код, стоит проверить, правильно ли резолвится имя (dig/nslookup) и не закэширован ли где-то старый адрес. DNS лежит на прикладном слое стека — как это встроено в общую картину, см. в статье про модели OSI и TCP/IP.
Где спотыкаются начинающие:
- Забывают про TTL при переезде. Меняют A-запись и ждут мгновенного эффекта, а старый адрес живёт в кэшах ещё час. Снижать TTL нужно заранее.
- Не помнят про строчку в
/etc/hosts. Локальное переопределение молча перебивает DNS и даёт «у меня работает, у других нет». - Списывают всё на DNS-кэш приложения. Приложения и клиенты держат свой кэш (например, JVM); после смены адреса сервис может стучаться по старому, пока его не перезапустить или не настроить TTL кэша.
- Путают CNAME и A. CNAME указывает на имя, а не на адрес; нельзя вешать CNAME на корень домена и мешать его с другими записями.
- Думают, что DNS отдаёт «сайт». DNS отдаёт только адрес. Дальше по этому адресу устанавливается соединение и идёт HTTP — это уже другой шаг.
Что учить дальше
DNS вернул адрес — дальше начинается собственно соединение. Логично разобрать IP-адреса, порты и NAT: как по полученному адресу пакет находит машину и конкретный сервис. Затем — прикладной слой поверх: HTTP и его защищённый вариант HTTPS и TLS, где, кстати, имя из DNS ещё раз проверяется в сертификате. А чтобы понять, как сервис остаётся доступным, когда часть узлов отваливается, посмотрите надёжность и отказоустойчивость — там DNS и кэши тоже играют роль.