更新时间:2023 年 2 月
CoreDNS 简介
官网:CoreDNS: DNS and Service Discovery
官方文档:CoreDNS: DNS and Service Discovery
CoreDNS 插件以及插件文档:Plugins (coredns.io)
kubernetes 关于 CoreDNS 的描述:使用 CoreDNS 进行服务发现 | Kubernetes
CoreDNS 是一个用 GO 语言编写的 DNS 服务器。不同于其他优秀的 DNS 服务器(例如: BIND、Knot、PowerDNS 或 Unbound),CoreDNS 非常灵活,几乎所有功能都外包到插件中
kubernetes 早期的 dns 组件有 skydns、kube-dns。目前,CoreDNS 就成了 Kubernetes 的默认 DNS 服务器
Kubernetes 版本与 kubeadm 安装的 CoreDNS 版本对应关系参考:deployment/CoreDNS-k8s_version.md
CoreDNS 的作用
Kubernetes 中 Pod 的 IP 可能会随着销毁或创建而改变。为了保证访问,需要实时为 Service 和 Pod 创建 DNS 记录,然后应用之间的访问使用一致的 DNS 名称而非 IP 地址,这样即使 IP 发生改变,应用之间依旧可以相互调用
Service 的 FQDN 如下
{ServiceName}.{Namespace}.svc.{ClusterDomain}
Corefile 配置
Corefile 是 CoreDNS 的配置文件,定义了一些内容
- DNS Server 的监听协议和端口
- DNS Server 负责哪个 Zone 的权威(authoritative)DNS 解析
- DNS Server 加载的插件
格式
Corefile 由一个个服务配置块组成,每个服务配置块定义了要解析的区域
、端口
、使用的插件
等信息,如果需要解析根域,则使用点号 .
表示
格式
ZONE:[PORT] {
# Zone Block
[PLUGIN] ... {
# Plugin Block
}
}
解析根域,端口未指定则使用默认的 53 端口
. {
# Plugins defined here.
}
目前,CoreDNS 接受四种不同的协议,可以在区域之前添加协议来指定使用的协议。四种协议如下
dns://
for plain DNS (默认协议,如果未明确指定协议,则使用该协议)tls://
for DNS over TLS, see RFC 7858https://
for DNS over HTTPS, see RFC 8484grpc://
for DNS over gRPC
tls://example.net:53 {
file db.example.net
forward . tls://223.5.5.5:853 {
tls_servername dns.alidns.com
force_tcp
max_fails 3
}
}
Kubernetes 中的默认配置
在 Kubernetes 集群中,可以通过 configmap 获取安装的 CoreDNS 的默认配置,即 Corefile 文件
$ /etc/kubeasz/bin/kubectl edit configmap coredns -n kube-system
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes skynemo.cn in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
reload
loadbalance
}
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"Corefile":".:53 {\n errors\n health {\n lameduck 5s\n }\n ready\n kubernetes skynemo.cn in-addr.arpa ip6.arpa {\n pods insecure\n fallthrough in-addr.arpa ip6.arpa\n ttl 30\n }\n prometheus :9153\n forward . /etc/resolv.conf {\n max_concurrent 1000\n }\n cache 30\n reload\n loadbalance\n}\n"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"addonmanager.kubernetes.io/mode":"EnsureExists"},"name":"coredns","namespace":"kube-system"}}
creationTimestamp: "2023-01-31T15:46:09Z"
labels:
addonmanager.kubernetes.io/mode: EnsureExists
name: coredns
namespace: kube-system
resourceVersion: "35013"
uid: 45af18ca-ef5d-452a-a6b6-a9a550b83c47
CoreDNS 插件
插件的工作模式
当 CoreDNS 启动时,将根据配置文件中的服务配置块运行一组 Server。每个 Server 由其服务的区域和端口定义,且每个服 Server 都有自己的插件链。CoreDNS 处理查询会经过以下几个步骤
-
如果在请求查询的端口上有多个 Server,会根据最长后缀匹配原则来适配 Server。例如,有两个 Server,一个
example.org
,另一个a.example.org
,此时,请求为www.a.example.org
,将会匹配路由到到后者 -
找到对应的 Server 后,会根据该 Server 配置的插件链进行路由。插件顺序是固定的,其静态排序由配置文件
plugin.cfg
确定 -
每个插件会判断是否对该请求进行处理,有以下几种处理情况
-
请求由当前插件处理
插件将生成对应的相应返回给客户端。此时,请求到此结束,插件链的下一个插件不会被调用。如
whoami
插件 -
请求不被当前插件处理
直接调用下一个插件。如果最后一个插件执行错误,服务器返回
SERVFAIL
响应 -
请求由当前插件以 fallthrough 方式处理
如果请求在该插件处理过程中有可能将跳转至下一个插件,该过程称为 fallthrough,并以关键字
fallthrough
来决定是否允许此项操作。例如host
插件,当查询域名未位于/etc/hosts
,则调用下一个插件 -
请求由当前插件处理,添加
hint
并调用下一个插件插件在其响应中添加了某些信息(hint)后继续交由下一个插件处理,这些额外的信息将组成对客户端的最终响应。如:
metric
插件
-
示例
Corefile 如下
coredns.io:5300 {
file /etc/coredns/zones/coredns.io.db
}
example.io:53 {
errors
log
file /etc/coredns/zones/example.io.db
}
example.net:53 {
file /etc/coredns/zones/example.net.db
}
.:53 {
errors
log
health
rewrite name foo.example.com foo.default.svc.cluster.local
kubernetes cluster.local 10.0.0.0/24
file /etc/coredns/example.db example.org
forward . /etc/resolv.conf
cache 30
}
注意这里有两个不同的端口:5300 和 53。在 CoreDNS 内部,每个端口都将生成一个 dnssever.Server
。即使有四个服务配置块(server blocks),我们也只能得到两个实际的 Server
CoreDNS 将收集与同一端口相关的所有服务配置块,并将它们组合到同一个 dnssever.Server
中。这个 dnssever.Server
将对端口上的查询进行多路复用,根据区域(Zone)的不同,选择特定的匹配服务配置块,并传递到不同的插件链。如果没有匹配的服务配置块,则返回 SERVFAIL
。下图直观地显示了这一点

常用插件解析
参考官方插件文档以及 kubernetes 关于 DNS 的文档:自定义 DNS 服务 | Kubernetes
error
插件文档:errors (coredns.io)
查询处理过程中遇到的任何错误都将打印到标准输出。特定类型的错误可以合并,每一段时间打印一次
每个服务的配置块只能使用此插件一次
health
插件文档:health (coredns.io)
健康状态接口插件,提供 pod 的存活探针(Liveness)接口。启用 health
插件(提供主动查询)时,默认情况下,运行状况在 :8080/health
(端口 8080
,路径 /health
)上导出,当 CoreDNS 启动并运行时(不管插件是否正常),会返回 200 OK
的 HTTP 状态码
ready
插件文档:ready (coredns.io)
插件就绪状态接口插件,提供 pod 的就绪(Readiness)探针接口。启用 ready
插件时,会在:8181/health
上以 HTTP 状态码形式返回 CoreDNS 的状态
与 health
不同的是,只有使用到的所有插件(能够发出就绪信号的插件)都准备就绪,才会返回 200 OK
;否则将返回 503
状态码,并附带出现问题的插件列表。一旦插件发出就绪信号,就不会再次查询它
kubernetes
kubernetes
插件是 CoreDNS 与 kubernetes 集成的最主要的插件
该插件实现了 Kubernetes 基于 DNS 的服务发现规范( Kubernetes DNS-Based Service Discovery Specification.),提供了 kubernetes service name
的 DNS 解析等功能
运行 kubernetes 插件的 CoreDNS 可以用作 kubernetes 集群中 kube-dns 的替代品。请参阅:deployment/kubernetes(现已转为 helm
和 kubeadm
维护)
stubDomain
和 upstreamNameserver
通过转发插件实现,详情请阅:Configuring Private DNS Zones and Upstream Nameservers in Kubernetes | Kubernetes
注:每个服务配置块只能使用此插件一次
启动
当 CoreDNS 在启用 kubernetes
插件的情况下启动时,它将延迟对外 DNS 服务 5 秒,直到它可以连接到Kubernetes API 并同步所有对象监视。如果这不能在 5 秒内完成连接 kubernetes API,那么 CoreDNS 将开始提供对外 DNS 服务,而 kubernetes
插件将继续尝试连接和同步所有对象监视。对于尚未同步的 Kubernetes 记录的任何请求,CoreDNS 都会回答 SERVFAIL
插件运行
- 首先,kube-apiserver 会将 Service 和对应的 Endpoint 信息存储在 etcd 中。这些信息包括 Service 名称、Service IP 地址和后端 Pod 的 IP 地址。并对外提供 API 接口
- CoreDNS 的
kubernetes
插件会向 kube-apiserver 发出 REST API 请求,以获取 Kubernetes 集群中的所有 Service 和 Endpoint 的信息 - kube-apiserver 返回一个 JSON 响应,其中包含 Kubernetes 集群中所有 Service 和 Endpoint 的信息
kubernetes
插件解析 JSON 响应,将 Service 和 Endpoint 信息转换成 DNS 记录,包括服务名称、IP 地址和端口号- 当 Kubernetes Service 的 IP 地址或端口号发生变化时,CoreDNS 的 kubernetes 插件会自动更新 DNS 记录,确保服务仍然能够正确地被解析。
监听 Kubernetes Endpoints
默认情况下,kubernetes
插件通过 API discovery.EndpointSlices
监视 Endpoints,以获取最新的 Endpoints 信息。但是,如果 Kubernetes 版本默认不支持 EndpointSliceProxying 特性(即 Kubernetes < 1.19),则使用 API api.Endpoints
prometheus
监控指标(度量指标)插件。指标输出路径固定为 /metrics
,端口默认为:localhost:9153
。数据符合 Prometheus 的 key-value 格式
forward
插件文档:forward (coredns.io)
DNS 解析转发插件,用于支持迭代查询。支持 UDP、TCP 和 TLS 上的 DNS 查询,并内置了健康检查
注:每个服务配置块只能使用此插件一次
cache
插件文档:cache (coredns.io)
缓存插件。启用缓存后,除区域传输和元数据记录外的默认所有记录都将缓存 3600 秒,可以指定缓存时间
注:每个服务配置块只能使用此插件一次
reload
插件文档:reload (coredns.io)
重载配置插件。此插件会自动重新加载已更改的核心文件 corefile(不包括分区文件,分区文件重载使用 auto
插件)
注:每个服务配置块只能使用此插件一次
loadbalance
负载均衡插件。如果同一域名的 A,AAAA 和 MX 解析记录有多条,轮训应答
注:每个服务配置块只能使用此插件一次
CoreDNS 的域名解析流程
普通 DNS 解析流程
- 查询发起:当应用程序尝试通过其域名访问资源时,它会向本地 CoreDNS 服务器发送 DNS 查询。在 kubernetes 的应用中,CoreDNS 的地址由 决定
- 转发查询:如果本地 CoreDNS 服务器在其缓存中没有所请求的资源的 IP 地址,它将将查询转发到上游 DNS 服务器(例如递归解析器)。
- 递归解析:上游 DNS 服务器通过将查询转发到层次结构中的其他 DNS 服务器来递归解析查询,直到到达具有所请求资源的 IP 地址的 DNS 服务器为止。
- 缓存响应:随着 DNS 查询返回所请求资源的 IP 地址,本地 CoreDNS 服务器会将响应缓存起来,以避免将来需要递归解析。
- 返回响应:本地 CoreDNS 服务器将 IP 地址返回给请求应用程序,从而允许它访问该资源。
除了上述步骤之外,CoreDNS 还支持插件,这些插件可以修改或增强域名解析流程。这些插件可以执行各种功能,例如 DNSSEC 验证、负载均衡和提供自定义 DNS 记录等
k8s 中应用的发起的 DNS 解析流程
kubelet 启动时,读取 /var/lib/kubelet/config.yaml
中设置的 DNS 地址(clusterDNS
),为后续的每个 Pod 配置 /etc/resolv.conf
中的 nameserver
,生成的 /etc/resolv.conf
如下:
$ kubectl exec -it deployment-busybox-54bb44f8bd-dmmx8 -- cat /etc/resolv.conf
search default.svc.skynemo.cn svc.skynemo.cn skynemo.cn
nameserver 169.254.20.10
options ndots:5
注:自定义 pod 中的
/etc/resolv.conf
设置,可以参考:Service 与 Pod 的 DNS | Kubernetes
如果启动了 NodeLocal DNSCache,则一般该 clusterDNS
会指定为本地运行的 DNS 缓存的 DaemonSet(例如上述例子中的 IP :169.254.20.10),然后 NodeLocal DNSCache 再转发到上游的 CoreDNS 的 Service(一般为 Service IP 段的第二个 IP);如果未启动,则直接指定为 CoreDNS 的 Service
NodeLocal DNSCache 以 DaemonSet 的形式运行在每个节点,为查询 DNS 解析提供一层缓存
主体流程如下图
其他 DNS 相关文档
- CoreDNS 调试:调试 DNS 问题 | Kubernetes
- CoreDNS 调优:自动扩缩集群 DNS 服务 | Kubernetes
- NodeLocal DNSCache:在 Kubernetes 集群中使用 NodeLocal DNSCache | Kubernetes
- Kubernetes 的 DNS 策略:参考:Service 与 Pod 的 DNS | Kubernetes
- Pod 的 DNS 设置:参考:Service 与 Pod 的 DNS | Kubernetes