Задача
Приложение в поде обращается к внешнему API — api.example.com. Запросы стабильно медленнее, чем те же запросы с самой ноды: разница в десятки миллисекунд на каждом обращении.
Внутрикластерные обращения при этом быстрые.
Вопрос: откуда берётся задержка именно на внешних именах?
Что кажется очевидным
«CoreDNS перегружен» или «сеть до внешнего резолвера медленная». Но метрики CoreDNS в норме, а нагрузка невысокая.
Как это работает на самом деле
Загляните в /etc/resolv.conf внутри любого пода:
kubectl exec -it my-pod -- cat /etc/resolv.confnameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5Здесь два интересных элемента.
search — список суффиксов, которые резолвер по очереди приписывает к неполному имени. Это то, что позволяет обращаться к сервису просто по имени backend, а не по полному backend.default.svc.cluster.local.
ndots:5 — правило, определяющее, считать ли имя «полным». Смысл такой: если в имени меньше 5 точек, сначала пробуем его с каждым суффиксом из search, и только потом — как есть.
Посчитаем точки в api.example.com — их две. Два меньше пяти, значит имя считается неполным, и резолвер начинает перебор:
api.example.com.default.svc.cluster.local→ NXDOMAINapi.example.com.svc.cluster.local→ NXDOMAINapi.example.com.cluster.local→ NXDOMAINapi.example.com→ успех
Четыре запроса вместо одного. А поскольку резолвер обычно спрашивает и A, и AAAA-записи, реальное число обращений удваивается — до восьми.
Отсюда и задержка: три бесполезных цикла «запрос → NXDOMAIN» перед полезным ответом. Внутрикластерные имена при этом быстрые, потому что для них первый же суффикс даёт попадание.
Ответ
Виноват ndots:5 вместе со списком search-доменов: внешнее имя с двумя точками считается неполным, поэтому резолвер сначала безуспешно перебирает все внутрикластерные суффиксы и только потом спрашивает настоящее имя.
Что с этим делать
Способ 1: точка в конце (самый простой)
Полностью квалифицированное имя — с точкой на конце — перебор отключает:
api.example.com. ← обратите внимание на завершающую точкуРезолвер считает такое имя абсолютным и спрашивает ровно один раз. Работает, если приложение корректно передаёт имя с точкой (некоторые библиотеки её обрезают).
Способ 2: настроить ndots для конкретного пода
spec:
dnsConfig:
options:
- name: ndots
value: "2"При ndots:2 имя api.example.com (две точки) уже считается полным — перебор не запускается. Внутрикластерные короткие имена (backend, 0 точек) продолжают работать через search.
Осторожно: если приложение использует имена вида backend.other-namespace (одна точка) — при ndots:2 они тоже перестанут дополняться суффиксами. Проверьте свои паттерны обращений.
Способ 3: полные имена внутри кластера
Если приложение всё равно ходит по полным именам, можно уменьшить и search-перебор:
backend.default.svc.cluster.local.Четыре точки + завершающая — резолвится сразу.
Дополнительно: кэш DNS на ноде
Для кластеров с интенсивным DNS-трафиком существует NodeLocal DNSCache — кэширующий агент на каждой ноде. Он снимает нагрузку с CoreDNS, убирает лишние сетевые прыжки и сглаживает всплески задержек. Для больших кластеров это стандартная практика.
Известная историческая проблема
Стоит знать про давнюю проблему с гонкой в conntrack при параллельных DNS-запросах (A и AAAA через один UDP-сокет), которая проявлялась как редкие пятисекундные задержки резолвинга. Симптом характерный: ровно 5 секунд (таймаут резолвера) на случайных запросах. Лечится настройкой single-request-reopen в dnsConfig, NodeLocal DNSCache или современным CNI. Если видите такие ровные пятисекундные всплески — это почти наверняка оно.
Диагностика
# Сколько запросов реально уходит
kubectl exec -it my-pod -- nslookup -debug api.example.com
# Замер: сравните время
kubectl exec -it my-pod -- time nslookup api.example.com
kubectl exec -it my-pod -- time nslookup api.example.com.
# Метрики и логи CoreDNS
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50Если второй замер (с точкой) заметно быстрее первого — диагноз подтверждён.