CoreDNS源码基于1.3.1版本。
架构
CoreDNS架构基于Caddy框架实现,整个项目大量使用了Caddy的插件功能,整体代码结构比较清晰,读起来也特别的舒服。
Makefile
首先看CoreDNS的Makefile部分代码:

core/plugin/zplugin.go
和core/dnsserver/zdirectives.go
文件都是由coredns.go
通过plugin.cfg
生成的。其中,coredns.go
就是启动文件,plugin.cfg
是配置的插件列表,默认插件如下:
# Directives are registered in the order they should be
# executed.
#
# Ordering is VERY important. Every plugin will
# feel the effects of all other plugin below
# (after) them during a request, but they must not
# care what plugin above them are doing.
# How to rebuild with updated plugin configurations:
# Modify the list below and run `go gen && go build`
# The parser takes the input format of
# <plugin-name>:<package-name>
# Or
# <plugin-name>:<fully-qualified-package-name>
#
# External plugin example:
# log:github.com/coredns/coredns/plugin/log
# Local plugin example:
# log:log
metadata:metadata
tls:tls
reload:reload
nsid:nsid
root:root
bind:bind
debug:debug
trace:trace
health:health
pprof:pprof
prometheus:metrics
errors:errors
log:log
dnstap:dnstap
chaos:chaos
loadbalance:loadbalance
cache:cache
rewrite:rewrite
dnssec:dnssec
autopath:autopath
template:template
hosts:hosts
route53:route53
federation:federation
k8s_external:k8s_external
kubernetes:kubernetes
file:file
auto:auto
secondary:secondary
etcd:etcd
loop:loop
forward:forward
proxy:proxy
erratic:erratic
whoami:whoami
on:github.com/mholt/caddy/onevent
默认代码中包含的是现有的所有插件,类似于一种键值对的方式提供。
再来看启动文件coredns.go
:

Run
方法,但是加载了github.com/coredns/coredns/core/plugin
包下的所有init方法。github.com/coredns/coredns/core/plugin
包下只有一个zplugin.go
文件,而这个文件就是Makefile生成的文件,如下:
// generated by directives_generate.go; DO NOT EDIT
package plugin
import (
// Include all plugins.
_ "github.com/coredns/coredns/plugin/auto"
_ "github.com/coredns/coredns/plugin/autopath"
_ "github.com/coredns/coredns/plugin/bind"
_ "github.com/coredns/coredns/plugin/cache"
_ "github.com/coredns/coredns/plugin/chaos"
_ "github.com/coredns/coredns/plugin/debug"
_ "github.com/coredns/coredns/plugin/dnssec"
_ "github.com/coredns/coredns/plugin/dnstap"
_ "github.com/coredns/coredns/plugin/erratic"
_ "github.com/coredns/coredns/plugin/errors"
_ "github.com/coredns/coredns/plugin/etcd"
_ "github.com/coredns/coredns/plugin/federation"
_ "github.com/coredns/coredns/plugin/file"
_ "github.com/coredns/coredns/plugin/forward"
_ "github.com/coredns/coredns/plugin/health"
_ "github.com/coredns/coredns/plugin/hosts"
_ "github.com/coredns/coredns/plugin/k8s_external"
_ "github.com/coredns/coredns/plugin/kubernetes"
_ "github.com/coredns/coredns/plugin/loadbalance"
_ "github.com/coredns/coredns/plugin/log"
_ "github.com/coredns/coredns/plugin/loop"
_ "github.com/coredns/coredns/plugin/metadata"
_ "github.com/coredns/coredns/plugin/metrics"
_ "github.com/coredns/coredns/plugin/nsid"
_ "github.com/coredns/coredns/plugin/pprof"
_ "github.com/coredns/coredns/plugin/proxy"
_ "github.com/coredns/coredns/plugin/reload"
_ "github.com/coredns/coredns/plugin/rewrite"
_ "github.com/coredns/coredns/plugin/root"
_ "github.com/coredns/coredns/plugin/route53"
_ "github.com/coredns/coredns/plugin/secondary"
_ "github.com/coredns/coredns/plugin/template"
_ "github.com/coredns/coredns/plugin/tls"
_ "github.com/coredns/coredns/plugin/trace"
_ "github.com/coredns/coredns/plugin/whoami"
_ "github.com/mholt/caddy/onevent"
)
Makefile生成的另一文件zdirectives.go
如下:
// generated by directives_generate.go; DO NOT EDIT
package dnsserver
// Directives are registered in the order they should be
// executed.
//
// Ordering is VERY important. Every plugin will
// feel the effects of all other plugin below
// (after) them during a request, but they must not
// care what plugin above them are doing.
var Directives = []string{
"metadata",
"tls",
"reload",
"nsid",
"root",
"bind",
"debug",
"trace",
"health",
"pprof",
"prometheus",
"errors",
"log",
"dnstap",
"chaos",
"loadbalance",
"cache",
"rewrite",
"dnssec",
"autopath",
"template",
"hosts",
"route53",
"federation",
"k8s_external",
"kubernetes",
"file",
"auto",
"secondary",
"etcd",
"loop",
"forward",
"proxy",
"erratic",
"whoami",
"on",
}
两个文件,一个是加载所有的插件模块,执行init方法;另一个就是配置了一个数组。
回到zplugin.go
文件,以github.com/coredns/coredns/plugin/auto
包为例,其相应的init方法如下:

所以,通过这种init的方式,将生成的插件依次注册到caddy中。
CoreDNS初始化
通过Run
方法进入CoreDNS的初始化过程。Run
方法主要就是对参数的验证,重点看其引入的包github.com/coredns/coredns/core/dnsserver
,对应的init方法如下:

RegisterServerType
方法完成插件的最终注册,其中,插件列表Directives
是core/dnsserver/zdirectives.go
文件的内容,包含了一系列的插件信息(插件顺序是有严格要求的);而newContext
就是dnsContext
对象,实现了caddy的Context接口,

MakeServers
完成dnsServer的创建。
DNSServer创建
CoreDNS有多种Server的创建,

NewServer
方法,主要作用是添加pluginChain
链,该链对应的就是相应插件的plugin.Handler
方法,逆序遍历,

host-->log-->errors
,最终实际加载的顺序则是errors-->log-->host
,和定义的顺序一致。
插件执行
所有的插件主要实现了Handler
接口,

Name
方法就是插件的名称,重点看ServeDNS
方法。ServeDNS
有三个参数,Context
表示整个上下文,ResponseWriter
表示返回给客户端的报文,dns.Msg
为客户端的请求。每个插件在处理请求时,调用
NextOrFailure
方法调用下一个插件去处理相应的逻辑,即链式调用;调用WriteMsg
方法完成结束调用,将消息传递给客户端。如果需要自定义插件的话,基本上实现了
Name
和ServeDNS
方法,并在plugin.cfg
合适的位置添加插件的信息,即可完成自定义插件的实现,非常方便。
Kubernetes插件
Kubernetes在CoreDNS中也是以插件的形式提供的,主要提供后端的资源作为域名解析的源。
启动
同其他的插件一致,Kubernetes也是先执行init将插件注册到caddy中,然后调用setup
方法完成启动工作。看下setup
主要做什么工作。

kubernetesParse
解析Kubernetes插件相应的配置;接着调用InitKubeCache
初始化kubeClient
,同时调用newdnsController
初始化dnsController,通过dnsController与Kubernetes集群交互,获取集群内相应的数据;最后调用AddPlugin
将插件以链表的形式添加到配置内,完成初始化工作。
运行
运行以实现了ServeDNS
方法开始。主要就是通过解析不同的请求类型进入不同的处理逻辑。

ServiceBackend
完成域名的解析工作。

Services
主要根据特定的请求类型,获取其相应的所有的Services;Reverse
主要做域名反向解析的动作,即通过IP获取相对应的域名;Lookup
主要用来做上游DNSServer的查找工作;Records
主要做域名解析的工作,返回特定的记录。总结
CoreDNS代码的结构非常清晰,通过初始化时加载自定义的插件顺序,依次注入到caddy中去,并在执行的时候,按照定义的插件顺序依次执行,形成一个链式的调用结构。通过DNSServer的创建和多个插件的个性化实现构成整个统一的代码结构。