Две недели назад Open Source-проект CoreDNS отметился своим очередным релизом — 008. Авторы называют свой продукт «DNS-сервером, состоящим из цепочки промежуточных компонентов (middleware), каждый из которых реализует какую-то возможность DNS». Что примечательно, они уже успели добиться включения CoreDNS в список официальных проектов организации CNCF (Cloud Native Computing Foundation), пополнив ряды Kubernetes, Prometheus, CNI, containerd, rkt и других разработок, активно применяемых в мире контейнеров, микросервисов и «родных облачных приложений» (cloud native).
Как появился CoreDNS, для чего он предназначен и как его можно использовать?
Знакомимся с CoreDNS
CoreDNS — DNS-сервер, появившийся в начале 2016 года (под свободной лицензией Apache License v2) как форк быстрого веб-сервера Caddy, написанного на языке Go. Сам HTTP-сервер в Caddy реализован пакетом httpserver, двумя важнейшими типами в котором являются Handler (функция обработки HTTP-запроса) и Middleware (промежуточный слой, прицепляющий один Handler к другому, — таким образом формируется цепочка из обработчиков). Этот подход с цепочкой из функций переняли и в CoreDNS, что позволило разработчикам описывать своё решение очень лаконично: «CoreDNS — DNS-сервер, сцепляющий middleware».
Второй основополагающей деталью в проекте CoreDNS является его преемственность от SkyDNS — service discovery, построенного поверх NoSQL-хранилища etcd и использующего DNS-запросы (специально сформированные SRV-записи) для обнаружения доступных сервисов. Фактически SkyDNS был лёгкой прослойкой между etcd, где на самом деле хранилась информация о сервисах, и DNS-сервером, через который эта информация становилась «публично» доступной. Судя по всему, активная разработка SkyDNS прекратилась около 7 месяцев назад (с добавлением поддержки etcd 3) в то время, как коммиты в кодовой базе CoreDNS можно наблюдать практически ежедневно.
В CoreDNS не стали ограничиваться etcd как единственным бэкендом для данных, отображаемых в DNS-записях. На данный момент поддерживается ещё Kubernetes (который, впрочем, как известно, тоже использует etcd… ), что позволяет авторам официально позиционировать CoreDNS в качестве замены kube-dns. (Подробнее о том, как эту замену попробовать в действии, читайте ниже.)
Примечание: Полная история взаимоотношений упомянутых DNS-решений: SkyDNS, CoreDNS, kube-dns — переплетена ещё теснее, чем могло показаться. Дело в том, что, во-первых, kube-dns использует библиотеки SkyDNS для обслуживания DNS-запросов, поступающих в поды и сервисы Kubernetes. А во-вторых, главным разработчиком CoreDNS является оригинальный автор SkyDNS — Miek Gieben — SRE из Google, которого также знают в сообществе Go благодаря его DNS-библиотеке. Всё это позволяет увидеть в CoreDNS не «конкурента» kube-dns, а скорее его эволюцию.
Возможности CoreDNS
Этот сервер позволяет принимать запросы по UDP/TCP, TLS (RFC 7858) и gRPC. А функционирует он как:
- первичный DNS-сервер, отдающий данные о зоне из файла (DNS и DNSSEC) и позволяющий выполнять трансфер зоны;
- вторичный DNS-сервер (поддерживаются только AXFR);
- прокси (перенаправляет запросы другим серверам).
Среди других значимых возможностей CoreDNS:
- кэширование результатов запросов;
- rewrite для запросов (qtype, qclass, qname);
- балансировка запросов;
- проверка состояния (health checking) конечных узлов;
- метрики в Prometheus;
- журналирование запросов и ошибок;
- профилирование.
Все перечисленные функции (включая даже режимы первичного и вторичного сервера) реализуются различными модулями, а точнее (в терминологии Caddy/CoreDNS) — middleware. Очевидно, что возможности легко расширять созданием новых middleware.
Архитектура CoreDNS, где за каждую функцию отвечает свой middleware
John Belamaric, архитектор в Infoblox и один из ведущих разработчиков CoreDNS, считает именно эту особенность главной в проекте:
Существует множество различных DNS-серверов, существуют даже другие решения для обнаружения сервисов, основанные на DNS. Но одним из главных достоинств CoreDNS является то, насколько это решение расширяемое и гибкое. Это позволяет легко адаптировать его под динамичный, часто изменяющийся мир cloud-native.
Почему CoreDNS взяли под опеку в CNCF?
Вот как объясняется это событие на сайте проекта:
Наша цель — сделать CoreDNS DNS-сервером и решением service discovery для cloud-native. CNCF как организация сосредоточена на совершенствовании архитектур для cloud-native. Таким образом, для нас это прекрасное совпадение [преследуемых целей]. Обнаружение сервисов — ключевой компонент в родном облачном пространстве CNCF, и CoreDNS первенствует в этой роли.
В заявлении от имени самой CNCF выступил Chris Aniszczyk (COO в CNCF), отметивший, что «CoreDNS предоставляет важные сервисы для именования и эффективно интегрируется со многими другими проектами проектами категории cloud-native в CNCF», а также «CoreDNS — привлекательный вариант для обнаружения сервисов в Kubernetes».
Примечание: На данный момент CoreDNS имеет начальный статус (inception) среди проектов CNCF, что означает необходимость пересмотра техническим комитетом этого статуса через год и принятия решения о его дальнейшей судьбе: убрать, продлить, повысить до incubating или graduated.
Перейдём от теории к практике.
CoreDNS как Service Discovery для Kubernetes
Как уже писалось в примечании выше, разработчики CoreDNS не просто предлагают альтернативу для kube-dns — они взаимодействуют с сообществом Kubernetes, чтобы результат их работы был полезным для всех. Хороший тому пример — их инициатива по созданию спецификации, описывающей Service Discovery на базе DNS для Kubernetes. Она появилась с тем, чтобы «гарантировать совместимость существующей реализации в Kube-DNS и новой в CoreDNS». Версия 1.0.0 этой спецификации в основном копирует поведение kube-dns — ей соответствуют релизы CoreDNS 005 и выше (предлагая, помимо этого, дополнительные возможности, если сравнивать с kube-dns).
Чтобы начать использовать CoreDNS в качестве Service Discovery в Kubernetes, разработчики подготовили конфигурацию (
ConfigMap
и Deployment
) и даже Bash-скрипт deploy.sh для быстрого деплоя. Они позаботились и о примере, как этим пользоваться (все дальнейшие листинги взяты из него):$ ./deploy.sh 10.3.0.0/24 cluster.local
-
10.3.0.0/24
— CIDRs сервисов; -
cluster.local
(указывается опционально) — доменное имя кластера.
Результатом выполнения скрипта станет такой манифест:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
log stdout
health
kubernetes cluster.local {
cidrs 10.3.0.0/24
}
proxy . /etc/resolv.conf
cache 30
}
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: coredns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "CoreDNS"
spec:
replicas: 1
selector:
matchLabels:
k8s-app: coredns
template:
metadata:
labels:
k8s-app: coredns
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ''
scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]'
spec:
containers:
- name: coredns
image: coredns/coredns:latest
imagePullPolicy: Always
args: [ "-conf", "/etc/coredns/Corefile" ]
volumeMounts:
- name: config-volume
mountPath: /etc/coredns
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
dnsPolicy: Default
volumes:
- name: config-volume
configMap:
name: coredns
items:
- key: Corefile
path: Corefile
---
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: coredns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "CoreDNS"
spec:
selector:
k8s-app: coredns
clusterIP: 10.3.0.10
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
Указанная здесь (в
Corefile
) директива cidrs 10.3.0.0/24
сообщает в Kubernetes middleware из CoreDNS, что необходимо обслуживать PTR-запросы для обратной зоны 0.3.10.in-addr.arpa
.Остаётся передать результат на исполнение в Kubernetes:
$ ./deploy.sh 10.3.0.0/24 | kubectl apply -f -
configmap "coredns" created
deployment "coredns" created
service "kube-dns" configured
… и убедиться, что новый DNS-сервер действительно заработал:
$ kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools
Waiting for pod default/dnstools to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.
# host kubernetes
kubernetes.default.svc.cluster.local has address 10.3.0.1
# host kube-dns.kube-system
kube-dns.kube-system.svc.cluster.local has address 10.3.0.10
# host 10.3.0.1
1.0.3.10.in-addr.arpa domain name pointer kubernetes.default.svc.cluster.local.
# host 10.3.0.10
10.0.3.10.in-addr.arpa domain name pointer kube-dns.kube-system.svc.cluster.local.
Как это выглядит на стороне самого CoreDNS? Вот пример для случая кластера из двух реплик CoreDNS, между которыми делается балансировка DNS-запросов:
# найдем поды со службой CoreDNS
$ kubectl get --namespace kube-system pods
NAME READY STATUS RESTARTS AGE
coredns-3558181428-0zhnh 1/1 Running 0 2m
coredns-3558181428-xri9i 1/1 Running 0 2m
heapster-v1.2.0-4088228293-a8gkc 2/2 Running 0 126d
kube-apiserver-10.222.243.77 1/1 Running 2 126d
kube-controller-manager-10.222.243.77 1/1 Running 2 126d
kube-proxy-10.222.243.77 1/1 Running 2 126d
kube-proxy-10.222.243.78 1/1 Running 0 126d
kube-scheduler-10.222.243.77 1/1 Running 2 126d
kubernetes-dashboard-v1.4.1-gi2xr 1/1 Running 0 24d
tiller-deploy-3299276078-e8phb 1/1 Running 0 24d
# посмотрим на логи первого
$ kubectl logs --namespace kube-system coredns-3558181428-0zhnh
2017/02/23 14:48:29 [INFO] Kubernetes middleware configured without a label selector. No label-based filtering will be performed.
.:53
2017/02/23 14:48:29 [INFO] CoreDNS-005
CoreDNS-005
10.2.6.127 - [23/Feb/2017:14:49:44 +0000] "AAAA IN kubernetes.default.svc.cluster.local. udp 54 false 512" NOERROR 107 544.128µs
10.2.6.127 - [23/Feb/2017:14:49:44 +0000] "MX IN kubernetes.default.svc.cluster.local. udp 54 false 512" NOERROR 107 7.576897ms
10.2.6.127 - [23/Feb/2017:14:49:52 +0000] "A IN kube-dns.kube-system.default.svc.cluster.local. udp 64 false 512" NXDOMAIN 117 471.176µs
23/Feb/2017:14:49:52 +0000 [ERROR 0 kube-dns.kube-system.default.svc.cluster.local. A] no items found
10.2.6.127 - [23/Feb/2017:14:50:00 +0000] "PTR IN 10.0.3.10.in-addr.arpa. udp 40 false 512" NOERROR 92 752.956µs
# посмотрим на логи второго
$ kubectl logs --namespace kube-system coredns-3558181428-xri9i
2017/02/23 14:48:29 [INFO] Kubernetes middleware configured without a label selector. No label-based filtering will be performed.
.:53
2017/02/23 14:48:29 [INFO] CoreDNS-005
CoreDNS-005
10.2.6.127 - [23/Feb/2017:14:49:44 +0000] "A IN kubernetes.default.svc.cluster.local. udp 54 false 512" NOERROR 70 1.10732ms
10.2.6.127 - [23/Feb/2017:14:49:52 +0000] "A IN kube-dns.kube-system.svc.cluster.local. udp 56 false 512" NOERROR 72 409.74µs
10.2.6.127 - [23/Feb/2017:14:49:52 +0000] "AAAA IN kube-dns.kube-system.svc.cluster.local. udp 56 false 512" NOERROR 109 210.817µs
10.2.6.127 - [23/Feb/2017:14:49:52 +0000] "MX IN kube-dns.kube-system.svc.cluster.local. udp 56 false 512" NOERROR 109 796.703µs
10.2.6.127 - [23/Feb/2017:14:49:56 +0000] "PTR IN 1.0.3.10.in-addr.arpa. udp 39 false 512" NOERROR 89 694.649µs
Для отключения логирования всех DNS-запросов (большая нагрузка на диск при реальном применени) достаточно убрать строку
log stdout
из Corefile
.Важно: авторы предупреждают, что в случае использования Google Container Engine (GKE) описываемый пример не сработает из-за дополнительных процессов, не позволяющих заменить стандартный kube-dns. Есть путь решения этой проблемы, однако официально он пока не был ими документирован/анонсирован.
CoreDNS для Minikube
В случае локального запуска Kubernetes с Minikube есть схожая проблема: используемый в нём addon manager периодически проверяет (и поддерживает) состояние конфигураций всех установленных дополнений, одним из которых является kube-dns. Чтобы этот менеджер не мешал работе CoreDNS, есть простое решение.
Оно заключается в том, чтобы изменить список установленных дополнений для minikube:
$ minikube addons list
- dashboard: enabled
- default-storageclass: enabled
- kube-dns: enabled
- heapster: disabled
- ingress: disabled
- registry-creds: disabled
- addon-manager: enabled
$ minikube addons disable kube-dns
kube-dns was successfully disabled
$ minikube addons list
- heapster: disabled
- ingress: disabled
- registry-creds: disabled
- addon-manager: enabled
- dashboard: enabled
- default-storageclass: enabled
- kube-dns: disabled
Эту настройку необходимо выполнить до применения конфигурации CoreDNS в Kubernetes (т.е. до запуска
kubectl apply -f
из примера выше). А после применения этой конфигурации потребуется ещё удалить ReplicationController
из kube-dns, поскольку отключение дополнения не делает этого автоматически:$ kubectl get -n kube-system pods
NAME READY STATUS RESTARTS AGE
coredns-980047985-g2748 1/1 Running 1 36m
kube-addon-manager-minikube 1/1 Running 0 9d
kube-dns-v20-qzvr2 3/3 Running 0 1m
kubernetes-dashboard-ks1jp 1/1 Running 0 9d
$ kubectl delete -n kube-system rc kube-dns-v20
replicationcontroller "kube-dns-v20" deleted
Заключение
CoreDNS — интересный проект, реальные перспективы которому придают его убедительное наследие (опыт со времён SkyDNS), тесная кооперация с профильным сообществом (Kubernetes и Go), признание в CNCF и, конечно, современный подход к реализации.
Читайте также в нашем блоге по близким темам:
- «Что такое service mesh и почему он мне нужен [для облачного приложения с микросервисами]?» (на примере linkerd — ещё одного проекта в CNCF);
- «Container Networking Interface (CNI) — сетевой интерфейс и стандарт для Linux-контейнеров» (другой сетевой проект в CNCF);
- «Наш опыт с Kubernetes в небольших проектах (обзор и видео доклада)» (об архитектуре и устройстве Kubernetes в целом).