Зачем вообще разбираться в Kubernetes DNS
Проблемы с резолвингом в Kubernetes часто выглядят одинаково: Service «вроде есть», Pod «вроде живой», но приложение периодически не может найти имя, запросы внезапно уходят во внешний DNS, а время ответа скачет. Почти всегда корень — комбинация настроек DNS внутри Pod (в первую очередь /etc/resolv.conf), логики ndots/search и того, как CoreDNS выбирает upstream (куда он форвардит запросы, которые не относятся к кластеру).
Ниже разложу Kubernetes DNS «по проводам»: что kubelet создаёт в Pod, как CoreDNS строит ответы, где обычно ломается форвардинг во внешний резолвер, и какие команды dig/nslookup реально помогают при инциденте.
Как выглядит DNS-путь запроса из Pod
Типовой путь запроса такой:
- Приложение в Pod вызывает резолвер (glibc/musl/библиотека языка), который читает правила из
/etc/resolv.conf. - Запрос уходит на
nameserver, которым обычно является ClusterIP сервиса kube-dns/CoreDNS (например,10.96.0.10). - CoreDNS решает: имя относится к зоне кластера (Service/Pod) или это внешняя зона.
- Если зона кластера — отвечает через плагин
kubernetes; если внешняя — пересылает вupstreamчерезforward(илиproxyв старых Corefile).
Для администратора здесь две зоны ответственности:
- DNS внутри Pod:
ndots,search, лимиты резолвера, нюансы glibc/musl, поведение приложения. - DNS в CoreDNS: Corefile, плагины, кеш, политика форвардинга, отдельные зоны (split-DNS).
Если вы держите несколько окружений (dev/stage/prod) или вам нужен изолированный стенд под CoreDNS-эксперименты и нагрузочные тесты DNS, удобнее всего делать это на отдельном VDS и воспроизводить проблему без влияния на боевой кластер.
Что находится в /etc/resolv.conf внутри Pod
Начните с самого простого: зайдите в Pod и посмотрите resolv.conf (в том же Pod, где проявляется проблема).
kubectl exec -it deploy/app -- cat /etc/resolv.conf
Чаще всего будет что-то вроде:
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
Здесь важно всё:
nameserver— куда именно Pod отправляет DNS-запросы (обычно сервис CoreDNS).search— суффиксы, которые резолвер будет автоматически подставлять к «коротким» именам.options ndots:5— порог: начиная с какого количества точек имя считается «достаточно полным», чтобы сначала пробовать резолвить «как есть» (без подстановкиsearch).

Почему ndots так сильно влияет на задержки
ndots — это количество точек в имени, начиная с которого резолвер сначала пытается резолвить имя «как есть», и только затем (если не получилось) перебирает варианты с search. В Kubernetes по умолчанию часто стоит ndots:5.
Практический эффект: имя вроде api.example.com содержит две точки. При ndots:5 резолвер будет считать его «коротким» и сначала попробует (в порядке из search):
api.example.com.default.svc.cluster.localapi.example.com.svc.cluster.localapi.example.com.cluster.local- и только потом —
api.example.comкак внешнее имя
Узнаваемый симптом: внешние домены резолвятся медленно, а в логах CoreDNS много запросов к несуществующим именам, где хвост заканчивается на
.svc.cluster.local.
Откуда берутся эти значения
Содержимое /etc/resolv.conf в Pod формируется kubelet на ноде на основе:
- настроек Pod (
spec.dnsPolicy,spec.dnsConfig); - параметров kubelet (например,
--cluster-dns,--cluster-domain); /etc/resolv.confсамой ноды (в зависимости от выбранной политики).
dnsPolicy: ClusterFirst, Default, None — что реально меняется
dnsPolicy определяет, как Pod использует кластерный DNS:
- ClusterFirst (обычно по умолчанию):
nameserverуказывает на CoreDNS, аsearchвключает...svc.cluster.local. - Default: Pod берёт DNS-настройки ноды (часто полезно для диагностики или для
hostNetwork). - None: DNS задаётся вручную через
dnsConfig(и здесь легко сломать резолвинг кластерных имён, если забыть нужные search-домены).
Если в Pod внезапно не ClusterIP CoreDNS, первым делом проверьте манифест и мутации (admission): нет ли dnsPolicy: Default/None или подмены dnsConfig.
CoreDNS, kube-dns и Service kube-dns: почему названия путают
Исторически в Kubernetes был аддон kube-dns. Сейчас чаще всего работает CoreDNS, но Service в кластере по привычке может называться kube-dns, а не coredns. Это нормально: важен не label, а фактическая конфигурация и pod’ы за сервисом.
Быстрая проверка:
kubectl -n kube-system get svc kube-dns
kubectl -n kube-system get deploy -l k8s-app=kube-dns
kubectl -n kube-system get cm coredns -o yaml
Если конфиг хранится в ConfigMap coredns, «движок» — CoreDNS, даже если сервис называется kube-dns.
Как CoreDNS решает: отвечать самому или идти в upstream
В CoreDNS это определяется Corefile. Типовой фрагмент выглядит так (пример для понимания, не копируйте слепо):
.:
errors
health
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
Ключевые моменты:
- Плагин
kubernetesотвечает за зоны кластера (обычноcluster.localи reverse-зоны). - Плагин
forwardопределяет upstream — куда отправлять всё остальное (в примере — резолверы из/etc/resolv.confвнутри Pod’а CoreDNS, то есть DNS ноды). cacheуменьшает частоту обращений к upstream;loadbalanceможет менять порядок A/AAAA в ответах (иногда важно при отладке).
Если вам нужно закрыть вопросы «доверия» и шифрования при обращении к внутренним сервисам (в том числе тем, что обнаруживаются через DNS), посмотрите SSL-сертификаты: с корректным TLS меньше шансов спутать «не тот» сервис при похожих именах и окружениях.
Что такое upstream на практике
Upstream — это внешние (относительно CoreDNS) DNS-резолверы, которые умеют резолвить «не кластерные» имена: публичные домены, корпоративные зоны, партнёрские зоны и т.д.
Частые варианты upstream:
- DNS ноды через
forward . /etc/resolv.conf(удобно, но вы наследуете все особенности ноды). - Явно заданные IP корпоративных DNS-серверов (часто надёжнее и предсказуемее).
- Отдельный кэширующий резолвер в инфраструктуре (например, Unbound) — полезно для стабильности и контроля.
Типовая проблема: CoreDNS форвардит в systemd-resolved stub и ловит ошибки
На многих дистрибутивах нода использует systemd-resolved со stub’ом 127.0.0.53. Если CoreDNS настроен на /etc/resolv.conf, а там указан 127.0.0.53, CoreDNS внутри Pod’а не сможет обратиться к loopback ноды и начнёт получать таймауты или SERVFAIL (конкретное поведение зависит от сети и окружения).
Проверка:
kubectl -n kube-system exec -it deploy/coredns -- cat /etc/resolv.conf
Если видите nameserver 127.0.0.53 — это красный флаг. Обычно решение одно из двух: прописать CoreDNS явные upstream-адреса или обеспечить, чтобы CoreDNS читал «правильный» resolv.conf ноды (не stub).
Split-DNS (stub zones): как направлять отдельные зоны в отдельные DNS
Термин stubDomains исторически связан с kube-dns, но идея актуальна и для CoreDNS: часть доменных зон нужно резолвить через корпоративные DNS, а всё остальное — через общий upstream.
В CoreDNS это обычно делают отдельными server block’ами или отдельными правилами forward на конкретную зону. Принцип:
- для зоны
corp.localфорвардим запросы на корпоративные DNS (upstream только для этой зоны); - для остальных зон используем общий upstream.
Практика: split-DNS почти всегда проще поддерживать на уровне CoreDNS, чем разносить по десяткам манифестов Pod’ов.
Если вы параллельно наводите порядок с корпоративными зонами и их делегированием, может быть полезно посмотреть материал: делегирование поддомена через NS: как избежать сюрпризов с DNS.
Диагностика: dig из Pod и чтение результатов
Для отладки удобнее всего работать прямо из проблемного Pod или из временного debug Pod (в зависимости от ваших политик образов). Ниже — проверки, которые чаще всего дают ответ «где именно сломалось».
1) Проверяем, что Pod видит CoreDNS
kubectl exec -it deploy/app -- nslookup kubernetes.default.svc.cluster.local 10.96.0.10
Если ответа нет, проблема обычно не в upstream, а в доступности CoreDNS: Service/Endpoints, NetworkPolicy, CNI, kube-proxy/IPVS, conntrack на ноде.
2) Смотрим search и ndots в Pod
kubectl exec -it deploy/app -- cat /etc/resolv.conf
Если search неожиданно длинный или содержит «чужие» домены, резолвер будет делать лишние попытки. Отдельно следите за ndots: при ndots:5 внешние имена почти всегда сначала «обрастают» суффиксами из search.
3) Сравниваем короткое имя и FQDN
Допустим, у вас есть сервис redis в namespace default.
kubectl exec -it deploy/app -- dig +time=1 +tries=1 redis
kubectl exec -it deploy/app -- dig +time=1 +tries=1 redis.default.svc.cluster.local
Если FQDN резолвится стабильно и быстро, а короткое имя — долго, время уходит на перебор вариантов по search и ожидание таймаутов на промежуточных запросах.
4) Проверяем внешний домен и видим «паразитные» попытки
kubectl exec -it deploy/app -- dig +search +time=1 +tries=1 api.example.com
Опция +search помогает увидеть поведение с учётом search-суффиксов. Если в выводе проскакивают варианты с .svc.cluster.local, это ожидаемо при высоком ndots, но иногда нежелательно по задержкам.
5) Смотрим, куда CoreDNS форвардит (upstream), и жив ли он
Сначала проверьте Corefile, затем — резолвинг из самого Pod’а CoreDNS:
kubectl -n kube-system get cm coredns -o yaml
kubectl -n kube-system exec -it deploy/coredns -- cat /etc/resolv.conf
kubectl -n kube-system exec -it deploy/coredns -- nslookup example.com
Если из CoreDNS не резолвится внешнее имя, проблема почти точно в upstream (сетевой доступ, firewall, недоступен корпоративный DNS, или CoreDNS смотрит в stub).

Частые причины проблем и быстрые фиксы
Слишком высокий ndots и лишние запросы
Если приложение постоянно ходит во внешние домены, а вы видите задержки и много NXDOMAIN в CoreDNS, можно рассмотреть снижение ndots точечно для конкретных Pod’ов через dnsConfig. Делайте это осознанно: низкий ndots меняет порядок попыток резолвинга и иногда может повлиять на «короткие» имена, если они пересекаются с внешними зонами.
Если есть возможность — используйте FQDN для внешних имён в конфигурации приложений. Это самый простой способ уменьшить зависимость от search/ndots.
CoreDNS смотрит в неправильный upstream
Если CoreDNS форвардит в /etc/resolv.conf, а там systemd-resolved stub или DNS, недоступные из Pod-сети, вы получите таймауты, SERVFAIL и «плавающий» резолвинг.
Диагностический критерий: кластерные имена резолвятся стабильно, а внешние — с задержками или периодическими ошибками. Тогда смотрим именно forward и достижимость upstream.
Split-DNS реализован «в Pod’ах», а не централизованно
Когда часть Pod’ов резолвит корпоративные зоны через один DNS, часть — через другой, а часть — через публичный, вы ловите не только задержки, но и разные ответы на одно и то же имя. Централизация на CoreDNS обычно проще: один конфиг, одна точка диагностики.
AAAA/IPv6-запросы и неожиданные таймауты
Если сеть или upstream не готовы к IPv6, но клиенты и CoreDNS активно спрашивают AAAA, возможны задержки на таймаутах. Для проверки сравнивайте отдельно:
kubectl exec -it deploy/app -- dig +time=1 +tries=1 A example.com
kubectl exec -it deploy/app -- dig +time=1 +tries=1 AAAA example.com
Практический чек-лист при инциденте Kubernetes DNS
- Проверить
/etc/resolv.confв проблемном Pod:nameserver,search,ndots. - Проверить резолвинг FQDN сервисов кластера:
kubernetes.default.svc.cluster.local. - Сравнить резолвинг коротких имён и FQDN внутри кластера.
- Проверить внешний домен из проблемного Pod и отдельно из Pod’а CoreDNS.
- Посмотреть CoreDNS Corefile: наличие
kubernetes,forward, настройки зон для split-DNS. - Проверить, что upstream доступен из namespace
kube-systemи что это не loopback stub.
Итоги: как держать DNS в Kubernetes предсказуемым
Резолвинг в Kubernetes складывается из двух логик: клиентской (в Pod, через resolv.conf, ndots и search) и серверной (CoreDNS: плагины, кеш и upstream). Большинство «странностей» появляется на стыке.
Если у вас много внешнего трафика и чувствительность к латентности, уделите внимание ndots, используйте FQDN там, где это возможно, и обеспечьте стабильный upstream (лучше контролируемый кэширующий резолвер, чем случайные настройки ноды). А если нужен split-DNS, делайте его на уровне CoreDNS, а не «по месту» в отдельных Pod.
В связке с DNS часто всплывают вопросы доменов и управления зонами; по смежной теме может пригодиться материал: перенос домена и EPP-код: что проверить заранее в DNS.


