k8sDNS也经历一些变化,之前的架构是 kube-dns,现在换成了CoreDNS。本文先简单描述下kube-dns之后详细介绍CoreDNS。
宏观上讲, kube-dns分两块,一块是与k8s强耦合的后端DNS服务用于解析Service地址等集群DNS的服务发现。一块是dnsmasq作为前端cache。总体上功能紧凑,但在定制开发方面很不友好。
基于caddy开发的DNS插件框架,并且内置了很多已有的DNS相关插件。具体的插件位置在 ./plugin 文件夹下。
通过配置文件来实现plugin的静态编译。可以通过Makefile来看到
core/plugin/zplugin.go core/dnsserver/zdirectives.go: plugin.cfg
GO111MODULE=on go generate coredns.go
plugin.cfg文件中放的即是CoreDNS支持的插件名称以及对应的package库。因为后面要去引用这个package从而调用他们的init函数。
coredns.go中调用的gen指令的地方
///home/ry/go/src/github.com/coredns/coredns/coredns.go:3
//go:generate go run directives_generate.go
//go:generate go run owners_generate.go
查看对应源码,会生成这两个文件: core/plugin/zplugin.go core/dnsserver/zdirectives.go 一个是引用全部的plugin,一个是生成一个string slice并赋值给一个变量: Directives。 import某个插件为的是调用其init函数。而init函数中的作用是让CoreDNS依赖的caddy系统认识这个插件。可以找一个插件比如log来看一下注册细节。
genImports("core/plugin/zplugin.go", "plugin", mi)
genDirectives("core/dnsserver/zdirectives.go", "dnsserver", md)
==> 调用 init方法,以log为例
func init() {
caddy.RegisterPlugin("log", caddy.Plugin{
ServerType: "dns",
Action: setup,
})
}
caddy是一个http2框架而CoreDNS是一个DNS服务,这里要让http框架“切换”7层协议。这是怎么做到的? 答案是caddy的框架很框架。怎么监听怎么处理请求都是有个interface来定义的。具体的定义如下:
type Server interface {
TCPServer
UDPServer
}
type TCPServer interface {
// Listen starts listening by creating a new listener
// and returning it. It does not start accepting
// connections. For UDP-only servers, this method
// can be a no-op that returns (nil, nil).
Listen() (net.Listener, error)
// Serve starts serving using the provided listener.
// Serve must start the server loop nearly immediately,
// or at least not return any errors before the server
// loop begins. Serve blocks indefinitely, or in other
// words, until the server is stopped. For UDP-only
// servers, this method can be a no-op that returns nil.
Serve(net.Listener) error
}
caddy定义的Server接口是交给具体的插件来实现的。翻译成人话大概就是: 告诉caddy怎么监听,怎么处理请求,caddy会帮我们监听,请求来了会自动调用我们给的处理函数。而这一切又通过caddy所谓的context来统一封装。具体如下:
type Context interface {
// Called after the Caddyfile is parsed into server
// blocks but before the directives are executed,
// this method gives you an opportunity to inspect
// the server blocks and prepare for the execution
// of directives. Return the server blocks (which
// you may modify, if desired) and an error, if any.
// The first argument is the name or path to the
// configuration file (Caddyfile).
//
// This function can be a no-op and simply return its
// input if there is nothing to do here.
InspectServerBlocks(string, []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error)
// This is what Caddy calls to make server instances.
// By this time, all directives have been executed and,
// presumably, the context has enough state to produce
// server instances for Caddy to start.
MakeServers() ([]Server, error)
}
给一个context,这个context用来创建Server,而这个Server要会怎么监听,怎么处理请求。就这样,caddy完美的把自己变成了一个框架,而不是gin这样的http框架。context怎么给过来?则要注册到caddy框架中。
///home/ry/go/src/github.com/coredns/coredns/core/dnsserver/register.go:23
func init() {
flag.StringVar(&Port, serverType+".port", DefaultPort, "Default port")
caddy.RegisterServerType(serverType, caddy.ServerType{
Directives: func() []string { return Directives },
DefaultInput: func() caddy.Input {
return caddy.CaddyfileInput{
Filepath: "Corefile",
Contents: []byte(".:" + Port + " {\nwhoami\n}\n"),
ServerTypeName: serverType,
}
},
NewContext: newContext,
})
}
这样就实现了CoreDNS到caddy的打通。caddy中的这行代码标记着caddy到CoreDNS的完美切换:
// /home/ry/go/src/github.com/coredns/coredns/vendor/github.com/mholt/caddy/caddy.go:604
inst.context = stype.NewContext(inst)
抓住这些核心则看细节就很顺了。细节不再展开。
- 优先用proxy: the proxy plugin supports more protocols than forward
- https://github.com/coredns/example https://coredns.io/2017/03/01/how-to-add-plugins-to-coredns/ plugin example