- PVSM.RU - https://www.pvsm.ru -

Как работает DNS в Linux. Часть 4: DNS в контейнерах

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

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

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

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

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

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

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

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

А теперь перейдем к контейнерам, где последовательно рассмотрим платформы 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 [4]

# Вариант 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 описано в этой статье [5] на Хабре. 

Основными недостатками 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 [6]

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

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

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

https://github.com/containers/podman/issues/15972 [7]

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

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

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

https://github.com/containers/podman/issues/10570 [8]

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

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

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

https://github.com/containers/netavark/issues/855 [9]

Исправлено в версиях 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 [10]. для пропуска поисковых доменов.

Настройка 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 в Linux. Часть 4: DNS в контейнерах - 2

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

Корпоративные окружения предъявляют особые требования к 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 [11]

Docker networking overview [12]

Podman rootless tutorial [13]

Podman network stack [14]

K8S DNS for Services and Pods [15]

K8S CoreDNS [16]

K8S NodeLocal DNSCache [17]

K8S Debugging DNS [18]

CoreDNS [19]

Автор: rearranged

Источник [20]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/linux/434584

Ссылки в тексте:

[1] Как работает DNS в Linux. Часть 1: от getaddrinfo до resolv.conf: https://habr.com/ru/companies/k2tech/articles/919194/

[2] Как работает DNS в Linux. Часть 2: все уровни DNS-кэширования: https://habr.com/ru/companies/k2tech/articles/925736/

[3] Как работает DNS в Linux. Часть 3:  Разбираемся с resolv.conf, systemd-resolved, NetworkManager и другими: https://habr.com/ru/companies/k2tech/articles/941284/

[4] https://github.com/moby/moby/issues/23910: https://github.com/moby/moby/issues/23910

[5] этой статье: https://habr.com/ru/companies/k2tech/articles/855140/

[6] https://access.redhat.com/solutions/7094253: https://access.redhat.com/solutions/7094253

[7] https://github.com/containers/podman/issues/15972: https://github.com/containers/podman/issues/15972

[8] https://github.com/containers/podman/issues/10570: https://github.com/containers/podman/issues/10570

[9] https://github.com/containers/netavark/issues/855: https://github.com/containers/netavark/issues/855

[10] www.google.com: http://www.google.com

[11] Docker rootless mode: https://docs.docker.com/engine/security/rootless/

[12] Docker networking overview: https://docs.docker.com/engine/network/

[13] Podman rootless tutorial: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md

[14] Podman network stack: https://www.redhat.com/en/blog/podman-new-network-stack

[15] K8S DNS for Services and Pods: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/

[16] K8S CoreDNS: https://kubernetes.io/docs/tasks/administer-cluster/coredns/

[17] K8S NodeLocal DNSCache: https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/

[18] K8S Debugging DNS: https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/

[19] CoreDNS: https://coredns.io/plugins/kubernetes/

[20] Источник: https://habr.com/ru/companies/k2tech/articles/959004/?utm_source=habrahabr&utm_medium=rss&utm_campaign=959004