SkyDNS2源码分析
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源码分析相关推荐
- 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析
目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...
- SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)
[SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...
- SpringBoot-web开发(二): 页面和图标定制(源码分析)
[SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...
- SpringBoot-web开发(一): 静态资源的导入(源码分析)
目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...
- Yolov3Yolov4网络结构与源码分析
Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...
- ViewGroup的Touch事件分发(源码分析)
Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...
- View的Touch事件分发(二.源码分析)
Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...
- MyBatis原理分析之四:一次SQL查询的源码分析
上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的. 准备工作 Mybatis完成一次SQL查询需要使用的代码如下: Java代码 String res ...
- [转]slf4j + log4j原理实现及源码分析
slf4j + log4j原理实现及源码分析 转载于:https://www.cnblogs.com/jasonzeng888/p/6051080.html
最新文章
- 【复盘】小朋友的奇思妙想
- Topcoder SRM 697题解
- Android获取网速的方法
- 吉首大学2019年程序设计竞赛
- 支付宝支付-提现到个人支付宝
- CodeForces - 1213E Two Small Strings(暴力+构造)
- ffmpeg摄像头推流
- 服务器一般在什么位置,云服务器比较普通服务器差异在哪里
- FastStone Capture 下载
- Mybatis实现逆向工程
- 地理坐标xy表示什么_地理坐标怎么写 书写格式及方法
- 关于获取安卓APP素材的方法
- mac发送微信表情卡顿(已解决!!!!)
- 交换机的全trunk模式(native vlan)
- 低代码的 Soulmate 燃爆 Ignite China 晚场趴,高光瞬间不止亿点点
- 英雄杀朱雀之章在线活动
- ZLG CANalyst驱动安装报错
- 使用免费OA系统,让你成为职场锦鲤
- 电子标签的LF, HF, UHF都分别代表什么?
- 头歌实验 HDFS的基本操作和Java API 操作
热门文章
- Vue (响应式原理-模拟-0)
- Browser-Bookmark-Codeing
- 求幂级数展开的部分和 (20 分)新鲜出炉!!!
- 6-7 jmu-Java-07多线程-Thread (3分)
- 动态规划之图像压缩问题
- 用户登陆注册功能(PHP)
- tableau两个不同的图合并_举个栗子!Tableau技巧(59):学做两个集合的维恩图(文氏图)Venn diagram...
- Codeforces 1167E 尺取法
- 关于数据库事务启用后的查询操作
- C++生成简单WAV文件(一)