【kubernetes/k8s源码分析】coredns 源码分析之二启动流程

    CoreDns 采用插件机制,所有功能都是插件形式编写,用户可以扩展自己的插件

 

1. init 函数

    路径: core/dnsserver/register.go

        调用 caddy.RegisterServerType 注册了 dns 类型的服务,context,这里有两个参数

  • Directives,插件名称的 string 列表,也代表了代码中的目录
  • NewContext,是初始化 context 的
// Any flags defined here, need to be namespaced to the serverType other
// wise they potentially clash with other server types.
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,
	})
}

    1.1 newContext 函数实例化 dnsContext

     实现了接口 caddy.Context,Context 接口包含的方法

  • InspectServerBlocks(string, []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error)
  • MakeServers() ([]Server, error)
func newContext(i *caddy.Instance) caddy.Context {
	return &dnsContext{keysToConfigs: make(map[string]*Config)}
}

 

2. Run 函数

    路径 coredns/coremain/run.go

    caddy.LoadCaddyfile 加载配置文件,caddy.start 看看这个方法主要执行的操作

// Run is CoreDNS's main() function.
func Run() {
	caddy.TrapSignals()

	// Get Corefile input
	corefile, err := caddy.LoadCaddyfile(serverType)
	if err != nil {
		mustLogFatal(err)
	}

	// Start your engines
	instance, err := caddy.Start(corefile)
	if err != nil {
		mustLogFatal(err)
	}

	// Twiddle your thumbs
	instance.Wait()

    2.1 Start 函数

      以 caddy file 配置文件启动服务,具体 caddy 不详细分析,知道调用了  context 的 MakeServers 方法

      func Start(cdyfile Input) (*Instance, error)

           -->   func startWithListenerFds

                 -->  ValidateAndExecuteDirectives

                        -->  inst.context.InspectServerBlocks

                        -->  executeDirectives

                                -->  setup 调用每一插件目录下的 setup,加入插件列表

                 -->  inst.context.MakeServers()

                 -->  OnStartup

                 -->  startServers

                      -->   s.Listen

                      -->   s.ListenPacket

                      -->    s.Serve

                      -->   s.ServePacket

                 -->  srv.OnStartupComplete

    2.2 MakeServers 函数

      创建一系列的服务实例,可以看到包括四种方式对外提供 DNS 服务,分别是 TCP/UDP、GRPC、HTTPS 和 TLS

// MakeServers uses the newly-created siteConfigs to create and return a list of server instances.
func (h *dnsContext) MakeServers() ([]caddy.Server, error) {

	// Now that all Keys and Directives are parsed and initialized
	// lets verify that there is no overlap on the zones and addresses to listen for
	errValid := h.validateZonesAndListeningAddresses()
	if errValid != nil {
		return nil, errValid
	}

	// we must map (group) each config to a bind address
	groups, err := groupConfigsByListenAddr(h.configs)
	if err != nil {
		return nil, err
	}

 

3 NewServer 函数

     路径 core/dnsserver/server.go

       注册插件以及 handler,实现了 ServerDNS 和 Name 方法

// NewServer returns a new CoreDNS server and compiles all plugins in to it. By default CH class
// queries are blocked unless queries from enableChaos are loaded.
func NewServer(addr string, group []*Config) (*Server, error) {

	s := &Server{
		Addr:         addr,
		zones:        make(map[string]*Config),
		graceTimeout: 5 * time.Second,
	}

    TCPServer 接口

// TCPServer is a type that can listen and serve connections.
// A TCPServer must associate with exactly zero or one net.Listeners.
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
}

    3.1 Listen 实现了 caddy TCP 服务的 接口

// Listen implements caddy.TCPServer interface.
func (s *Server) Listen() (net.Listener, error) {
	l, err := listen("tcp", s.Addr[len(transport.DNS+"://"):])
	if err != nil {
		return nil, err
	}
	return l, nil
}

    3.2 ListenPacket 实现了 caddy UDP 服务的接口

// ListenPacket implements caddy.UDPServer interface.
func (s *Server) ListenPacket() (net.PacketConn, error) {
	p, err := listenPacket("udp", s.Addr[len(transport.DNS+"://"):])
	if err != nil {
		return nil, err
	}

	return p, nil
}

    3.3 Serve 函数实现了 caddy TCP 服务的接口

     可以看到注册的 handler,最终调用 ServerDNS 方法

// 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)
		s.ServeDNS(ctx, w, r)
	})}
	s.m.Unlock()

	return s.server[tcp].ActivateAndServe()
}

    3.4 ServePacket 函数实现了 caddy UDP 服务的接口

// 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)
		s.ServeDNS(ctx, w, r)
	})}
	s.m.Unlock()

	return s.server[udp].ActivateAndServe()
}

    3.5 ServerDNS 函数

      每个请求都会发送给 core/dnsserver/server.go 的 ServeDNS 进行处理

// ServeDNS is the entry point for every request to the address that s
// 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
	}

     3.5.1 找到匹配的 zone,分别调用插件的 ServerDNS 处理

example.io:53 {
    log
    errors
    file db.example.io
}

for {
	l := len(q[off:])
	for i := 0; i < l; i++ {
		b[i] = q[off+i]
		// normalize the name for the lookup
		if b[i] >= 'A' && b[i] <= 'Z' {
			b[i] |= ('a' - 'A')
		}
	}

	if h, ok := s.zones[string(b[:l])]; ok {
		if r.Question[0].Qtype != dns.TypeDS {
			if h.FilterFunc == nil {
				rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
				if !plugin.ClientWrite(rcode) {
					errorFunc(s.Addr, w, r, rcode)
				}
				return
			}
			// FilterFunc is set, call it to see if we should use this handler.
			// This is given to full query name.
			if h.FilterFunc(q) {
				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 actually 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
	}
}

     3.5.2 DS 请求

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
}

     3.5.3 未匹配则尝试默认 zone .

.:53 {
    kubernetes
    forward . 8.8.8.8
    log
    errors
    cache
}

// 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
}

 

总结:

   init 初始化调用 RegisterServerType 注册服务类型 dns

   init 初始化调用 caddy.RegisterPlugin 注册一系列类型为 dns 的插件

   caddy 加载配置 caddyFile 文件,并启动 Start 方法,启动四种 server 提供 DNS 服务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值