2019独角兽企业重金招聘Python工程师标准>>>

SkyDNS2是SkyDNS Version 2.x的统称,其官方文档只有README.md,网上能找到的资料也不多,因此需要我们自行对代码进行一定的分析,才能对其有更好的理解,这就是本文的工作,通过走读SkyDNS的代码,了解其内部架构及其工作原理。

说明

  • SkyDNS2的github地址: https://github.com/skynetservices/skydns
  • Version: v2.5.3a

SkyDNS架构

关于SkyDNS是什么?.... 这些知识,请前往官网了解。

下面我直接把我阅读代码后理解的SkyDNS架构贴出来:

SkyDNS工作原理

SkyDNS Server的工作,依赖后端Key-Value存储的支持。当前支持etcd或etcd3作为Backend(架构图中蓝色部分),为SkyDNS提供配置和数据的管理。

通过环境变量ETCD_MACHINES进行etcd cluster的配置,如果Backend为etcd3,还需要设置etcd中/v2/keys//skydns/config/etcd3为true。SkyDNS中有etcd client模块,负责与ETCD_MACHINES的通信。

SkyDNS主要对应的etcd key path如下:

/v2/keys/skydns/config
/v2/keys/skydns/local/skydns/east/production/rails
/v2/keys/skydns/local/skydns/dns/stub
/v2/keys/skydns/local/skydns/...

通过如下环境变量的配置,支持prometheus监控(架构图中棕色部分)。如果想disable prometheus监控,则配置环境变量PROMETHEUS_PORT的值为0即可。

Port      = os.Getenv("PROMETHEUS_PORT")
Path      = envOrDefault("PROMETHEUS_PATH", "/metrics")
Namespace = envOrDefault("PROMETHEUS_NAMESPACE", "skydns")
Subsystem = envOrDefault("PROMETHEUS_SUBSYSTEM", "skydns")

如果/v2/keys/skydns/config/nameservers有值,则SkyDNS解析不了的Domain,会forward到对应的这些IP:Port构成的nameservers,由它们进行解析(架构图中绿色部分)。

参考官方文档https://github.com/skynetservices/skydns/blob/master/README.md完成参数配置后,便可启动SkyDNS。

SkyDNS Server的启动过程如下:

  • 创建etcd client对象;
  • dns_addr 和 nameservers参数合法性检查;
  • 加载启动参数到etcd,覆盖/v2/keys/skydns/config中原有数据;
  • 配置SkyDNS Server参数的default值,并创建SkyDNS server对象;
  • 去etcd中加载.../dns/stub/<domain>/xx数据作为server的stub zones数据,并启动对.../dns/stub/的watcher,一旦有数据更新,就加载到server的stub zones数据中;
  • 注册SkyDNS metrics到prometheus;
  • 然后在/v2/keys/skydns/config/dns_addr配置的interface和port上开启tcp/udp监听服务并block住,由此开始提供DSN服务。

在github.com/skynetservices/skydns/server/server.go中的ServeDNS方法覆盖了miekg/dns/server中的ServeMux.ServeDNS方法,由自实现的ServeDNS提供来处理DNS client的请求。

github.com/skynetservices/skydns/server/server.go// ServeDNS is the handler for DNS requests, responsible for parsing DNS request, possibly forwarding
// it to a real dns server and returning a response.
func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {...// Check cache first.m1 := s.rcache.Hit(q, dnssec, tcp, m.Id)if m1 != nil {...// Still round-robin even with hits from the cache.// Only shuffle A and AAAA records with each other.if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA {s.RoundRobin(m1.Answer)}...return}for zone, ns := range *s.config.stub {if strings.HasSuffix(name, "." + zone) || name == zone {metrics.ReportRequestCount(req, metrics.Stub)resp := s.ServeDNSStubForward(w, req, ns)if resp != nil {s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp)}metrics.ReportDuration(resp, start, metrics.Stub)metrics.ReportErrorCount(resp, metrics.Stub)return}}...if name == s.config.Domain {if q.Qtype == dns.TypeSOA {m.Answer = []dns.RR{s.NewSOA()}return}if q.Qtype == dns.TypeDNSKEY {if s.config.PubKey != nil {m.Answer = []dns.RR{s.config.PubKey}return}}}if q.Qclass == dns.ClassCHAOS {if q.Qtype == dns.TypeTXT {switch name {case "authors.bind.":fallthroughcase s.config.Domain:hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}authors := []string{"Erik St. Martin", "Brian Ketelsen", "Miek Gieben", "Michael Crosby"}for _, a := range authors {m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{a}})}for j := 0; j < len(authors)*(int(dns.Id())%4+1); j++ {q := int(dns.Id()) % len(authors)p := int(dns.Id()) % len(authors)if q == p {p = (p + 1) % len(authors)}m.Answer[q], m.Answer[p] = m.Answer[p], m.Answer[q]}returncase "version.bind.":fallthroughcase "version.server.":hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{Version}}}returncase "hostname.bind.":fallthroughcase "id.server.":// TODO(miek): machine name to returnhdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"localhost"}}}return}}// still here, failm.SetReply(req)m.SetRcode(req, dns.RcodeServerFailure)return}switch q.Qtype {case dns.TypeNS:if name != s.config.Domain {break}// Lookup s.config.DnsDomainrecords, extra, err := s.NSRecords(q, s.config.dnsDomain)if isEtcdNameError(err, s) {m = s.NameError(req)return}m.Answer = append(m.Answer, records...)m.Extra = append(m.Extra, extra...)case dns.TypeA, dns.TypeAAAA:records, err := s.AddressRecords(q, name, nil, bufsize, dnssec, false)if isEtcdNameError(err, s) {m = s.NameError(req)return}m.Answer = append(m.Answer, records...)case dns.TypeTXT:records, err := s.TXTRecords(q, name)if isEtcdNameError(err, s) {m = s.NameError(req)return}m.Answer = append(m.Answer, records...)case dns.TypeCNAME:records, err := s.CNAMERecords(q, name)if isEtcdNameError(err, s) {m = s.NameError(req)return}m.Answer = append(m.Answer, records...)case dns.TypeMX:records, extra, err := s.MXRecords(q, name, bufsize, dnssec)if isEtcdNameError(err, s) {m = s.NameError(req)return}m.Answer = append(m.Answer, records...)m.Extra = append(m.Extra, extra...)default:fallthrough // also catch other types, so that they return NODATAcase dns.TypeSRV:records, extra, err := s.SRVRecords(q, name, bufsize, dnssec)if err != nil {if isEtcdNameError(err, s) {m = s.NameError(req)return}logf("got error from backend: %s", err)if q.Qtype == dns.TypeSRV { // Otherwise NODATAm = s.ServerFailure(req)return}}// if we are here again, check the types, because an answer may only// be given for SRV. All other types should return NODATA, the// NXDOMAIN part is handled in the above code. TODO(miek): yes this// can be done in a more elegant manor.if q.Qtype == dns.TypeSRV {m.Answer = append(m.Answer, records...)m.Extra = append(m.Extra, extra...)}}if len(m.Answer) == 0 { // NODATA responsem.Ns = []dns.RR{s.NewSOA()}m.Ns[0].Header().Ttl = s.config.MinTtl}
}

上面代码逻辑比较复杂,细节上需要你慢慢去理解,简短的可以总结如下:

  • 如架构图中标注的线路1:如果在SkyDNS维护的cache中找到对应Msg,则从cache中读取并返回Msg给DNS client;
  • 如架构图中标注的线路2:如果在cache中没有对应的记录,并且是需要DNS forward的场景(比如name匹配到stub zones等),则将请求forward到对应的DNS servers进行处理;
  • 如架构图中标注的线路3:如果在cache中没有对应的记录,并且Question Type为A/AAAA,SRV等类型时,就通过etcd client去etcd cluster中获取对应的Rule,并构造Msg返回。

总结

通过走读SkyDNS的代码,了解其内部架构及其工作原理。

Mark

SkyDNS2 Changes since version 1:

  • Does away with Raft and uses etcd (which uses raft).
  • Makes it possible to query arbitrary domain names.
  • Is a thin layer above etcd, that translates etcd keys and values to the DNS.
  • Does DNSSEC with NSEC3 instead of NSEC.

转载于:https://my.oschina.net/jxcdwangtao/blog/827750

SkyDNS2源码分析相关推荐

  1. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  2. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  3. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  4. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  5. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

  6. ViewGroup的Touch事件分发(源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...

  7. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  8. MyBatis原理分析之四:一次SQL查询的源码分析

    上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的. 准备工作 Mybatis完成一次SQL查询需要使用的代码如下: Java代码   String res ...

  9. [转]slf4j + log4j原理实现及源码分析

    slf4j + log4j原理实现及源码分析 转载于:https://www.cnblogs.com/jasonzeng888/p/6051080.html

最新文章

  1. 【复盘】小朋友的奇思妙想
  2. Topcoder SRM 697题解
  3. Android获取网速的方法
  4. 吉首大学2019年程序设计竞赛
  5. 支付宝支付-提现到个人支付宝
  6. CodeForces - 1213E Two Small Strings(暴力+构造)
  7. ffmpeg摄像头推流
  8. 服务器一般在什么位置,云服务器比较普通服务器差异在哪里
  9. FastStone Capture 下载
  10. Mybatis实现逆向工程
  11. 地理坐标xy表示什么_地理坐标怎么写 书写格式及方法
  12. 关于获取安卓APP素材的方法
  13. mac发送微信表情卡顿(已解决!!!!)
  14. 交换机的全trunk模式(native vlan)
  15. 低代码的 Soulmate 燃爆 Ignite China 晚场趴,高光瞬间不止亿点点
  16. 英雄杀朱雀之章在线活动
  17. ZLG CANalyst驱动安装报错
  18. 使用免费OA系统,让你成为职场锦鲤
  19. 电子标签的LF, HF, UHF都分别代表什么?
  20. 头歌实验 HDFS的基本操作和Java API 操作

热门文章

  1. Vue (响应式原理-模拟-0)
  2. Browser-Bookmark-Codeing
  3. 求幂级数展开的部分和 (20 分)新鲜出炉!!!
  4. 6-7 jmu-Java-07多线程-Thread (3分)
  5. 动态规划之图像压缩问题
  6. 用户登陆注册功能(PHP)
  7. tableau两个不同的图合并_举个栗子!Tableau技巧(59):学做两个集合的维恩图(文氏图)Venn diagram...
  8. Codeforces 1167E 尺取法
  9. 关于数据库事务启用后的查询操作
  10. C++生成简单WAV文件(一)