乐于分享
好东西不私藏

想了解CoreDNS?这篇源码分析文章别错过

想了解CoreDNS?这篇源码分析文章别错过

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:metadatageoip:geoipcancel:canceltls:tlsreload:reloadnsid:nsidbufsize:bufsizeroot:rootbind:binddebug:debugtrace:traceready:readyhealth:healthpprof:pprofprometheus:metricserrors:errorslog:logdnstap:dnstaplocal:localdns64:dns64acl:aclany:anychaos:chaosloadbalance:loadbalancecache:cacherewrite:rewriteheader:headerdnssec:dnssecautopath:autopathminimal:minimaltemplate:templatetransfer:transferhosts:hostsroute53:route53azure:azureclouddns:clouddnsk8s_external:k8s_externalkubernetes:kubernetesfile:fileauto:autosecondary:secondaryetcd:etcdloop:loopforward:forwardgrpc:grpcerratic:erraticwhoami:whoamion:github.com/coredns/caddy/oneventsign:sign

源码目录下的Makefile根据plugin.cfg生成了两个go文件:zplugin.go和zdirectives.go

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.goimport (    _ "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.goimport (    ..."github.com/coredns/coredns/core/dnsserver")funcRun()//解析参数    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.PortPort = "53"funcinit() {    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.gofuncStart(cdyfile Input) (*Instance, error)    inst := &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})}    err := startWithListenerFds(cdyfile, inst, nil)funcstartWithListenerFds(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.funcNewDispenser(filename string, input io.Reader) Dispenser {                        tokens, _ := allTokens(input) // ignoring error because nothing to do with itreturn 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 = nextreturn 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 {returnnil, 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 groupvar servers []caddy.Serverfor addr, group := range groups {// switch on addrswitch 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表示一个dnsserverfor _, 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 everythingvar stack plugin.Handler//从后向前逆序遍历 site.Pluginfor i := len(site.Plugin) - 1; i >= 0; i-- {                            stack = site.Plugin[i](stack)// register the *handler* also                            site.registerHandler(stack)                                c.registry[h.Name()] = hif s.trace == nil && stack.Name() == "trace" {// we have to stash away the plugin, not the// Tracer object, because the Tracer won't be initialized yetif 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, nilfor _, 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) {gofunc() {deferfunc() {                        inst.wg.Done()                        stopWg.Done()                    }()                    errChan <- s.Serve(ln)                }()gofunc() {deferfunc() {                        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 = hfor {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) (interror) {    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 = zonevar (        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, nilcase dns.TypeNS:if state.Name() == zone {            records, extra, err = plugin.NS(ctx, &k, zone, state, plugin.Options{})break        }fallthroughdefault:// 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 failurereturn 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    }iflen(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.funcSRV(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 := 100if serv.Weight != 0 {            weight = serv.Weight        }if _, ok := w[serv.Priority]; !ok {            w[serv.Priority] = weightcontinue        }        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 1if 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.htmlhttps://blog.csdn.net/zhonglinzhang/article/details/99679323https://www.codercto.com/a/89703.html

//NodeLocal DNSCachehttps://www.cnblogs.com/sanduzxcvbnm/p/16013560.htmlhttps://blog.csdn.net/xixihahalelehehe/article/details/118894971

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 想了解CoreDNS?这篇源码分析文章别错过

评论 抢沙发

8 + 2 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮