浅析 CoreDNS 的工作机制
CoreDNS 是一个开源的域名系统(DNS)服务器,用于将域名解析为 IP 地址以实现网络通信。它是一个用 Go 语言编写的可扩展 DNS 服务器,旨在取代传统的 DNS 服务器并提供更灵活、可配置的解析方案。
CoreDNS 提供了模块化的插件系统,允许用户根据需求选择和组合插件,以定制 DNS 服务器的功能和行为。通过添加不同的插件,用户可以实现缓存、转发、重写、策略路由、服务发现等功能,从而满足各种复杂的域名解析需求。
插件由设置(Setup)、**注册(Registration)和处理程序(Handler)**部分组成。
- Setup 程序解析配置和插件的指令。
- Handler 是处理查询并实现所有逻辑的代码。
- Registration 是在 CoreDNS 中注册插件 - 这在编译 CoreDNS 时完成。服务器可以使用所有注册的插件,每个服务器中配置哪些插件的决定在运行时进行,并在 CoreDNS 的配置文件 Corefile 中完成。
安装运行
在 macOS 上安装 CoreDNS:
brew install coredns
可以执行 coredns -plugins
来查看已经安装的插件:
coredns -plugins
Server types:
dns
Caddyfile loaders:
flag
default
Other plugins:
dns.acl
dns.any
dns.auto
dns.autopath
dns.azure
dns.bind
dns.bufsize
dns.cache
dns.cancel
dns.chaos
dns.clouddns
dns.debug
dns.dns64
dns.dnssec
dns.dnstap
dns.erratic
dns.errors
dns.etcd
dns.file
dns.forward
dns.geoip
dns.grpc
dns.header
dns.health
dns.hosts
dns.k8s_external
dns.kubernetes
dns.loadbalance
dns.local
dns.log
dns.loop
dns.metadata
dns.minimal
dns.nsid
dns.pprof
dns.prometheus
dns.ready
dns.reload
dns.rewrite
dns.root
dns.route53
dns.secondary
dns.sign
dns.template
dns.tls
dns.trace
dns.transfer
dns.tsig
dns.view
dns.whoami
on
CoreDNS 的运行非常简单,执行 coredns
命令即可启动服务器。为了避免端口冲突,可以指定其他的端口启动:
coredns -dns.port=1053
.:1053
CoreDNS-1.10.0
darwin/arm64, go1.19.1,
如果需要对 DNS 服务器进行配置,需要通过 -conf
指定一个 Corefile 配置文件,还可以在配置文件中启用插件。不指定配置文件的话,CoreDNS 会使用 默认的配置,仅启用 whoami
和 log
两个插件:
.:PORT {
whoami
log
}
比如我从一台虚拟机上执行 DNS 的解析请求,192.168.1.174
是运行 CoreDNS 服务器的 macOS 地址:
dig @192.168.1.174 -p 1053 example.com
; <<>> DiG 9.16.1-Ubuntu <<>> @192.168.1.174 -p 1053 example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1283
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: d73ad3e9df490541 (echoed)
;; QUESTION SECTION:
;example.com. IN A
;; ADDITIONAL SECTION:
example.com. 0 IN A 192.168.1.11
_udp.example.com. 0 IN SRV 0 0 48788 .
;; Query time: 8 msec
;; SERVER: 192.168.1.174#1053(192.168.1.174)
;; WHEN: Sat Jul 15 02:38:11 UTC 2023
;; MSG SIZE rcvd: 114
在响应的 ADDITIONAL
中可以看到 example.com
解析到了 192.168.1.11
,而这个地址则是发起请求的虚拟机的 IP 地址。这就是 whoami
插件的 逻辑:
whoami 会返回请求方的本地 IP 地址、端口。
源码解析
启动
Coredns 使用 fork 自 Caddy 的 Coredns Caddy 启动 DNS 服务器。
- 加载指定的 Corefile 配置文件,如
/etc/coredns/Corefile
coremain/run.go#L60
- 启动 Caddy Server coremain/run.go#L66
- 运行插件
1. 检查配置文件中的插件。Coredns 有一个支持的 插件列表,这个列表中插件的顺序非常重要。
2. 为各插件创建控制器(Controller)
3. 找到插件注册的启动方法
setup
,如 Kubernetes 插件的setup
方法。各插件的setup
方法都可以在coredns/plugin/[PLUGIN NAME]/setup.go
中找到。 4. 启动插件。 - 创建服务器 core/dnsserver/register.go#L134
- 启动服务器 github.com/coredns/caddy@v1.1.1/caddy.go:542
处理请求
- 服务器启动之后开始接收 DNS 的解析请求,见 github.com/coredns/caddy@v1.1.1/caddy.go:805。
- 处理 DNS 解析请求 core/dnsserver/server.go#L173,这里会调用 方法 ServeDNS 按顺序执行配置的插件,所有插件都实现了 plugin.Handler 接口。
配置的每个插件会决定是否执行 DNS 的解析,以及下一步的操作:返回响应、报错、立即返回,亦或是调用下一个插件继续执行。详细内容,看参考 官方文档。
CoreDNS 与 Kubernetes
CoreDNS 在 Kubernetes 中扮演着重要的角色,为集群内部的 DNS 解析提供服务。通过与 Kubernetes 的紧密集成,CoreDNS 能够提供可靠的服务发现和通信功能,使得容器和服务能够通过域名进行互相访问和通信,为 Kubernetes 集群的运行提供了基础设施支持。
所有功能的实现,都在 kubernetes 插件 中,该插件实现了 Kubernetes 的基于 DNS 的服务发现规范,CoreDNS 以此来替代 kube-dns。
CoreDNS 在 Kubernetes 中的配置(维护在 ConfigMap coredns
中):
.: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 . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
CoreDNS 在处理解析请求时,会按顺序执行各个插件。
插件配置
kubernetes cluster.local in-addr.arpa ip6.arpa { #1
pods insecure #2
fallthrough in-addr.arpa ip6.arpa #3
ttl 30 #4
}
- #1 kubernetes 插件会处理
cluster.local
域的 DNS 解析请求,如foo.bar.svc.cluster.local
;in-addr.arpa
和ip6.arpa
用于做 DNS 反向查询,如4.4.8.8.in-addr.arpa
。 - #2 将 POD 域名(如
10-244-0-2.bar.pod.cluster.local
)的解析模式设置为insecure
,还支持disabled
和verified
另外两种模式。 - #3 对于反向查询的解析请求,会交给其他插件继续查询。
- #4 解析结果的有效期,默认是 5s,这里设置为 30s。
启动
CoreDNS 启动时会延迟最多 5 秒提供 DNS 服务,直到可以连接到 Kubernetes API 并同步所有监控的对象,见 文档。
kubernetes 插件将根据配置 最多启动 4 个控制器,分别持续监控 Service、Pod(解析模式设置为 verified
时启动)、Endpoint、Namespace 资源,维护本地缓存。
处理请求
kubernetes 插件实现了 plugin.ServiceBackend
接口,响应请求时从本地缓存中查找记录。
以 A 记录的查询为例,调用 plugin#A 方法 间接调用 ServiceBackend#Services
方法的实现 进行检索,返回 IP 地址。
nslookup -type=a foo.bar.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: foo.bar.svc.cluster.local
Address: 10.96.60.147