DNS в контейнерных средах представляет собой принципиально иную парадигму по сравнению с традиционными виртуальными машинами или физическими серверами. Технология контейнеризации формирует специфические задачи разрешения имён, обусловленные тремя ключевыми аспектами: изоляцией, динамической природой среды и требованиями масштабируемости.

Изоляция означает, что каждый контейнер существует в собственном сетевом пространстве имён с индивидуальной DNS-конфигурацией, что требует специальных механизмов для обеспечения связности. Динамичность подразумевает, что сервисы постоянно создаются и уничтожаются, IP-адреса меняются, а традиционные статические DNS-записи становятся неэффективными. Масштабирование - необходимость обработки тысяч DNS-запросов в секунду без единых точек отказа, что требует распределённых и отказоустойчивых архитектур.

В отличие от традиционных сред, где DNS-конфигурация наследуется от хоста, контейнерные платформы создают собственные DNS-экосистемы с автоматическим обнаружением сервисов, динамическим обновлением записей и сложными политиками разрешения имен. Эта сложность усугубляется необходимостью поддержки как “внутрикластерного”, так и внешнего разрешения имён, обеспечения производительности при высоких нагрузках и интеграции с существующими корпоративными DNS-инфраструктурами

Каждая контейнерная платформа — Docker, Podman, Kubernetes — реализует собственную DNS-архитектуру со специфическими особенностями, преимуществами и подводными камнями. Понимание этих различий критически важно для построения надежных и производительных контейнерных инфраструктур. С чем мы и попробуем разобраться в этой статье.

Это четвертая статья в цикле разбора механизмов разрешения имен и работы DNS. Предыдущие три можно найти по этим ссылкам:

- Как работает DNS в Linux. Часть 1: от getaddrinfo до resolv.conf

- Как работает DNS в Linux. Часть 2: все уровни DNS-кэширования

- Как работает DNS в Linux. Часть 3:  Разбираемся с resolv.conf, systemd-resolved, NetworkManager и другими

А теперь перейдем к контейнерам, где последовательно рассмотрим платформы Docker, Podman и Kubernetes.

Docker: эволюция DNS от простоты к сложности

Docker использует встроенный DNS-прокси для каждой пользовательской сети, что обеспечивает автоматическое разрешение имен контейнеров. Этот прокси работает по адресу 127.0.0.11 и выполняет две основные функции: разрешает имена контейнеров в рамках одной сети и пересылает внешние запросы на DNS-серверы, настроенные в daemon.json или resolv.conf хоста.

Архитектура DNS в rootful режиме

В классическом rootful режиме Docker создает стандартную конфигурацию DNS для контейнеров:

# Типичный /etc/resolv.conf в контейнере
nameserver 127.0.0.11
options ndots:0

DNS-прокси Docker принимает запросы от контейнеров и обрабатывает их следующим образом:

  • внутренние имена: разрешает имена контейнеров и сервисов в рамках пользовательских сетей Docker

  • внешние имена: пересылает запросы на DNS-серверы, указанные в конфигурации демона или хостовом resolv.conf

  • область действия: работает во всех типах сетей Docker, включая bridge-сеть по умолчанию, но с разной функциональностью:

  • В пользовательских сетях предоставляет встроенное разрешение имен контейнеров

  • В default bridge-сети автоматическое разрешение имен контейнеров НЕ работает; доступен только устаревший механизм --link, который добавляет записи в /etc/hosts

Глобальные настройки Docker можно задать в /etc/docker/daemon.json:

{
  "dns": ["<dns1>", "<dns2>"],
  "dns-opts": ["use-vc", "rotate"],
  "dns-search": ["example.com"]
}

Особенности rootless режима

Rootless Docker кардинально меняет сетевую архитектуру. Вместо прямого доступа к сетевому стеку хоста используется user namespace с изолированной сетевой средой. Основные отличия включают:

Сетевой стек: используется slirp4netns или RootlessKit для эмуляции сетевого стека в пользовательском пространстве.

DNS-конфигурация: контейнеры получают DNS-сервер 10.0.2.3 вместо традиционного 127.0.0.11.

```bash
# В rootless контейнере
nameserver 10.0.2.3
```

Проблемы производительности: rootless режим показывает значительно худшую сетевую производительность из-за накладных расходов на трансляцию пакетов.

Проброс source IP: по умолчанию source IP адреса не передаются корректно, что критично для сетевых сервисов вроде DNS или прокси. Для решения требуется специальная конфигурация:

```bash
# В ~/.config/systemd/user/docker.service.d/override.conf
[Service]
Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns"
```

Известные проблемы Docker DNS

Конфликт с systemd-resolved: автоматическая подстановка 127.0.0.53 может нарушить работу DNS в контейнерах.

Решение: Настройка /etc/docker/daemon.json с явными DNS-серверами:

{
  "dns": ["<dns1>", "<dns2>"],
  "dns-opts": [],
  "dns-search": []
}

Проблемы с VPN: DNS-запросы могут не проходить через VPN-туннель, что приводит к утечкам DNS.

Решение: Принудительная маршрутизация DNS через VPN:

# Использование VPN DNS-серверов
docker run --dns 10.8.0.1 --dns-search vpn.local myimage
# Или также глобально в daemon.json для всех контейнеров
{
  "dns": ["10.8.0.1"]  # IP DNS-сервера в VPN сети
}

Устаревшие копии resolv.conf: изменения хостового файла не влияют на запущенные контейнеры до их перезапуска.

https://github.com/moby/moby/issues/23910

# Вариант 1: Перезапуск контейнеров после изменения хостового DNS

docker restart $(docker ps -q)

# Вариант 2: Монтирование resolv.conf как volume (обновления в реальном времени)

docker run -v /etc/resolv.conf:/etc/resolv.conf:ro myimage

# Вариант 3: Использование --dns флага с явными серверами, или также указанием в daemon.json

Podman: от CNI к современному Netavark

Podman представляет наиболее динамичную эволюцию DNS-архитектур в контейнерном мире, совершив переход от устаревшего CNI к современному решению Netavark/aardvark-dns. Эта трансформация отражает общую тенденцию индустрии к более производительным и функциональным DNS-решениям.

Архитектура CNI (Legacy режим)

До версии 4.0 Podman использовал CNI (Container Network Interface) с плагином dnsname для разрешения имен контейнеров. Эта архитектура включала:

  • dnsmasq как DNS-сервер: для каждой CNI-сети создавался отдельный экземпляр dnsmasq

  • файловое хранилище записей: DNS-записи хранились в файлах в директории /run/containers/cni/dnsname или $XDG_RUNTIME_DIR/containers/cni/dnsname

  • ограниченную функциональность: поддерживалось только базовое разрешение A-записей без PTR и других типов записей

Конфигурация CNI сети с dnsname выглядела следующим образом:

{
    "cniVersion": "0.4.0",
    "name": "cni-bridge-network",
    "plugins": [
      {
        "type": "bridge",
        "bridge": "cni0"
      },
      {
        "type": "dnsname",
        "domainName": "foobar.com",
        "capabilities": {
            "aliases": true
        }
      }
    ]
}

Максимально подробн�� про работу Podman с бэкендом CNI описано в этой статье на Хабре. 

Основными недостатками CNI подхода были низкая производительность dnsmasq при высоких нагрузках, отсутствие поддержки современных DNS-функций, сложность конфигурации и обслуживания и ограниченная интеграция с современными контейнерными оркестраторами.

Переход к Netavark и aardvark-dns

Начиная с Podman 4.0 по умолчанию используется новый сетевой стек Netavark с DNS-сервером aardvark-dns. Это революционное изменение принесло множество улучшений:

  • Aardvark-dns как авторитетный сервер: написанный на Rust авторитетный DNS-сервер для A/AAAA записей контейнеров

  • поддержка PTR-записей: автоматическое создание обратных DNS-записей для упрощения диагностики

  • улучшенная производительность: значительно более высокая производительность по сравнению с dnsmasq

  • лучшая поддержка IPv6: особенно в области NAT и port forwarding

Типичная конфигурация Netavark сети:

{
    "name": "mynetwork",
    "id": "3977b0c90383b8460b75547576dba6ebcf67e815f0ed0c4b614af5cb329ebb83",
    "driver": "bridge",
    "network_interface": "podman1",
    "created": "2022-09-06T12:08:12.853219229Z",
    "subnets": [{
        "subnet": "10.89.0.0/24",
        "gateway": "10.89.0.1"
    }],
    "ipv6_enabled": false,
    "internal": false,
    "dns_enabled": true,
    "ipam_options": {
        "driver": "host-local"
    }
}

В отличие от CNI, Netavark предоставляет нативную интеграцию с container runtime, автоматическое управление DNS-записями при создании и удалении контейнеров и современные возможности мониторинга и отладки.

# Проверить используемый сетевой бэкенд
podman info --format '{{.Host.NetworkBackend}}'

DNS в rootless Podman

Rootless режим Podman формирует особые требования для DNS-архитектуры, поскольку отсутствуют привилегии для создания полноценных bridge-сетей. 

slirp4netns: традиционный rootless networking

В классическом rootless режиме с slirp4netns Podman создаёт изолированную сетевую среду:

# DNS в rootless контейнере (slirp4netns)
nameserver 10.0.2.3
options edns0 trust-ad
search

Архитектура DNS в slirp4netns:

• DNS-сервер 10.0.2.3 — встроенный DNS-прокси slirp4netns

• Автоматическая трансляция DNS-запросов через user-mode NAT

• Изолированное пространство имён с собственным routing table

• Предсказуемая, но ограниченная сетевая конфигурация

Ключевые ограничения slirp4netns:

• Невозможность использования Netavark в полном объёме из-за отсутствия привилегий

• Дополнительный overhead на трансляцию пакетов в user-space

• Проблемы с разрешением локальных имён хоста

• Ограниченная поддержка IPv6 DNS-запросов

• Производительность значительно хуже native networking

Революция с pasta/passt: новый стандарт rootless DNS

Начиная с Podman 5.3, pasta/passt становится стандартным сетевым бэкендом для новых установок во многих дистрибутивах, вытесняя slirp4netns, кардинально изменяя подход к DNS.

# DNS в rootless контейнере (pasta/passt)
nameserver 192.168.1.1    # Реальный DNS хоста
nameserver <dns1>        # Upstream DNS с хоста
search home.local         # Search domains с хоста
options edns0 trust-ad

Pasta (Pack A Subtle Tap Abstraction) работает на основе passt (Plug A Simple Socket Transport) — современного сетевого драйвера, который обеспечивает “quasi-native” сетевую связность для виртуальных машин и контейнеров в пользовательском режиме без требования привилегий. 

Ключевое отличие от slirp4netns — pasta не использует Network Address Translation (NAT) по умолчанию и копирует IP-адреса с основного интерфейса хоста в namespace контейнера.

Translation layer architecture: pasta реализует слой трансляции между виртуальным Layer-2 сетевым интерфейсом и нативными Layer-4 сокетами (TCP, UDP, ICMP) на хосте. Это создаёт иллюзию, что процессы приложений в контейнере выполняются на локальном хосте с сетевой точки зрения.

Встроенные сетевые сервисы: pasta включает собственные реализации ARP, DHCP, NDP, и DHCPv6, предоставляющие контейнеру сетевую конфигурацию, максимально близкую к нативной конфигурации хоста.

Архитектурные улучшения:

  • Использует IP-адрес хоста вместо предопределённого container IP (10.0.2.x)

  • Использует имя сетевого интерфейса с хоста вместо стандартного tap0

  • Использует gateway адрес с хоста вместо собственного gateway с NAT

Ключевые преимущества pasta для DNS:

Прямое наследование DNS-конфигурации: pasta копирует /etc/resolv.conf хоста в контейнер, обеспечивая идентичную DNS-конфигурацию. Это кардинально отличается от slirp4netns, который использует собственный DNS-прокси.

Встроенные DNS-сервисы: pasta включает собственные реализации:

  • DHCP-сервер для автоматической настройки DNS

  • DNS forwarder для эффективной пересылки запросов

  • ARP resolver для разрешения локальных имён

  • NDP (IPv6) для modern dual-stack environments

Quasi-native DNS resolution: pasta имитирует выполнение DNS-запросов напрямую на хосте, устраняя промежуточные proxy layers.

Управление DNS в Netavark

Netavark предоставляет современные возможности управления DNS, которые по-разному взаимодействуют с rootless сетевыми бэкендами.

В rootful режиме (полная функциональность Netavark)

# Создание сети с кастомными DNS-настройками
podman network create --driver bridge \
  --dns <dns2> \
  --dns <dns1> \
  --dns-search company.local \
  custom-dns-net

# Динамическое обновление DNS для существующей сети
podman network update custom-dns-net --dns-add 9.9.9.9
podman network update custom-dns-net --dns-search-add internal.local

# Доступ к aardvark-dns с хоста
nslookup webapp.dns.podman 10.89.0.1

В rootless режиме с slirp4netns

slirp4netns не поддерживает полную функциональность Netavark. DNS-настройки ограничены:

• Невозможность создания кастомных bridge-сетей с DNS-настройками

• Aardvark-dns недоступен из-за изоляции user namespace

• DNS-конфигурация определяется исключительно slirp4netns

# Ограниченные возможности в slirp4netns режиме
podman run --dns <dns1> --dns-search company.local alpine
# DNS настройки применяются к /etc/resolv.conf, но без aardvark-dns

В rootless режиме с pasta/passt

Pasta обеспечивает расширенную совместимость с Netavark:

• Поддержка кастомных DNS-настроек через pasta options

• Частичная поддержка network management команд

• Автоматическая интеграция с хостовой DNS-конфигурацией

# Эмуляция slirp4netns поведения в pasta
podman run --network pasta:-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3 alpine

# Кастомизация DNS в pasta режиме
podman run --dns <dns2> --dns-search internal.local \
  --network pasta:--map-guest-addr=172.16.1.100 alpine

Известные проблемы Podman DNS

Aardvark-DNS не может разрешить имена

Сбои разрешения имен в Podman 4.x с Netavark/aardvark-dns

https://access.redhat.com/solutions/7094253

Решается обновлением до последней версии и перенастройкой сетей.

Долгие задержки при разрешении DNS между контейнерами 

DNS-запросы между peer pods зависали на длительное время в версиях aardvark-dns 1.1.x-1.5.x.

https://github.com/containers/podman/issues/15972

Исправлено в версиях 1.6+

DNS resolution по умолчанию использует 8.8.8.8 в rootful режиме

Podman rootful CNI DNS resolution неправильно настраивал DNS-серверы по умолчанию.

https://github.com/containers/podman/issues/10570

Решено в GitHub Issue containers/podman#10570

DNS настройки игнорируются в podman run

DNS параметры, заданные в podman run, не применялись к /etc/resolv.conf в ранних версиях Netavark.

https://github.com/containers/netavark/issues/855

Исправлено в версиях aardvark-dns 1.6.0+ и netavark 1.6.0+  

Podman демонстрирует наиболее активную эволюцию DNS-архитектур среди контейнерных платформ, с успешным переходом от CNI к Netavark в rootful режиме и революционным внедрением pasta/passt для rootless containers. Большинство критических DNS-проблем решены в последних версиях, что делает Podman привлекательной альтернативой Docker для production deployment с хорошей производительностью DNS и расширенными возможностями управления.

Я пытаюсь объяснить коллеге, почему DNS не работает в его rootless Podman контейнере
Я пытаюсь объяснить коллеге, почему DNS не работает в его rootless Podman контейнере

Kubernetes: сложность корпоративного DNS

Kubernetes представляет наиболее сложную и функциона��ьную DNS-архитектуру среди контейнерных платформ. Центральным компонентом является CoreDNS — модульный DNS-сервер, обслуживающий домены *.cluster.local и пересылающий остальные запросы на внешние DNS-серверы.

Базовая архитектура CoreDNS

CoreDNS развертывается как Deployment в namespace kube-system и доступен через Service с именем kube-dns:

# Типичная DNS-конфигурация в поде
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

IP-адрес 10.96.0.10 соответствует ClusterIP сервиса kube-dns:

$ kubectl get svc -n kube-system -l k8s-app=kube-dns
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP

CoreDNS настраивается через ConfigMap с Corefile — конфигурационным файлом, определяющим поведение DNS:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . <dns1> <dns2> {
            except cluster.local in-addr.arpa ip6.arpa
        }
        cache 30
        loop
        reload
        loadbalance
    }

Основные плагины CoreDNS:

  • kubernetes: обрабатывает запросы для cluster.local

  • forward: пересылает внешние запросы на upstream DNS

  • cache: кэширует ответы на 30 секунд

  • health: предоставляет health checks на порту 8080

  • prometheus: экспортирует метрики на порту 9153

Kubernetes поддерживает несколько DNS Policy для управления разрешением имен в подах. Политики - это параметр конфигурации, который определяет, откуда и как поды получают настройки DNS для разрешения имен. Это критически важная настройка, влияющая на способность подов обращаться как к кластерным сервисам, так и к внешним ресурсам.

  • ClusterFirst (по умолчанию): использует кластерный DNS (CoreDNS).

  • ClusterFirstWithHostNet: комбинирует кластерный DNS с hostNetwork.

  • Default: наследует DNS-конфигурацию хоста.

  • None: требует ручной настройки DNS.

Пример пода с ручной настройкой DNS для dnsPolicy: None:

spec:
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - <dns1>
      - <dns2>
    searches:
      - ns1.svc.cluster.local
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

Диагностика DNS проблем в Kubernetes

Kubernetes предоставляет специальные инструменты для диагностики DNS:

# Развертывание диагностического пода
kubectl apply -f https://k8s.io/examples/admin/dns/dnsutils.yaml
# Создаёт pod 'dnsutils' с nslookup, dig, host и другими DNS-утилитами

# Тестирование разрешения кластерных имен
kubectl exec -i -t dnsutils -- nslookup kubernetes.default
# Должен вернуть ClusterIP сервиса kubernetes (обычно 10.96.0.1)
# Если не работает — проблема с CoreDNS или kube-dns Service

# Проверка внешних имен
kubectl exec -i -t dnsutils -- nslookup google.com
# Проверяет способность CoreDNS пересылать запросы на внешние DNS
# Если не работает — проблема с upstream DNS или network policies

Типичные ошибки, известные проблемы и решения

SERVFAIL: CoreDNS недоступен или перегружен — проверить статус подов CoreDNS.

# определить DNS-компоненты
kubectl get pods -n kube-system -l k8s-app=coredns
kubectl get pods -n kube-system -l k8s-app=kube-dns
# проверить статус сервиса DNS
kubectl get svc -n kube-system -l k8s-app=kube-dns
kubectl get svc -n kube-system -l k8s-app=coredns
# Проверка логов
kubectl logs -n kube-system -l k8s-app=coredns

NXDOMAIN: неполные или устаревшие DNS-записи — проверить конфигурацию сервисов.

kubectl get endpoints <service-name> -n <namespace>
kubectl describe service <service-name> -n <namespace>
kubectl get pods -n <namespace> -l <selector-from-service>

Медленные ответы: возможно это проблемы с ndots или перегрузка CoreDNS.

# Измерить время DNS-запроса
kubectl exec dnsutils -- sh -c "time nslookup google.com" 2>&1
# Проверить настройки resolv.conf (ndots)
kubectl exec dnsutils -- cat /etc/resolv.conf

Поды с hostNetwork: true используют сетевое пространство имен хоста, что приводит к проблемам с DNS.

Суть проблемы: под не может разрешать кластерные сервисы, так как использует хостовую DNS-конфигурацию.

Решение: использование dnsPolicy: ClusterFirstWithHostNet:

spec:
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet

Альтернативное решение - iptables правила для перенаправления трафика:

# Перенаправление DNS-трафика на ClusterIP
iptables -t nat -A OUTPUT -p tcp --dport 53 -d external-ip \
    -j DNAT --to-destination cluster-ip:53

Одна из ключевых проблем Kubernetes DNS — параметр ndots:5, который заставляет резолвер проверять неполные имена (содержащие меньше чем 5 точек) сначала среди внутренних доменов кластера, и только потом делать запрос на внешний DNS:

# При запросе www.google.com с ndots:5 происходят попытки:
www.google.com.default.svc.cluster.local
www.google.com.svc.cluster.local
www.google.com.cluster.local
www.google.com.  # только потом прямой запрос

Это приводит к множественным DNS-запросам и значительным задержкам. 

Решения включают:

Использование FQDN с точкой: www.google.com. для пропуска поисковых доменов.

Настройка ndots для отдельных подов:

spec:
  dnsConfig:
    options:
    - name: ndots
      value: "1"

Оптимизация CoreDNS для улучшения производительности и отказоустойчивости

CoreDNS в Kubernetes часто становится узким местом при большом количестве сервисов и внешних DNS-запросов. Понимание тонкостей настройки CoreDNS становится критически важным для поддержания стабильности production кластеров.

Кэширование

По умолчанию плагин cache установлен на 30 секунд. Для крупных кластеров можно увеличить TTL и размеры кэша:

cache {
    success 10000 60   # до 10k успешных ответов, TTL 60s
    denial 10000 15    # NXDOMAIN до 10k, TTL 15s
}

Success cache: увеличение до 60-120 секунд снижает нагрузку на upstream на 70-80%

Denial cache: NXDOMAIN должны кэшироваться меньше (10-15 секунд)

Prefetch mechanism: автоматическое обновление записей за 10% до истечения TTL.

Политика форвардинга

В плагине forward можно выбирать стратегию обращения к upstream DNS:

random — распределяет запросы равномерно по серверам (улучшает балансировку);

sequential — всегда опрашивает сервера в порядке списка (по умолчанию).

Для кластеров с несколькими внешними DNS рекомендуется использовать random:

forward . <dns1> <dns2> {
    policy random
}

Протокольная оптимизация

Директива prefer_udp снижает overhead при большом числе коротких запросов.

forward . <dns1> <dns2> {
    prefer_udp
    max_concurrent 1000
}

Указание force_tcp может быть полезно, если есть проблемы с фрагментацией UDP пакетов (часто в облаках и VPN).

Масштабирование

Горизонтальное масштабирование CoreDNS — не опция, а необходимость для production кластеров. Типичная конфигурация с 1-2 репликами совершенно неадекватна при нагрузке в десятки тысяч DNS-запросов в секунду. Каждая реплика CoreDNS способна обрабатывать 5000-10000 QPS до деградации производительности, а в кластере на 100+ нод и 1000+ подов пиковая нагрузка легко достигает 50000-100000 QPS, особенно во время:

  • Массовых deployments, когда сотни новых подов одновременно начинают разрешать DNS-имена сервисов

  • Rolling updates приложений, создающих временные пики DNS-активности

  • Автомасштабирования приложений на основе HPA

  • Startup burst — когда приложения делают множество DNS-запросов при инициализации

Недостаточное количество CoreDNS реплик приводит к:

  • Высокой латентности DNS (>100ms вместо <10ms)

  • Timeouts и SERVFAIL ответам

  • Перегрузке CPU и memory на CoreDNS подах

  • Каскадным отказам приложений, которые не могут обнаружить зависимые сервисы

Ручное масштабирование CoreDNS

Простейший способ увеличить количество реплик CoreDNS:Критически важно распределить CoreDNS реплики по разным нодам для обеспечения отказоустойчивости. Если все реплики CoreDNS находятся на одной ноде, ее отказ приведет к полному DNS outage.

# Anti-affinity для распределения по нодам
kubectl patch deployment coredns -n kube-system -p '
{
  "spec": {
    "template": {
      "spec": {
        "affinity": {
          "podAntiAffinity": {
            "preferredDuringSchedulingIgnoredDuringExecution": [{
              "weight": 100,
              "podAffinityTerm": {
                "labelSelector": {
                  "matchLabels": {"k8s-app": "kube-dns"}
                },
                "topologyKey": "kubernetes.io/hostname"
              }
            }]
          }
        }
      }
    }
  }
}'

При масштабировании критически важно настроить PodDisruptionBudget для предотвращения одновременного удаления слишком многих CoreDNS реплик.

NodeLocal DNSCache: современное решение

NodeLocal DNSCache представляет современный подход к оптимизации DNS в Kubernetes. Это DaemonSet, который запускает DNS-кэш на каждой ноде кластера.

Преимущества NodeLocal DNSCache:

  • Снижение среднего времени DNS-разрешения (Поды обращаются к локальному кэшу на той же ноде вместо прохождения через kube-dns Service)

  • Устранение conntrack записей для DNS-соединений (Прямое обращение к локальному кэшу избегает iptables DNAT правил и connection tracking)

  • Прямое обращение к Cloud DNS, минуя kube-dns для внешних запросов (Это снижает нагрузку на центральный CoreDNS и улучшает латентность.)

  • Автоматическое наследование stub domains и upstream nameservers

Архитектура: поды обращаются к DNS-кэшу на той же ноде, избегая iptables DNAT правил и connection tracking.

Конфигурация NodeLocal DNSCache:

apiVersion: v1
kind: ConfigMap
metadata:
  name: node-local-dns
  namespace: kube-system
data:
  Corefile: |
    cluster.local:53 {
        errors
        cache {
            success 9984 30
            denial 9984 5
        }
        reload
        loop
        bind 169.254.20.25 10.96.0.10
        forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
        }
        prometheus :9253
        health 169.254.20.25:8080
    }

NodeLocal DNSCache работает на специальном link-local IP-адресе 169.254.20.25 на каждой ноде. Поды автоматически настраиваются для использования этого адреса как primary nameserver.

Кастомизация для корпоративных сред

Корпоративные окружения предъявляют особые требования к DNS-инфраструктуре: интеграция с существующими системами разрешения имён, разделение внутренних и внешних зон, соблюдение политик безопасности и compliance требований. CoreDNS предоставляет гибкие механи��мы для реализации этих требований через stub domains, split-horizon DNS, network policies и encrypted DNS.

CoreDNS поддерживает гибкую настройку upstream серверов и stub domains для интеграции с корпоративными DNS-инфраструктурами:

# Конфигурация для корпоративного DNS
consul.local:53 {
    errors
    cache 30
    forward . 10.150.0.1
}

# Принудительное использование конкретного upstream
forward . 172.16.0.1  # вместо /etc/resolv.conf

Split-horizon DNS: различные ответы для внутренних и внешних запросов:

# Пример split-brain конфигурации в CoreDNS
internal.company.com:53 {
    hosts {
        192.168.1.10 api.internal.company.com
        fallthrough
    }
    forward . 192.168.1.1  # внутренний DNS
}

external.company.com:53 {
    hosts {
        203.0.113.10 api.external.company.com
        fallthrough
    }
    forward . <dns1>     # внешний DNS
}

Ограничение доступа к DNS-серверам: использование network policies.

Kubernetes Network Policies позволяют реализовать fine-grained контроль доступа к CoreDNS, ограничивая, какие поды могут выполнять DNS-запросы и откуда CoreDNS может получать запросы.

Пример базовой NetworkPolicy для защиты CoreDNS:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
meta
  name: coredns-network-policy
  namespace: kube-system
spec:
  podSelector:
    matchLabels:
      k8s-app: kube-dns
  policyTypes:
  - Ingress
  ingress:
  # Разрешить DNS-запросы от всех подов в кластере
  - from:
    - namespaceSelector: {}
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53
 # Разрешить health-checks (для liveness/readiness проб)
  - from:
    - namespaceSelector: {}
    ports:
    - protocol: TCP
      port: 8080  # health
    - protocol: TCP
      port: 8181  # ready
  # Разрешить Prometheus метрики от мониторинга
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    ports:
    - protocol: TCP
      port: 9153

Мониторинг и логирование DNS

Эффективный мониторинг CoreDNS критически важен для обеспечения стабильности всего Kubernetes кластера. Проблемы с DNS могут привести к каскадным отказам, когда сервисы не могут обнаруживать друг друга, что делает систему мониторинга первой линией защиты от серьезных инцидентов.

CoreDNS предоставляет богатые возможности мониторинга:

Prometheus метрики: доступны на порту 9153 для каждого пода CoreDNS.

Ключевые метрики включают:

- Время ответа DNS

- Процент успешных запросов

- Cache hit ratio

- Количество upstream запросов

Логирование запросов:

добавление плагина log в Corefile для детального логирования:

Corefile: |
  .:53 {
      log  # добавить для логирования всех запросов
      errors
      # остальная конфигурация
  }

Health checks и автоматическое восстановление

CoreDNS предоставляет два endpoint для health checking:

Health endpoint (/health:8080)

# пример liveness probe
livenessProbe:
  httpGet:
    path: /ready
    port: 8181
    scheme: HTTP
  initialDelaySeconds: 30
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 5

Ready endpoint (/ready:8181)

# пример readiness probe
readinessProbe:
  httpGet:
    path: /health
    port: 8080
    scheme: HTTP
  initialDelaySeconds: 10
  periodSeconds: 10
  timeoutSeconds: 5
  successThreshold: 1
  failureThreshold: 3

Интеграция с облачными платформами

Kubernetes DNS-архитектура в production среде редко существует изолированно. Интеграция с облачными DNS-сервисами, системами автоматического управления записями и service mesh решениями создаёт комплексную экосистему, обеспечивающую высокую доступность, безопасность и автоматизацию DNS-управления.

Cloud DNS интеграция

Использование управляемых DNS-сервисов облачных провайдеров. Google Cloud DNS, Amazon Route 53 и Azure DNS предоставляют высокодоступные и масштабируемые решения для внешних запросов.

External DNS

External DNS выводит управление DNS в Kubernetes на новый уровень, автоматически создавая и обновляя DNS-записи на основе Kubernetes ресурсов. Это устраняет необходимость ручного управления DNS и обеспечивает полную синхронизацию состояния кластера с внешними DNS-провайдерами.

Архитектура и принцип работы

External DNS работает как controller, который:

  • Мониторит Kubernetes ресурсы (Service, Ingress)

  • Извлекает DNS-аннотации из ресурсов

  • Синхронизирует записи с внешним DNS-провайдером через API

  • Поддерживает актуальность записей при изменениях

Service mesh интеграция

Istio использует Envoy proxies для перехвата всего трафика, включая DNS-запросы. В такой архитектуре DNS становится частью service mesh control plane.

Архитектура Istio DNS:

  • Sidecar Envoy перехватывает DNS-запросы от приложений

  • Pilot предоставляет service registry через xDS API

  • DNS proxy в Envoy разрешает имена на основе Istio service registry

  • Внешние домены по-прежнему разрешаются через CoreDNS

Практические рекомендации и выводы

Рекомендации по платформам

Если вы используете Docker — обратите внимание на различия между rootful и rootless режимами. 

В rootful режиме 

  • мониторьте стабильность DNS-прокси 127.0.0.11

  • настройте альтернативные DNS-серверы в daemon.json

  • при использовании systemd-resolved создайте симлинк на /run/systemd/resolve/resolv.conf вместо stub-resolver

В rootless режиме 

  • учитывайте ограничения производительности из-за slirp4netns

  • учитывайте, что DNS-сервер 10.0.2.3 является частью изолированного сетевого стека

Если переходите на современный Podman

  • мигрируйте с CNI на Netavark/aardvark-dns для улучшения производительности. 

  • Используйте возможности динамического управления DNS через podman network update

В rootless режиме 

  • учитывайте ограничения slirp4netns

  • держите в голове, что Podman 5.3+ использует pasta по умолчанию, но при проблемах с pasta можно вернуться к slirp4netns через containers.conf

Если используете Kubernetes

  • обязательно рассмотрите внедрение NodeLocal DNSCache для снижения задержек. 

  • оптимизируйте ndots для приложений, делающих много внешних запросов. 

  • настройте правильную dnsPolicy для подов с hostNetwork. 

  • мониторьте производительность CoreDNS и планируйте масштабирование

Заключение

DNS в контейнерных средах представляет собой многослойную архитектуру со значительными различиями между платформами. Docker обеспечивает простоту использования с встроенным DNS-прокси, но требует особого внимания в rootless режиме. Podman демонстрирует активную эволюцию от устаревшего CNI к современному Netavark с aardvark-dns, обеспечивая лучшую производительность и функциональность. Kubernetes предлагает наиболее сложную и мощную DNS-систему с CoreDNS, но требует глубокого понимания особенностей конфигурации и оптимизации.

Контейнерные DNS-архитектуры представляют собой быстро развивающуюся область с постоянными улучшениями производительности, безопасности и функциональности. Понимание особенностей каждой платформы и применение современных практик обеспечивает надежную и масштабируемую DNS-инфраструктуру для контейнерных приложений. Инвестиции в правильную архитектуру DNS окупаются через улучшение производительности приложений, снижение операционных затрат и повышение общей надежности системы.

Рекомендуемая литература

Docker rootless mode

Docker networking overview

Podman rootless tutorial

Podman network stack

K8S DNS for Services and Pods

K8S CoreDNS

K8S NodeLocal DNSCache

K8S Debugging DNS

CoreDNS

Комментарии (0)