共计 5953 个字符,预计需要花费 15 分钟才能阅读完成。
这篇文章主要讲解了“SkyDNS 工作原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着丸趣 TV 小编的思路慢慢深入,一起来研究和学习“SkyDNS 工作原理是什么”吧!
SkyDNS2 是 SkyDNS Version 2.x 的统称,其官方文档只有 README.md,网上能找到的资料也不多,因此需要我们自行对代码进行一定的分析,才能对其有更好的理解,这就是本文的工作,通过走读 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. :
fallthrough
case 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]
return
case version.bind. :
fallthrough
case 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}}}
return
case hostname.bind. :
fallthrough
case id.server. :
// TODO(miek): machine name to return
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{localhost}}}
return
// still here, fail
m.SetReply(req)
m.SetRcode(req, dns.RcodeServerFailure)
return
switch q.Qtype {
case dns.TypeNS:
if name != s.config.Domain {
break
// Lookup s.config.DnsDomain
records, 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 NODATA
case 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 NODATA
m = 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 response
m.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 工作原理是什么”的内容了,经过本文的学习后,相信大家对 SkyDNS 工作原理是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是丸趣 TV,丸趣 TV 小编将为大家推送更多相关知识点的文章,欢迎关注!