Если бы я был абсолютным злом, я бы сломал DNS по всему миру, взял попкорн и стал наблюдать за тем, как вы мучитесь.
Общие вопросы
Все примеры и допущения будут приводиться с учётом того, что кластер Kubernetes поставлен с использованием kubespray.
Для проверки работы DNS будем использовать образ infoblox/dnstools:
kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools
Домен для кластера
Домен кластера определяется при установке кластера. Посмотрите в kubespray inventory вашего кластера. Нас интересует файл group_vars/k8s-cluster/k8s-cluster.yml
# DNS configuration. # Kubernetes cluster name, also will be used as DNS domain cluster_name: cluster.local
При установке кластера мы не меняли значения этого поля.
Записи A и AAAA
Для services
В системе можно выделить два типа сервисов: обычные и headless.
Обычным сервисам присваивается ip адрес, headless нет.
При обращении к обычному сервису, к его IP адресу, происходит NAT преобразование «подставляющее» IP адрес пода (подов).
headless service по своей сути – это запись типа А в DNS, указывающая непосредственно на IP пода.
Типичное FQDN имя сервиса:
service-name.namespace-name.svc.cluster.local
Для pods
FQDN имя для простого пода:
pod-ip-address.namespace-name.pod.cluster.local
Например:
10-233-79-186.default.pod.cluster.local
DNS сервер кластера
Схема kubespray
Kubespray ставит следующие компоненты системы:
- coredns – основной DNS сервер, отвечающий за разрешение имен внутри кластера Kubernetes.
- nodelocaldns – кеширующий DNS сервер. По одному на каждую ноду кластера.
- dns-autoscaler – приложение, автоматически увеличивающее или уменьшающее количество подов coredns в кластере (https://github.com/kubernetes-sigs/cluster-proportional-vertical-autoscaler).
Основным DNS сервером является coredns (https://coredns.io). На него ложится все преобразования внутри кластера DNS.
Для доступа и распределения запросов между подами coredns, создан соответствующий сервис. На самом деле сервис один, хотя на схеме показаны три штуки. Но мы знаем, что сервис – это набор правил NAT преобразований на каждой ноде кластера.
Nodelocaldns выполнен в виде daemonSet. Это значит, что по одному экземпляру пода будет запущено на каждом кластере сети. Задача nodelocaldns – кеширование запросов от приложений, расположенных на ноде.
Файлы манифестов всех системных компонент кластера, установленного при помощи kubespray, можно найти в директории /etc/kubernetes. В том числе configMaps системы DNS: coredns-config.yml, nodelocaldns-config.yml.
Рассмотрим конфигурацию coredns.
.:53 { errors health { lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa } prometheus :9153 forward . /etc/resolv.conf { prefer_udp } cache 30 loop reload loadbalance }
Как видно из файла, сервер отвечает на все запросы на 53 порту. Модуль Kubernetes отвечает за домен cluster.local и обратные преобразования. Все остальные запросы будут пересылаться на DNS сервера, описанные в файле /etc/resolv.conf того компьютера, на котором запущен этот под.
Модуль кubernetes реализует концепцию Kubernetes DNS-Based Service Discovery (https://github.com/kubernetes/dns/blob/master/docs/specification.md).
Кеширующий DNS сервер тоже реализован с использованием coredns, но с другой конфигурацией.
cluster.local:53 { errors cache { success 9984 30 denial 9984 5 } reload loop bind 169.254.25.10 forward . 10.233.0.3 { force_tcp } prometheus :9253 health 169.254.25.10:9254 } in-addr.arpa:53 { errors cache 30 reload loop bind 169.254.25.10 forward . 10.233.0.3 { force_tcp } prometheus :9253 } ip6.arpa:53 { errors cache 30 reload loop bind 169.254.25.10 forward . 10.233.0.3 { force_tcp } prometheus :9253 } .:53 { errors cache 30 reload loop bind 169.254.25.10 forward . /etc/resolv.conf prometheus :9253 }
Обратите внимание на схеме приложения обращаются напрямую на 53-й порт DNS сервера. Service для доступа к подам не определен.
Основная задача кеширущего сервера – получать запросы от приложений, расположенных непосредственно на ноде. Если определить сервис, то мы не сможем ограничить запросы только на данный сервер. Поэтому разработчики пошли на хитрость:
bind 169.254.25.10
Сеть 169.254.0.0/16 – это так называемая Link-Local (см. RFC 3927). Подсети link-local не маршрутизируются: маршрутизаторы не должны отправлять пакеты с адресами link-local в другие сети. Система поднимает IP 169.254.25.10 на каждой ноде. С другой стороны, DaemonSet запускает на каждой ноде под nodelocaldns, в котором DNS сервер будет открывать 53-й порт на этом IP.
В результате, на каждой ноде мы получаем кеширующий DNS сервер, находящийся на одном и том же IP. Теперь достаточно сконфигурировать resolver в каждом поде на использование DNS на IP 169.254.25.10.
IP адрес задаётся в конфигурационном файле kubespray inventory: group_vars/k8s-cluster/k8s-cluster.yml
# Enable nodelocal dns cache enable_nodelocaldns: true nodelocaldns_ip: 169.254.25.10 nodelocaldns_health_port: 9254
Запросы к внутренним зонам кластера (cluster.local, in-addr.arpa и ip6.arpa), будут пересылаться на service coredns. Но поскольку мы настраиваем пересылку в DNS сервере, мы должны использовать IP адрес сервиса. В kubespray по умолчанию это 10.233.0.3.
Еще один интересный момент:
force_tcp
Кеширующие DNS сервера ходят к coredns по протоколу TCP. Разработчики говорят, что в связи с большим количеством NAT преобразований внутри сети Kubernetes, использование TCP ускоряет доставку запросов.
Запросы на все остальные домены пересылаются на DNS сервера, определённые в стандартных файлах /etc/resolv.conf, находящихся на нодах, где запущены поды nodelocaldns. Важно отметить, что данные из этих файлов читаются только при старте пода. Поэтому внесение изменений в resolv.conf не повлияет на работу кеширующего DNS сервера. Поэтому, после изменения этого файла, перезапустите nodelocaldns на этой ноде.
Настройки pod
Hostname и subdomain
По умолчанию под имеет только краткое имя. Если по каким-то причинам ему надо задать FQDN имя, его можно определить непосредственно в yaml файле.
apiVersion: v1 kind: Pod metadata: name: dnstools namespace: default spec: hostname: dns subdomain: tools containers: - name: dnstools image: infoblox/dnstools:latest command: - sleep - "36000" imagePullPolicy: IfNotPresent restartPolicy: Always
В итоге у контейнера будет установлено имя:
dns.tools.default.svc.cluster.local
Преобразование имени в ip адрес будет добавлено в файл /etc/hosts контейнера.
dnstools# cat /etc/hosts # Kubernetes-managed hosts file. 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet fe00::0 ip6-mcastprefix fe00::1 ip6-allnodes fe00::2 ip6-allrouters 10.233.79.189 dns.tools.default.svc.cluster.local dns
В зону DNS это преобразование добавляться не будет!
Файл hosts
Для добавления записей в файл /etc/hosts контейнеров пода можно использовать следующую конструкцию:
apiVersion: apps/v1 kind: Deployment metadata: name: dnstools spec: replicas: 1 selector: matchLabels: app: dnstools template: metadata: labels: app: dnstools spec: hostname: dns subdomain: tools hostAliases: - ip: 8.8.8.8 hostnames: - dns.google.local - dns8.google.local - ip: 8.8.4.4 hostnames: - dns4.google.local containers: - name: dnstools image: infoblox/dnstools:latest command: - sleep - "36000" imagePullPolicy: IfNotPresent restartPolicy: Always
Смотрите секцию hostAliases.
Pod – конфигурация resolver
При описании пода можно вместо стандартного файла /etc/resolvr.conf настроить свой собственный вариант.
В этом случае необходимо изменить dnsPolicy на none:
dnsPolicy: "None"
Эта политика заставляет под игнорировать настройки DNS Kubernetes. При этом требуется настроить DNS с использованием параметра
dnsConfig: nameservers: - 8.8.8.8 - 8.8.4.4 searches: - kryukov.biz options: - name: ndots value: "2"