Две недели назад 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 в целом).