CoreDNS是使用go语言编写的快速灵活的DNS服务,采用链式插件模式,每个插件实现独立的功能,底层协议可以是tcp/udp,也可以是TLS,gRPC等。默认监听所有ip地址,可使用bind插件指定监听指定地址。
配置文件
格式如下
[SCHEME://]ZONE [[SCHEME://]ZONE]...[:PORT] {
[PLUGIN]...
}
SCHEME是可选的,默认值为dns://,也可以指定为tls://,grpc://或者https://。
ZONE是可选的,指定了此dnsserver可以服务的域名前缀,如果不指定,则默认为root,表示可以接收所有的dns请求。
PORT是选项的,指定了监听端口号,默认为53,如果这里指定了端口号,则不能通过参数-dns.port覆盖。
一块上面格式的配置表示一个dnsserver,称为serverblock,可以配置多个serverblock表示多个dnsserver。
下面通过一个例子说明,如下配置文件指定了4个serverblock,即4个dnsserver,第一个监听端口5300,后面三个监听同一个端口53,每个dnsserver指定了特定的插件。
coredns.io:5300 {
file /etc/coredns/zones/coredns.io.db
}
example.io:53 {
log
errors
file /etc/coredns/zones/example.io.db
}
example.net:53 {
file /etc/coredns/zones/example.net.db
}
.:53 {
kubernetes
errors
log
health
}
下图为配置的简略图
a. 从图中可看到插件执行顺序不是配置文件中的顺序,这是因为插件执行顺序是在源码目录中的plugin.cfg指定的,一旦编译后,顺序就固定了。
b. .根serverblock虽然指定了health,但是图中却没有,这是因为health插件不参与dns请求的处理。能处理dns请求的插件必须提供如下两个接口函数。
Handler interface {
ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
Name() string
}
dns请求处理流程
收到dns请求后,首先根据域名匹配zone找到对应的dnsserver(最长匹配优先),如果没有匹配到,则使用默认的root dnsserver。
找到dnsserver后,就要按照插件顺序执行其中配置的插件,当然并不是配置的插件都会被执行,如果某个插件成功找到记录,则返回成功,否则根据插件是否配置了fallthrough等来决定是否执行下一个插件。
源码分析
plugin.cfg
源码目录下的plugin.cfg指定了插件执行顺序,如果想添加插件,可按格式添加到指定位置。
metadata:metadata
geoip:geoip
cancel:cancel
tls:tls
reload:reload
nsid:nsid
bufsize:bufsize
root:root
bind:bind
debug:debug
trace:trace
ready:ready
health:health
pprof:pprof
prometheus:metrics
errors:errors
log:log
dnstap:dnstap
local:local
dns64:dns64
acl:acl
any:any
chaos:chaos
loadbalance:loadbalance
cache:cache
rewrite:rewrite
header:header
dnssec:dnssec
autopath:autopath
minimal:minimal
template:template
transfer:transfer
hosts:hosts
route53:route53
azure:azure
clouddns:clouddns
k8s_external:k8s_external
kubernetes:kubernetes
file:file
auto:auto
secondary:secondary
etcd:etcd
loop:loop
forward:forward
grpc:grpc
erratic:erratic
whoami:whoami
on:github.com/coredns/caddy/onevent
sign:sign
源码目录下的Makefile根据plugin.cfg生成了两个go文件:zplugin.go和zdirectives.go。
core/plugin/zplugin.go core/dnsserver/zdirectives.go: plugin.cfg
go generate coredns.go
go get
core/plugin/zplugin.go会导入所有的插件,执行所有插件的init函数。
import (
// Include all plugins.
_ "github.com/coredns/caddy/onevent"
_ "github.com/coredns/coredns/plugin/acl"
_ "github.com/coredns/coredns/plugin/any"
_ "github.com/coredns/coredns/plugin/auto"
_ "github.com/coredns/coredns/plugin/autopath"
_ "github.com/coredns/coredns/plugin/azure"
_ "github.com/coredns/coredns/plugin/bind"
_ "github.com/coredns/coredns/plugin/bufsize"
_ "github.com/coredns/coredns/plugin/cache"
_ "github.com/coredns/coredns/plugin/cancel"
_ "github.com/coredns/coredns/plugin/chaos"
_ "github.com/coredns/coredns/plugin/clouddns"
_ "github.com/coredns/coredns/plugin/debug"
_ "github.com/coredns/coredns/plugin/dns64"
_ "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/file"
_ "github.com/coredns/coredns/plugin/forward"
_ "github.com/coredns/coredns/plugin/geoip"
_ "github.com/coredns/coredns/plugin/grpc"
_ "github.com/coredns/coredns/plugin/header"
_ "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/local"
_ "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/minimal"
_ "github.com/coredns/coredns/plugin/nsid"
_ "github.com/coredns/coredns/plugin/pprof"
_ "github.com/coredns/coredns/plugin/ready"
_ "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/sign"
_ "github.com/coredns/coredns/plugin/template"
_ "github.com/coredns/coredns/plugin/tls"
_ "github.com/coredns/coredns/plugin/trace"
_ "github.com/coredns/coredns/plugin/transfer"
_ "github.com/coredns/coredns/plugin/whoami"
)
core/dnsserver/zdirectives.go将所有插件名字放在一个数组中。
var Directives = []string{
"metadata",
"geoip",
"cancel",
"tls",
"reload",
"nsid",
"bufsize",
"root",
"bind",
"debug",
"trace",
"ready",
"health",
"pprof",
"prometheus",
"errors",
"log",
"dnstap",
"local",
"dns64",
"acl",
"any",
"chaos",
"loadbalance",
"cache",
"rewrite",
"header",
"dnssec",
"autopath",
"minimal",
"template",
"transfer",
"hosts",
"route53",
"azure",
"clouddns",
"k8s_external",
"kubernetes",
"file",
"auto",
"secondary",
"etcd",
"loop",
"forward",
"grpc",
"erratic",
"whoami",
"on",
"sign",
}
codedns 主函数
//coredns/coredns.go
import (
_ "github.com/coredns/coredns/core/plugin" // Plug in CoreDNS.
"github.com/coredns/coredns/coremain"
)
main
coremain.Run()
codedns.go 首先导入了包"github.com/coredns/coredns/core/plugin",此包内只有一个文件zplugin.go,此文件为自动生成的,主要导入了所有的插件,执行每个插件的init函数。
接着执行 run.go Run
//coredns/coremain/run.go
import (
...
"github.com/coredns/coredns/core/dnsserver"
)
func Run()
//解析参数
flag.Parse()
//如果指定了参数 version,则打印版本信息后退出
if version {
showVersion()
os.Exit(0)
}
//如果指定了参数 plugins,则只打印插件信息后退出
if plugins {
fmt.Println(caddy.DescribePlugins())
os.Exit(0)
}
//解析配置文件
corefile, err := caddy.LoadCaddyfile(serverType)
cdyfile, err := loadCaddyfileInput(serverType)
for _, l := range caddyfileLoaders {
//执行 confLoader
cdyfile, err := l.loader.Load(serverType)
}
instance, err := caddy.Start(corefile)
// Twiddle your thumbs
instance.Wait()
此文件又引入了包"github.com/coredns/coredns/core/dnsserver",其init函数在 dnsserver/register.go 文件中,如下所示,主要是注册了serverType
const serverType = "dns"
// DefaultPort is the default port.
const DefaultPort = transport.Port
Port = "53"
func init() {
flag.StringVar(&Port, serverType+".port", DefaultPort, "Default port")
flag.StringVar(&Port, "p", 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\nlog\n}\n"),
ServerTypeName: serverType,
}
},
NewContext: newContext,
})
}
剩下的就是解析参数,解析配置文件后,执行caddy.Start。
这里就是根据配置文件中指定的serverblock,执行插件的setup进行初始化,创建对应的server,开始监听dns请求
//caddy/caddy.go
func Start(cdyfile Input) (*Instance, error)
inst := &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})}
err := startWithListenerFds(cdyfile, inst, nil)
func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]restartTriple) error {
ValidateAndExecuteDirectives(cdyfile, inst, false)
//stypeName 为 dns
stypeName := cdyfile.ServerType()
//stype 通过 RegisterServerType 注册,在 //coredns/core/dnsserver/register.go init时注册
stype, err := getServerType(stypeName)
stype, ok := serverTypes[serverType]
if ok {
return stype, nil
}
...
inst.caddyfileInput = cdyfile
//func loadServerBlocks(serverType, filename string, input io.Reader) ([]caddyfile.ServerBlock, error)
sblocks, err := loadServerBlocks(stypeName, cdyfile.Path(), bytes.NewReader(cdyfile.Body()))
validDirectives := ValidDirectives(serverType)
serverBlocks, err := caddyfile.Parse(filename, input, validDirectives)
p := parser{Dispenser: NewDispenser(filename, input), validDirectives: validDirectives}
// NewDispenser returns a Dispenser, ready to use for parsing the given input.
func NewDispenser(filename string, input io.Reader) Dispenser {
tokens, _ := allTokens(input) // ignoring error because nothing to do with it
return Dispenser{
filename: filename,
tokens: tokens,
cursor: -1,
}
}
return p.parseAll()
//coredns/core/dnsserver/register.go:newContext
inst.context = stype.NewContext(inst)
//coredns/core/dnsserver/register.go:InspectServerBlocks
sblocks, err = inst.context.InspectServerBlocks(cdyfile.Path(), sblocks)
return executeDirectives(inst, cdyfile.Path(), stype.Directives(), sblocks, justValidate)
//遍历执行插件注册的 setup 函数
for _, dir := range directives {
for i, sb := range sblocks {
//获取插件的初始化函数 setup
setup, err := DirectiveAction(inst.serverType, dir)
if stypePlugins, ok := plugins[serverType]; ok {
if plugin, ok := stypePlugins[dir]; ok {
return plugin.Action, nil
}
}
//执行插件注册的 setup 函数
setup(controller)
//将 onStart 添加到数组 c.instance.OnStartup
c.OnStartup(onStart)
//每个插件的setup函数都会调用如下函数,注册插件handler
//AddPlugin -> c.Plugin = append(c.Plugin, m)
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
l.Next = next
return l
})
}
}
slist, err := inst.context.MakeServers()
errValid := h.validateZonesAndListeningAddresses()
for _, c := range h.configs {
c.Plugin = c.firstConfigInBlock.Plugin
c.ListenHosts = c.firstConfigInBlock.ListenHosts
c.Debug = c.firstConfigInBlock.Debug
c.TLSConfig = c.firstConfigInBlock.TLSConfig
}
//将监听相同地址的config放在同一个group。一个config表示一个 dnsserver
// we must map (group) each config to a bind address
groups, err := groupConfigsByListenAddr(h.configs)
groups := make(map[string][]*Config)
for _, conf := range configs {
for _, h := range conf.ListenHosts {
addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(h, conf.Port))
if err != nil {
return nil, err
}
addrstr := conf.Transport + "://" + addr.String()
groups[addrstr] = append(groups[addrstr], conf)
}
}
return groups, nil
//为同一个组的config创建一个server,多个插件共享一个底层server,上层通过
//zone区分请求是到哪个dnsserver的
// then we create a server for each group
var servers []caddy.Server
for addr, group := range groups {
// switch on addr
switch tr, _ := parse.Transport(addr); tr {
case transport.DNS:
s, err := NewServer(addr, group)
s := &Server{
Addr: addr,
zones: make(map[string]*Config),
graceTimeout: 5 * time.Second,
}
//site的类型是 Config,每个site表示一个dnsserver
for _, site := range group {
// set the config per zone
s.zones[site.Zone] = site
//遍历每个dnsserver配置的插件
//site.Plugin 为每个插件初始化setup时调用 dnsserver.GetConfig(c).AddPlugin 生成,
//顺序是按照数组 Directives 从前向后
// compile custom plugin for everything
var stack plugin.Handler
//从后向前逆序遍历 site.Plugin
for i := len(site.Plugin) - 1; i >= 0; i-- {
stack = site.Plugin[i](stack)
// register the *handler* also
site.registerHandler(stack)
c.registry[h.Name()] = h
if s.trace == nil && stack.Name() == "trace" {
// we have to stash away the plugin, not the
// Tracer object, because the Tracer won't be initialized yet
if t, ok := stack.(trace.Trace); ok {
s.trace = t
}
}
// Unblock CH class queries when any of these plugins are loaded.
if _, ok := EnableChaos[stack.Name()]; ok {
s.classChaos = true
}
}
//pluginChain 为第一个插件的 handler,收到dns请求后,先执行第一个插件的 handler
//在 ServeDNS(core/dnsserver/server.go) 函数中执行 pluginChain
site.pluginChain = stack
}
return s, nil
servers = append(servers, s)
...
}
}
return servers, nil
for _, startupFunc := range inst.OnStartup {
//比如 kubernetes 的 onStart 函数
err = startupFunc()
}
startServers(slist, inst, restartFds)
for _, s := range serverList {
if ln == nil {
ln, err = s.Listen()
//core/dnsserver/server.go:Listen
l, err := reuseport.Listen("tcp", s.Addr[len(transport.DNS+"://"):])
return l, nil
}
if pc == nil {
pc, err = s.ListenPacket()
//core/dnsserver/server.go:ListenPacket
p, err := reuseport.ListenPacket("udp", s.Addr[len(transport.DNS+"://"):])
return p, nil
}
inst.servers = append(inst.servers, ServerListener{server: s, listener: ln, packet: pc})
}
for _, s := range inst.servers {
func(s Server, ln net.Listener, pc net.PacketConn, inst *Instance) {
go func() {
defer func() {
inst.wg.Done()
stopWg.Done()
}()
errChan <- s.Serve(ln)
}()
go func() {
defer func() {
inst.wg.Done()
stopWg.Done()
}()
errChan <- s.ServePacket(pc)
}()
}(s.server, s.listener, s.packet, inst)
}
}
tcp协议调用Serve,udp协议调用ServePacket
//core/dnsserver/server.go
// Serve starts the server with an existing listener. It blocks until the server stops.
// This implements caddy.TCPServer interface.
func (s *Server) Serve(l net.Listener) error {
s.m.Lock()
s.server[tcp] = &dns.Server{Listener: l, Net: "tcp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
ctx := context.WithValue(context.Background(), Key{}, s)
ctx = context.WithValue(ctx, LoopKey{}, 0)
s.ServeDNS(ctx, w, r)
})}
s.m.Unlock()
return s.server[tcp].ActivateAndServe()
}
// ServePacket starts the server with an existing packetconn. It blocks until the server stops.
// This implements caddy.UDPServer interface.
func (s *Server) ServePacket(p net.PacketConn) error {
s.m.Lock()
s.server[udp] = &dns.Server{PacketConn: p, Net: "udp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
ctx := context.WithValue(context.Background(), Key{}, s)
ctx = context.WithValue(ctx, LoopKey{}, 0)
s.ServeDNS(ctx, w, r)
})}
s.m.Unlock()
return s.server[udp].ActivateAndServe()
}
收到DNS请求后,调用ServeDNS,根据域名匹配dnsserver,如果没有匹配不到则使用根dnsserver,然后执行dnsserver中配置的插件
// ServeDNS is the entry point for every request to the address that
// is bound to. It acts as a multiplexer for the requests zonename as
// defined in the request so that the correct zone
// (configuration and plugin stack) will handle the request.
func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
// The default dns.Mux checks the question section size, but we have our
// own mux here. Check if we have a question section. If not drop them here.
if r == nil || len(r.Question) == 0 {
errorAndMetricsFunc(s.Addr, w, r, dns.RcodeServerFailure)
return
}
// Wrap the response writer in a ScrubWriter so we automatically make the reply fit in the client's buffer.
w = request.NewScrubWriter(r, w)
q := strings.ToLower(r.Question[0].Name)
var (
off int
end bool
dshandler *Config
)
//根据dns请求的域名作为zone(最长匹配优先),遍历 s.zones 进行匹配(每个zone表示一个dnsserver),
//如果匹配到了,则设置 dshandler = h
for {
if h, ok := s.zones[q[off:]]; ok {
if h.pluginChain == nil { // zone defined, but has not got any plugins
errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused)
return
}
if r.Question[0].Qtype != dns.TypeDS {
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
if !plugin.ClientWrite(rcode) {
errorFunc(s.Addr, w, r, rcode)
}
return
}
// The type is DS, keep the handler, but keep on searching as maybe we are serving
// the parent as well and the DS should be routed to it - this will probably *misroute* DS
// queries to a possibly grand parent, but there is no way for us to know at this point
// if there is an actual delegation from grandparent -> parent -> zone.
// In all fairness: direct DS queries should not be needed.
dshandler = h
}
off, end = dns.NextLabel(q, off)
if end {
break
}
}
//匹配到zone,执行dnsserver的插件的 ServeDNS。
//如果插件的 ServeDNS 直接返回了(比如k8s插件查找成功时),则只执行一个插件,
//如果插件的 ServeDNS 调用plugin.NextOrFailure,则开始执行下一个插件 ServeDNS 了,
//依次类推,直到有插件返回成功或者失败。
if r.Question[0].Qtype == dns.TypeDS && dshandler != nil && dshandler.pluginChain != nil {
// DS request, and we found a zone, use the handler for the query.
rcode, _ := dshandler.pluginChain.ServeDNS(ctx, w, r)
if !plugin.ClientWrite(rcode) {
errorFunc(s.Addr, w, r, rcode)
}
return
}
//如果dnsserver没有匹配的zone,则最后尝试执行根zone,即配置文件中指定的"."
// Wildcard match, if we have found nothing try the root zone as a last resort.
if h, ok := s.zones["."]; ok && h.pluginChain != nil {
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
if !plugin.ClientWrite(rcode) {
errorFunc(s.Addr, w, r, rcode)
}
return
}
// Still here? Error out with REFUSED.
errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused)
}
以k8s插件为例
//k8s插件的 ServeDNS 函数
// ServeDNS implements the plugin.Handler interface.
func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
qname := state.QName()
zone := plugin.Zones(k.Zones).Matches(qname)
if zone == "" {
return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r)
}
zone = qname[len(qname)-len(zone):] // maintain case of original query
state.Zone = zone
var (
records []dns.RR
extra []dns.RR
truncated bool
err error
)
switch state.QType() {
case dns.TypeA:
records, truncated, err = plugin.A(ctx, &k, zone, state, nil, plugin.Options{})
case dns.TypeAAAA:
records, truncated, err = plugin.AAAA(ctx, &k, zone, state, nil, plugin.Options{})
case dns.TypeTXT:
records, truncated, err = plugin.TXT(ctx, &k, zone, state, nil, plugin.Options{})
case dns.TypeCNAME:
records, err = plugin.CNAME(ctx, &k, zone, state, plugin.Options{})
case dns.TypePTR:
records, err = plugin.PTR(ctx, &k, zone, state, plugin.Options{})
case dns.TypeMX:
records, extra, err = plugin.MX(ctx, &k, zone, state, plugin.Options{})
case dns.TypeSRV:
records, extra, err = plugin.SRV(ctx, &k, zone, state, plugin.Options{})
case dns.TypeSOA:
if qname == zone {
records, err = plugin.SOA(ctx, &k, zone, state, plugin.Options{})
}
case dns.TypeAXFR, dns.TypeIXFR:
return dns.RcodeRefused, nil
case dns.TypeNS:
if state.Name() == zone {
records, extra, err = plugin.NS(ctx, &k, zone, state, plugin.Options{})
break
}
fallthrough
default:
// Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
fake := state.NewWithQuestion(state.QName(), dns.TypeA)
fake.Zone = state.Zone
_, _, err = plugin.A(ctx, &k, zone, fake, nil, plugin.Options{})
}
//没有查找到 dns 记录时,如果配置了fallthrough,则执行下一个插件,
//否则返回错误信息
if k.IsNameError(err) {
if k.Fall.Through(state.Name()) {
return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r)
}
if !k.APIConn.HasSynced() {
// If we haven't synchronized with the kubernetes cluster, return server failure
return plugin.BackendError(ctx, &k, zone, dns.RcodeServerFailure, state, nil /* err */, plugin.Options{})
}
return plugin.BackendError(ctx, &k, zone, dns.RcodeNameError, state, nil /* err */, plugin.Options{})
}
if err != nil {
return dns.RcodeServerFailure, err
}
if len(records) == 0 {
return plugin.BackendError(ctx, &k, zone, dns.RcodeSuccess, state, nil, plugin.Options{})
}
//查到dns记录,返回dns响应
m := new(dns.Msg)
m.SetReply(r)
m.Truncated = truncated
m.Authoritative = true
m.Answer = append(m.Answer, records...)
m.Extra = append(m.Extra, extra...)
w.WriteMsg(m)
return dns.RcodeSuccess, nil
}
// SRV returns SRV records from the Backend.
// If the Target is not a name but an IP address, a name is created on the fly.
func SRV(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) {
//比如对于 kubernetes 插件来说,b.Services 为 coredns/plugin/kubernetes/kubernetes.go:Services
services, err := b.Services(ctx, state, false, opt)
dup := make(map[item]struct{})
lookup := make(map[string]struct{})
// Looping twice to get the right weight vs priority. This might break because we may drop duplicate SRV records latter on.
w := make(map[int]int)
for _, serv := range services {
weight := 100
if serv.Weight != 0 {
weight = serv.Weight
}
if _, ok := w[serv.Priority]; !ok {
w[serv.Priority] = weight
continue
}
w[serv.Priority] += weight
}
for _, serv := range services {
// Don't add the entry if the port is -1 (invalid). The kubernetes plugin uses port -1 when a service/endpoint
// does not have any declared ports.
if serv.Port == -1 {
continue
}
w1 := 100.0 / float64(w[serv.Priority])
if serv.Weight == 0 {
w1 *= 100
} else {
w1 *= float64(serv.Weight)
}
weight := uint16(math.Floor(w1))
// weight should be at least 1
if weight == 0 {
weight = 1
}
what, ip := serv.HostType()
switch what {
case dns.TypeCNAME:
srv := serv.NewSRV(state.QName(), weight)
records = append(records, srv)
if _, ok := lookup[srv.Target]; ok {
break
}
lookup[srv.Target] = struct{}{}
if !dns.IsSubDomain(zone, srv.Target) {
m1, e1 := b.Lookup(ctx, state, srv.Target, dns.TypeA)
if e1 == nil {
extra = append(extra, m1.Answer...)
}
m1, e1 = b.Lookup(ctx, state, srv.Target, dns.TypeAAAA)
if e1 == nil {
// If we have seen CNAME's we *assume* that they are already added.
for _, a := range m1.Answer {
if _, ok := a.(*dns.CNAME); !ok {
extra = append(extra, a)
}
}
}
break
}
// Internal name, we should have some info on them, either v4 or v6
// Clients expect a complete answer, because we are a recursor in their view.
state1 := state.NewWithQuestion(srv.Target, dns.TypeA)
addr, _, e1 := A(ctx, b, zone, state1, nil, opt)
if e1 == nil {
extra = append(extra, addr...)
}
// TODO(miek): AAAA as well here.
case dns.TypeA, dns.TypeAAAA:
addr := serv.Host
serv.Host = msg.Domain(serv.Key)
srv := serv.NewSRV(state.QName(), weight)
if ok := isDuplicate(dup, srv.Target, "", srv.Port); !ok {
records = append(records, srv)
}
if ok := isDuplicate(dup, srv.Target, addr, 0); !ok {
extra = append(extra, newAddress(serv, srv.Target, ip, what))
}
}
}
return records, extra, nil
}
func (k *Kubernetes) Services(ctx context.Context, state request.Request, exact bool, opt plugin.Options) (svcs []msg.Service, err error) {
s, e := k.Records(ctx, state, false)
r, e := parseRequest(state.Name(), state.Zone)
services, err := k.findServices(r, state.Zone)
//根据dns请求的service和namespace获取index
idx := object.ServiceKey(r.service, r.namespace)
//根据index从缓存获取 service 信息
serviceList = k.APIConn.SvcIndex(idx)
endpointsListFunc = func() []*object.Endpoints { return k.APIConn.EpIndex(idx) }
zonePath := msg.Path(zone, coredns)
for _, svc := range serviceList {
}
return services, err
参考
//如何写coredns插件
http://dockone.io/article/9620
//coredns源码分析
https://wenku.baidu.com/view/34cabc1e346baf1ffc4ffe4733687e21af45ff7c.html
https://blog.csdn.net/zhonglinzhang/article/details/99679323
https://www.codercto.com/a/89703.html
//NodeLocal DNSCache
https://www.cnblogs.com/sanduzxcvbnm/p/16013560.html
https://blog.csdn.net/xixihahalelehehe/article/details/118894971