Вы вводите в браузере 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. Вот что происходит по шагам:

  1. Рекурсивный резолвер. Ваша машина спрашивает не весь интернет, а один сервер-посредник — рекурсивный резолвер (обычно это DNS вашего провайдера или публичный вроде 8.8.8.8). Его задача — сбегать за ответом вместо вас и вернуть готовый IP.
  2. Корневые серверы. Резолвер не знает адрес сразу, поэтому идёт по цепочке сверху вниз. Сначала он спрашивает корневой сервер: «Кто отвечает за зону .com?» Корень не знает конкретный адрес api.example.com, но знает, куда обратиться дальше.
  3. TLD-серверы. Корень отправляет резолвер к серверам зоны .com (TLD — top-level domain, домен верхнего уровня). Те тоже не знают финальный адрес, но знают, кто отвечает за example.com.
  4. Авторитетный сервер. Наконец резолвер приходит к авторитетному серверу домена 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 и кэши тоже играют роль.