您好,登錄后才能下訂單哦!
這篇文章主要講解了“SkyDNS工作原理是什么”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“SkyDNS工作原理是什么”吧!
SkyDNS2是SkyDNS Version 2.x的統(tǒng)稱,其官方文檔只有README.md,網(wǎng)上能找到的資料也不多,因此需要我們自行對(duì)代碼進(jìn)行一定的分析,才能對(duì)其有更好的理解,這就是本文的工作,通過走讀SkyDNS的代碼,了解其內(nèi)部架構(gòu)及其工作原理。
SkyDNS Server的工作,依賴后端Key-Value存儲(chǔ)的支持。當(dāng)前支持etcd或etcd3作為Backend(架構(gòu)圖中藍(lán)色部分),為SkyDNS提供配置和數(shù)據(jù)的管理。
通過環(huán)境變量ETCD_MACHINES
進(jìn)行etcd cluster的配置,如果Backend為etcd3,還需要設(shè)置etcd中/v2/keys//skydns/config/etcd3為true。SkyDNS中有etcd client模塊,負(fù)責(zé)與ETCD_MACHINES
的通信。
SkyDNS主要對(duì)應(yīng)的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/...
通過如下環(huán)境變量的配置,支持prometheus監(jiān)控(架構(gòu)圖中棕色部分)。如果想disable prometheus監(jiān)控,則配置環(huán)境變量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,會(huì)forward到對(duì)應(yīng)的這些IP:Port
構(gòu)成的nameservers,由它們進(jìn)行解析(架構(gòu)圖中綠色部分)。
參考官方文檔https://github.com/skynetservices/skydns/blob/master/README.md完成參數(shù)配置后,便可啟動(dòng)SkyDNS。
SkyDNS Server的啟動(dòng)過程如下:
創(chuàng)建etcd client對(duì)象;
dns_addr 和 nameservers參數(shù)合法性檢查;
加載啟動(dòng)參數(shù)到etcd,覆蓋/v2/keys/skydns/config中原有數(shù)據(jù);
配置SkyDNS Server參數(shù)的default值,并創(chuàng)建SkyDNS server對(duì)象;
去etcd中加載.../dns/stub/<domain>/xx數(shù)據(jù)作為server的stub zones數(shù)據(jù),并啟動(dòng)對(duì).../dns/stub/的watcher,一旦有數(shù)據(jù)更新,就加載到server的stub zones數(shù)據(jù)中;
注冊(cè)SkyDNS metrics到prometheus;
然后在/v2/keys/skydns/config/dns_addr配置的interface和port上開啟tcp/udp監(jiān)聽服務(wù)并block住,由此開始提供DSN服務(wù)。
在github.com/skynetservices/skydns/server/server.go中的ServeDNS方法覆蓋了miekg/dns/server中的ServeMux.ServeDNS方法,由自實(shí)現(xiàn)的ServeDNS提供來處理DNS client的請(qǐng)求。
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 } }
上面代碼邏輯比較復(fù)雜,細(xì)節(jié)上需要你慢慢去理解,簡短的可以總結(jié)如下:
如架構(gòu)圖中標(biāo)注的線路1:如果在SkyDNS維護(hù)的cache中找到對(duì)應(yīng)Msg,則從cache中讀取并返回Msg給DNS client;
如架構(gòu)圖中標(biāo)注的線路2:如果在cache中沒有對(duì)應(yīng)的記錄,并且是需要DNS forward的場景(比如name匹配到stub zones等),則將請(qǐng)求forward到對(duì)應(yīng)的DNS servers進(jìn)行處理;
如架構(gòu)圖中標(biāo)注的線路3:如果在cache中沒有對(duì)應(yīng)的記錄,并且Question Type為A/AAAA,SRV等類型時(shí),就通過etcd client去etcd cluster中獲取對(duì)應(yīng)的Rule,并構(gòu)造Msg返回。
感謝各位的閱讀,以上就是“SkyDNS工作原理是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)SkyDNS工作原理是什么這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。