周末看了一下 coredns 的源代码,目前为止只是差不多搞清楚了这个代码库的逻辑。写篇博客记录一下,如果你也想要阅读这个库的源代码的话,这篇博客可以节省你的一些时间。
coredns 介绍
代码库地址:https://github.com/coredns/coredns
coredns 是一个 dns server,简单来说就是这个软件启动之后可以监听一个端口,然后你将 dns 查询请求发给这个端口,它可以告诉你 dns 解析的结果。所以作为任意一种 dns server(DNS Authoritative Nameserver, 或者 Recursive Resolver Server) 工作,我认为都是没有问题的。
这个代码库值得一读的理由有:
- coredns 以 plugin 的形式工作,除了 plugin 之外部分的代码很少,而且那部分代码其实不必阅读也可以(在读完本文之后);plugin 作为 first-class citizen 的好处是,职责清晰,一个 plugin 只做一件事情。比如 metrics,trace 这种东西,都是以 plugin 的形式存在的。这样,阅读起来非常方便;
- 每个 plugin 完成自己的事情,阅读起来难度低。而且这个库要求的背景知识很少,不需要懂很多网络的协议。DNS 相关的协议部分,看到哪里不懂的时候再查就好了。
不好的地方:
- 这个服务是基于一个 coredns 自己 fork 维护的 caddy server 来实现的,就导致可能需要去看 caddy 部分的代码。而且 coredns 自己维护的 caddy 已经脱离最新版了,我发现用的 caddy plugin 已经在官方的代码库中删除了。所以有些地方理解起来可能不简单。
看代码之前的准备工作(推荐)
如果之前没有用过的话,建议看代码之前先:
- 看完 manual:https://coredns.io/manual/toc/,可以知道使用方法,和大体的工作原理;
- 看下 plugin 的写法:https://coredns.io/2016/12/19/writing-plugins-for-coredns/
编译方法
因为 golang 是编译型的语言,所以无法像 Python 那样动态安装、加载插件。要新增插件,必须重新编译,将 plugin 的代码编译进二进制。
插件的列表在 plugin.cfg 中。如果新安装插件,需要将新插件写入这个列表,然后运行 go generate coredns.go
命令,重新生成这两个文件:
- coredns/core/plugin/zplugin.go: import 列表,这样插件的代码由于被 import 了所以会作为依赖编译进去
- core/dnsserver/zdirectives.go
编译的命令是: CGO_ENABLED=0 go build -v -ldflags="-s -w -X github.com/coredns/coredns/coremain.GitCommit=3288b111-dirty" -o coredns
. GitCommit
作为变量注入进 binary,运行的时候打印。
程序入口
coredns.go
里面 import 了 plugin,然后调用了 coremain.Run
。
coremain.Run
里面只是处理了一些和 Version(用于 Print)的信息,和命令和参数。最后启动了 Caddy server。
coredns server 的逻辑在 coredns/core/dnsserver
. 但是中间涉及和 caddy v1 的交互,比如 MakeServers()
, 是 caddy 里面的接口。应该也不是很重要。
server 里面的入口应该是 Serve()
。基本的逻辑是拿到 plugin chain,然后调用第一个 Plugin, 调用 Plugin 的 ServeDNS()
。
Plugins
Plugin Chain, 顾名思义,是 Chain 在一起的。并不是在 server 里面一个 for 循环调用所有的 Plugins,而是 Server 只会调用第一个 Plugin。
第一个 Plugin 可以处理 DNS 请求,返回结果。如果像是 metrics 这样不负责逻辑的 Plugin,可以在完成自己要做的事情之后,去调用 coredns/plugin/plugin.go 里面的 NextOrFailure
函数,交给下一个 Plugin 去处理。和 ASGI 一样,turtles all the way down.
Plugin 主要做两件事情:
- 通过
init()
函数将自己注册进去,也可以做一些初始化的工作。比如支持配置。Corefile
中的配置可以在 init 的时候通过 setup 接口读到; ServeDNS()
主要的函数入口。处理 DNS 请求。
whoami
这个 Plugin 返回查询者的 IP 端口等信息,逻辑比较简单,建议从这个 plugin 开始看。
Pingback: Coredns 源码解析:启动流程 | 卡瓦邦噶!