CoreDNS粗解( 四 )

可以看到监听的处理调用链还是比较长的 。
小结Coredns默认会同时监听tcp和udp, 基于解析的配置文件会构造一个pluginChain, 而这个pluginChain就如它的名字那样直白 , 将插件包装成一个链条依次执行以完成dns解析 。
自定义插件编写因为caddy的优秀的插件系统 , 所以扩展起来很方便 。
编写插件大致分为一下几步

  • 复制代码框架(模板)
  • 注入插件代码
  • 重新生成插件列表
  • 编译运行
复制代码框架(模板)要开发自己的插件首先要下载coredns的源代码, coredns的代码结构如下:
tree .├── ADOPTERS.md├── CODE_OF_CONDUCT.md -> .github/CODE_OF_CONDUCT.md├── CODEOWNERS├── CONTRIBUTING.md -> .github/CONTRIBUTING.md├── core├── coredns.1.md├── coredns.go├── corefile.5.md├── coremain├── directives_generate.go├── Dockerfile├── go.mod├── go.sum├── GOVERNANCE.md├── LICENSE├── Makefile├── Makefile.doc├── Makefile.docker├── Makefile.release├── man├── notes├── owners_generate.go├── pb├── plugin├── plugin.cfg├── plugin.md├── README.md├── request├── SECURITY.md -> .github/SECURITY.md└── test官方提供了一个可以直接运行的示例:
https://github.com/coredns/example, 你可以拷贝下来做成自己的一个模块, 比如:
go mod init github.com/{github账号}/{你的仓库名}或者将其直接复制到coredns的plugin目录
【CoreDNS粗解】这里使用第二种方法, 本文的插件名叫dforward
所以目录结构如下:
.├── coredns.go├── directives_generate.go├── Dockerfile├── go.mod├── go.sum├── plugin│├── acl│├── example# 自定插件在这│# 省略其他插件名└── test├── auto_test.go63 directories, 196 files注入插件代码为了让我们的插件集成到coredns里面 , 我们需要编辑plugin.cfg文件以及重新生成代码 。
从上面的源代码阅读我们知道插件的顺序很重要 , 所以不能将插件的顺序放在太前 , 假设我们的插件放在第一位 , 那么log, errors等插件就都不会在我们自定义的插件前被调用 , 还有就是cache插件就不能正常缓存了 , 当然了 , 如果你的插件就是类似于log, errors, cache等插件的功能 , 那么你可以将其放在较前面的顺序 , 具体顺序应该具体分析 。
效果如下:
loop:loopexample:example# 你的插件在这个位置forward:forwardgrpc:grpc如果你是使用第一种方法注入编写插件 , 那么效果如下:
loop:loopdforward:github.com/{github账号}/{你的仓库名}# 你的插件在这个位置forward:forwardgrpc:grpc重新生成插件列表重新生成相关代码 , 并不复杂 , 只需要在coredns代码根目录执行一条命令即可 。
go generate如果没有出现错误, 你会发现corednsserverzdirectives.go 和corepluginzplugin.go两个文件出现了dforward的相关信息 。
编译运行然后就可以在Corefile文件里面使用example的指令了 。
example这个指令的唯一功能就是在日志中输出一个example 。
小结编写Coredns的插件并不复杂 , 唯一要考虑的是 , 是不是需要编写自己的插件 , 可以先了解内置的插件列表以及外部的插件列表中的各个插件功能在决定是否需要编写 , 很多时候是不需要 , 如果你真的需要编写一个自己的插件来满足特定的功能 , 也不需要自己实现各种功能 , 比如转发你可以直接调用forward插件 , 缓存可以直接调用cache插件等 , 再者就是coredns有许多常用的工具函数 , 这些放在下一个段落 。
一些常用的工具函数一些在域名解析中常用到的函数等
判断一个域名是否是一个zone(可以简单认为是域名)列表的子域名 。import ( "fmt" "github.com/coredns/coredns/plugin")func main() { zones := []string{"a.com.", "b.com."} fmt.Println(plugin.Zones(zones).Matches("xx.a.com.")) fmt.Println(plugin.Zones(zones).Matches("xx.c.com.")) // 没匹配上就会输出空字符串}输出如下:
a.com.
如果是很多域名的话 , 建议搞一个前缀树来匹配 , 因为这里的Matches方法是便利列表来匹配的 。


推荐阅读