谈谈名字服务Polaris的sidecar模式

2023-08-19 09:46:18 浏览数 (1)

源码地址:https://github.com/polarismesh/polaris-sidecar

polaris-sidecar 作为 polaris 的本地边车代理,提供两个可选功能模式:

  • 本地 DNS:使用 DNS 解析的方式访问北极星上的服务
  • 服务网格:通过劫持流量的方式实现服务发现和治理,开发侵入性低

解释器接口

代码语言:javascript复制
type NamingResolver interface {
   // Name will return the name to resolver
   Name() string
   // Initialize will init the resolver on startup
   Initialize(c *ConfigEntry) error
   // Start the plugin runnable
   Start(context.Context)
   // Destroy will destroy the resolver on shutdown
   Destroy()
   // ServeDNS is like dns.Handler except ServeDNS may return an response or nil
   ServeDNS(context.Context, dns.Question) *dns.Msg
}

dns和mesh两种模式都分别实现了这几个接口

初始化

代码语言:javascript复制
func newAgent(configFile string, bootConfig *BootConfig) (*Agent, error) {
   var err error
   polarisAgent := &Agent{}
   polarisAgent.config, err = parseYamlConfig(configFile, bootConfig)
   if nil != err {
      log.Errorf("[agent] fail to parse sidecar config, err: %v", err)
      return nil, err
   }
   nameservers, searchNames := parseResolvConf(polarisAgent.config.bindLocalhost())
   log.Infof("[agent] finished to parse /etc/resolv.conf, nameservers %s, search %s", nameservers, searchNames)
   if len(polarisAgent.config.Recurse.NameServers) == 0 {
      polarisAgent.config.Recurse.NameServers = nameservers
   }
   log.Infof("[agent] finished to parse sidecar config, current active config is %s", *polarisAgent.config)
   // 初始化日志打印
   err = log.Configure(polarisAgent.config.Logger)
   log.Infof("[agent] success to init log config")
   if err != nil {
      return nil, err
   }
   for _, resolverCfg := range polarisAgent.config.Resolvers {
      if !resolverCfg.Enable {
         log.Infof("[agent] resolver %s is not enabled", resolverCfg.Name)
         continue
      }
      name := resolverCfg.Name
      handler := resolver.NameResolver(name)
      if nil == handler {
         log.Errorf("[agent] resolver %s is not found", resolverCfg.Name)
         return nil, fmt.Errorf("fail to lookup resolver %s, consider it's not registered", name)
      }
      err = handler.Initialize(resolverCfg)
      if nil != err {
         for _, initHandler := range polarisAgent.resolvers {
            initHandler.Destroy()
         }
         log.Errorf("[agent] fail to init resolver %s, err: %v", resolverCfg.Name, err)
         return nil, err
      }
      log.Infof("[agent] finished to init resolver %s", resolverCfg.Name)
      polarisAgent.resolvers = append(polarisAgent.resolvers, handler)
   }
   recurseAddresses := make([]string, 0, len(polarisAgent.config.Recurse.NameServers))
   for _, nameserver := range polarisAgent.config.Recurse.NameServers {
      recurseAddresses = append(recurseAddresses, fmt.Sprintf("%s:53", nameserver))
   }
   polarisAgent.udpServer = &dns.Server{
      Addr: polarisAgent.config.Bind   ":"   strconv.Itoa(polarisAgent.config.Port), Net: "udp"}
   polarisAgent.udpServer.Handler = &dnsHandler{
      protocol: "udp",
      resolvers: polarisAgent.resolvers,
      searchNames: searchNames,
      recursorTimeout: time.Duration(polarisAgent.config.Recurse.TimeoutSec) * time.Second,
      recursors: recurseAddresses,
      recurseEnable: polarisAgent.config.Recurse.Enable,
   }
   polarisAgent.tcpServer = &dns.Server{
      Addr: polarisAgent.config.Bind   ":"   strconv.Itoa(polarisAgent.config.Port), Net: "tcp"}
   polarisAgent.tcpServer.Handler = &dnsHandler{
      protocol: "tcp",
      resolvers: polarisAgent.resolvers,
      searchNames: searchNames,
      recursorTimeout: time.Duration(polarisAgent.config.Recurse.TimeoutSec) * time.Second,
      recursors: recurseAddresses,
      recurseEnable: polarisAgent.config.Recurse.Enable,
   }
   return polarisAgent, nil
}

上面代码主要逻辑如下:

  • 解析配置文件初始化给polarisAgent的config对象
  • 解析/etc/resolve.conf配置文件获取本地dns的nameservers等信息,用来初始化polarisAgent对象相关属性
  • 根据config的解释器配置,初始化解释器handler,执行handler的初始化方法
  • 初始化了两个dns服务器,一个是tcp协议的,一个是udp协议的

启动流程

代码语言:javascript复制
func (p *Agent) Start(ctx context.Context) error {
   for _, handler := range p.resolvers {
      handler.Start(ctx)
      log.Infof("[agent] success to start resolver %s", handler.Name())
   }
   errChan := make(chan error)
   go func() {
      errChan <- p.tcpServer.ListenAndServe()
   }()
   go func() {
      errChan <- p.udpServer.ListenAndServe()
   }()
   var recvErrCounts int
   defer func() {
      for _, handler := range p.resolvers {
         handler.Destroy()
      }
   }()
   for {
      select {
      case err := <-errChan:
         if nil != err {
            return err
         }
         recvErrCounts  
         if recvErrCounts == 2 {
            return nil
         }
      case <-ctx.Done():
         return nil
      }
   }
}

主要逻辑如下:

  • 执行每个解释器的start方法:dns和mesh两种
  • 启动dns服务器

下面来看看两种情况的启动流程

本地DNS

服务网格

代码语言:javascript复制
func (r *resolverMesh) Start(ctx context.Context) {
   interval := time.Duration(r.config.ReloadIntervalSec) * time.Second

   go func() {
      ticker := time.NewTicker(interval)
      defer ticker.Stop()
      var currentServices map[string]struct{}
      for {
         select {
         case <-ticker.C:
            services, err := r.registry.GetCurrentNsService()
            if err != nil {
               log.Errorf("[mesh] error to get services, err: %v", err)
               continue
            }
            if ifServiceListChanged(currentServices, services) {
               log.Infof("[Mesh] services lookup are %v", services)
               r.localDNSServer.UpdateLookupTable(services, r.config.DNSAnswerIp)
               currentServices = services
            }
         case <-ctx.Done():
            return
         }
      }
   }()
}

主要流程如下:

  • 启动一个协程
  • 轮询操作:获取当前名字空间service,更新本地dns缓存表

0 人点赞