在上一篇文章中,我们了解了 CoreDNS 的基本概念、安装和配置文件的说明,与 CoreDNS 中的插件,本文将从零开始,深入学习 CoreDNS 插件开发的完整流程,让您能够开发出满足特定业务需求的高质量插件。在最后我们学习一下 CoreDNS 中的外部插件用以引入开发思路,来解决我们平台中的一些难点。
代码构成
在学习开发插件之前,我们先需要了解下 CoreDNS 的代码目录构成。
|
|
插件开发核心概念
在 CoreDNS 中,插件开发核心概念是 “注册” [1] (Register) ,详细的步骤大致分为如下几步:
- Configration, 用户通过 CoreDNS 的默认
Corefile
完成配置加载,这个文件也可以按照第一章中提到的-conf
参数来指定。 - Setup, 解析配置和内部数据结构,并完成初始化。
- Handler, 是 Plugin 的入口,用户必须对自己的插件继承这个 plugin.Handler 接口。
- Plugin Integration, 用户在 plugin.cfg 整合自己的插件代码,并完成编译。
注册 (Register) 和设置 (Setup) 你的插件
注册 (Register)
首先你的插件需要下面函数的调用才能完成注册的操作,每一个插件拥有一个 name(实例中叫 foo)
|
|
在上面代码所示,setup 函数将会被调用以完成注册。
设置 (Setup)
Setup 函数的功能就是载入配置文件与填充内部结构,他的参数是 *caddy.Controller
,他的返回值是 error
|
|
如果我们在配置中配置了下面的参数
|
|
那么在获取这个参数可以使用下面代码
|
|
用户可以使用 c.Next()
来迭代参数,如果 c.Next()
为 True,因为参数可能存在多个,所以可以迭代多次来完成参数的获取。需要主义的是,第一个参数通常为 Plugin name。在大多数教程中都交给用户是跳过第一个参数。
编写插件 (Handler)
Handler 可以称之为你的插件的入口,定义插件的要求如下:
- 因为
plugin.Handler
接口的定义,定义 handler 必须要实现方法 ServeDNS ,ServeDNS 是处理每个 DNS 查询请求的方法。 - 以及至少一个属性 “next”,next 表示在 CoreDNS 的请求链 (Chain) 中的下一个插件。
- 必须包含
Name()
方法
实现自定义插件的伪代码如下:
|
|
在上面可以看到,ServeDNS()
返回两个参数,响应 (int) 和错误 (error),如果 error 不为空,则返回 SERVFAIL 到客户端。响应代码会告诉 CoreDNS 插件链 ”是否已经写入回复“,如果没有写入,CoreDNS会自行处理这个情况。
CoreDNS 会处理下面场景 [2] :
- SERVFAIL (dns.RcodeServerFailure)
- REFUSED (dns.RcodeRefused)
- FORMERR (dns.RcodeFormatError)
- NOTIMP (dns.RcodeNotImplemented)
还有一些常用的返回码:
- RcodeSuccess: No error
- RcodeServerFailure: Server failure
- RcodeNameError: Domain doesn’t exist
- RcodeNotImplemented: Record type not implemented
将自己插件编译到CoreDNS中
CoreDNS 提供了两种方式可以运行自定义插件,即 “编译时配置” (Compile-time Configuration) 和 “包装源码” 方式,
编译时配置
“编译时配置” (Compile-time Configuration) 也是官方在描述编译自定义插件的方式,就是在 CoreDNS 源码的 “plugin.cfg
” 文件内增加你的插件然后运行 go generate。这个方式你必须 Clone CoreDNS 的源码进行修改和编译。
修改 plugin.cfg
|
|
Wrapping code
也可以根据 CoreDNS 官方的模式进行修改源码,在代码 core/plugin/zplugin.go 可以看到
|
|
然后在代码 core/coredns.go 中可以看到,wrap 了 github.com/coredns/coredns/core/dnsserver
|
|
在 coredns.go 中可以看到是这样执行的
|
|
通过这个我们可以推理出两种方式来编译外部自定义插件
- 把插件放置到指定位置,修改代码,将其引入。
- 自行引入 coremain, dnsserver;接着运行。
方法2,如下代码所示 [3]
|
|
最后编译自己的 CoreDNS Server 即可。
外部插件源码学习
通过前面部分我们掌握了自定义 CoreDNS 插件的 Specification,下面将以 mysql 插件来深入的学习开发自定义插件。
mysql 插件是允许用户使用 mysql 作为 zone 数据持久化的介质,通过配置可以预估他的代码内容。
|
|
通过上面配置可以知道,在 setup 中一定是迭代所有的参数解析为插件运行的配置,正如代码 coredns_mysql/setup.go 所示
|
|
上面可以看到通过迭代 c.Next()
,并遗弃第一个(第一个作为插件名称)然后为为每个配置赋值。
代码 handler.go 中有定义其配置的数据结构
|
|
在大致了解了代码结构后,在了解下文件结构
文件 | 作用 |
---|---|
handler.go | 定义了 handler 的实现 |
mysql.go | model 相关的操作 |
setup.go | 配置 setup函数,初始化配置,并填充到配置数据结构中 |
type.go | 用于定义数据库表类型和查询结果的内容 |
开发第一个提供静态 DNS 查询的插件
通过上面的学习,在这个任务中,我们来开发一个 “静态记录插件”。这个插件可以从配置文件中读取静态的 DNS 记录并提供查询服务。
实现基本的 DNS 记录响应
首先定义插件的数据结构
|
|
Setup: 将插件注册到 CoreDNS
|
|
Configration - 配置插件可以解析域
|
|
上面配置定义了几条静态 DNS 记录,当客户端查询对应域名时,插件会返回配置的记录。
Compile - 将插件整合到 CoreDNS 中
因为这里我们不是作为一个公共仓库,也不是使用载入 coredns 主包的方式进行的,所以我们需要把代码放置于 coredns plugins/staticdns 中
然后在 core/plugin/zplugin.go 中增加下面一行
|
|
然后编译代码
|
|
总结
在本章节中,详细的展开对 coredns 插件的编写部分,通过 CoreDNS 目录结构,代码构成 以及 CoreDNS 自定义插件编写规范,简单学习了 CoreDNS的源码;通过外部插件 mysql 学习了插件定义的思路,以及自定义了一个 staticdns 插件三个部分完成学习 coredns 自定义插件。
在下一篇文章中,将从系统需求开始,一步步学习完成系统设计,以为后面实高性能 DNS 域名平台做好准备。
Reference
[1] How to Register a CoreDNS Plugin?
[2] Writing Plugins