dns服务器系统架构,详解 DNS 与 CoreDNS 的实现原理
原文鏈接:https://draveness.me/dns-coredns
【編者的話】域名系統(Domain Name System)是整個互聯網的電話簿,它能夠將可被人理解的域名翻譯成可被機器理解 IP 地址,使得互聯網的使用者不再需要直接接觸很難閱讀和理解的 IP 地址。
我們在這篇文章中的第一部分會介紹 DNS 的工作原理以及一些常見的 DNS 問題,而第二部分我們會介紹 DNS 服務 CoreDNS 的架構和實現原理。
DNS域名系統在現在的互聯網中非常重要,因為服務器的 IP 地址可能會經常變動,如果沒有了 DNS,那么可能 IP 地址一旦發生了更改,當前服務器的客戶端就沒有辦法連接到目標的服務器了,如果我們為 IP 地址提供一個『別名』并在其發生變動時修改別名和 IP 地址的關系,那么我們就可以保證集群對外提供的服務能夠相對穩定地被其他客戶端訪問。
DNS 其實就是一個分布式的樹狀命名系統,它就像一個去中心化的分布式數據庫,存儲著從域名到 IP 地址的映射。
工作原理在我們對 DNS 有了簡單的了解之后,接下來我們就可以進入 DNS 工作原理的部分了,作為用戶訪問互聯網的第一站,當一臺主機想要通過域名訪問某個服務的內容時,需要先通過當前域名獲取對應的 IP 地址。這時就需要通過一個 DNS 解析器負責域名的解析,下面的圖片展示了 DNS 查詢的執行過程:
本地的 DNS 客戶端向 DNS 解析器發出解析 draveness.me 域名的請求;
DNS 解析器首先會向就近的根 DNS 服務器 . 請求頂級域名 DNS 服務的地址;
拿到頂級域名 DNS 服務 me. 的地址之后會向頂級域名服務請求負責 dravenss.me. 域名解析的命名服務;
得到授權的 DNS 命名服務時,就可以根據請求的具體的主機記錄直接向該服務請求域名對應的 IP 地址;
DNS 客戶端接受到 IP 地址之后,整個 DNS 解析的過程就結束了,客戶端接下來就會通過當前的 IP 地址直接向服務器發送請求。
對于 DNS 解析器,這里使用的 DNS 查詢方式是迭代查詢,每個 DNS 服務并不會直接返回 DNS 信息,而是會返回另一臺 DNS 服務器的位置,由客戶端依次詢問不同級別的 DNS 服務直到查詢得到了預期的結果;另一種查詢方式叫做遞歸查詢,也就是 DNS 服務器收到客戶端的請求之后會直接返回準確的結果,如果當前服務器沒有存儲 DNS 信息,就會訪問其他的服務器并將結果返回給客戶端。
域名層級域名層級是一個層級的樹形結構,樹的最頂層是根域名,一般使用.來表示,這篇文章所在的域名一般寫作 draveness.me,但是這里的寫法其實省略了最后的.,也就是全稱域名(FQDN)dravenss.me.。
根域名下面的就是 com、net 和 me 等頂級域名以及次級域名 draveness.me,我們一般在各個域名網站中購買和使用的都是次級域名、子域名和主機名了。
域名服務器既然域名的命名空間是樹形的,那么用于處理域名解析的 DNS 服務器也是樹形的,只是在樹的組織和每一層的職責上有一些不同。DNS 解析器從根域名服務器查找到頂級域名服務器的 IP 地址,又從頂級域名服務器查找到權威域名服務器的 IP 地址,最終從權威域名服務器查出了對應服務的 IP 地址。
$?dig?-t?A?draveness.me?+trace
我們可以使用 dig 命令追蹤 draveness.me 域名對應 IP 地址是如何被解析出來的,首先會向預置的 13 組根域名服務器發出請求獲取頂級域名的地址:
.????????????56335???IN??NS??m.root-servers.net.
.???????????56335???IN??NS??b.root-servers.net.
.???????????56335???IN??NS??c.root-servers.net.
.???????????56335???IN??NS??d.root-servers.net.
.???????????56335???IN??NS??e.root-servers.net.
.???????????56335???IN??NS??f.root-servers.net.
.???????????56335???IN??NS??g.root-servers.net.
.???????????56335???IN??NS??h.root-servers.net.
.???????????56335???IN??NS??i.root-servers.net.
.???????????56335???IN??NS??a.root-servers.net.
.???????????56335???IN??NS??j.root-servers.net.
.???????????56335???IN??NS??k.root-servers.net.
.???????????56335???IN??NS??l.root-servers.net.
.???????????56335???IN??RRSIG???NS?8?0?518400?20181111050000?20181029040000?2134?.?G4NbgLqsAyin2zZFetV6YhBVVI29Xi3kwikHSSmrgkX+lq3sRgp3UuQ3?JQxpJ+bZY7mwzo3NxZWy4pqdJDJ55s92l+SKRt/ruBv2BCnk9CcnIzK+?OuGheC9/Coz/r/33rpV63CzssMTIAAMQBGHUyFvRSkiKJWFVOps7u3TM?jcQR0Xp+rJSPxA7f4+tDPYohruYm0nVXGdWhO1CSadXPvmWs1xeeIKvb?9sXJ5hReLw6Vs6ZVomq4tbPrN1zycAbZ2tn/RxGSCHMNIeIROQ99kO5N?QL9XgjIJGmNVDDYi4OF1+ki48UyYkFocEZnaUAor0pD3Dtpis37MASBQ?fr6zqQ==
;;?Received?525?bytes?from?8.8.8.8#53(8.8.8.8)?in?247?ms
根域名服務器是 DNS 中最高級別的域名服務器,這些服務器負責返回頂級域的權威域名服務器地址,這些域名服務器的數量總共有 13 組,域名的格式從上面返回的結果可以看到是 .root-servers.net,每個根域名服務器中只存儲了頂級域服務器的 IP 地址,大小其實也只有 2MB 左右,雖然域名服務器總共只有 13 組,但是每一組服務器都通過提供了鏡像服務,全球大概也有幾百臺的根域名服務器在運行。在這里,我們獲取到了以下的 5 條 NS 記錄,也就是 5 臺 me. 定義域名 DNS 服務器:
me.??????????172800??IN??NS??b0.nic.me.
me.?????????172800??IN??NS??a2.nic.me.
me.?????????172800??IN??NS??b2.nic.me.
me.?????????172800??IN??NS??a0.nic.me.
me.?????????172800??IN??NS??c0.nic.me.
me.?????????86400???IN??DS??2569?7?1?09BA1EB4D20402620881FD9848994417800DB26A
me.?????????86400???IN??DS??2569?7?2?94E798106F033500E67567B197AE9132C0E916764DC743C55A9ECA3C?7BF559E2
me.?????????86400???IN??RRSIG???DS?8?1?86400?20181113050000?20181031040000?2134?.?O81bud61Qh+kJJ26XHzUOtKWRPN0GHoVDacDZ+pIvvD6ef0+HQpyT5nV?rhEZXaFwf0YFo08PUzX8g5Pad8bpFj0O//Q5H2awGbjeoJnlMqbwp6Kl?7O9zzp1YCKmB+ARQgEb7koSCogC9pU7E8Kw/o0NnTKzVFmLq0LLQJGGE?Y43ay3Ew6hzpG69lP8dmBHot3TbF8oFrlUzrm5nojE8W5QVTk1QQfrZM?90WBjfe5nm9b4BHLT48unpK3BaqUFPjqYQV19C3xJ32at4OwUyxZuQsa?GWl0w9R5TiCTS5Ieupu+Q9fLZbW5ZMEgVSt8tNKtjYafBKsFox3cSJRn?irGOmg==
;;?Received?721?bytes?from?192.36.148.17#53(i.root-servers.net)?in?59?ms
當 DNS 解析器從根域名服務器中查詢到了頂級域名 .me 服務器的地址之后,就可以訪問這些頂級域名服務器其中的一臺 b2.nic.me 獲取權威 DNS 的服務器的地址了:
draveness.me.????????86400???IN??NS??f1g1ns1.dnspod.net.
draveness.me.???????86400???IN??NS??f1g1ns2.dnspod.net.
fsip6fkr2u8cf2kkg7scot4glihao6s1.me.?8400?IN?NSEC3?1?1?1?D399EAAB?FSJJ1I3A2LHPTHN80MA6Q7J64B15AO5K??NS?SOA?RRSIG?DNSKEY?NSEC3PARAM
fsip6fkr2u8cf2kkg7scot4glihao6s1.me.?8400?IN?RRSIG?NSEC3?7?2?8400?20181121151954?20181031141954?2208?me.?eac6+fEuQ6gK70KExV0EdUKnWeqPrzjqGiplqMDPNRpIRD1vkpX7Zd6C?oN+c8b2yLoI3s3oLEoUd0bUi3dhyCrxF5n6Ap+sKtEv4zZ7o7CEz5Fw+?fpXHj7VeL+pI8KffXcgtYQGlPlCM/ylGUGYOcExrB/qPQ6f/62xrPWjb?+r4=
qcolpi5mj0866sefv2jgp4jnbtfrehej.me.?8400?IN?NSEC3?1?1?1?D399EAAB?QD4QM6388QN4UMH78D429R72J1NR0U07??NS?DS?RRSIG
qcolpi5mj0866sefv2jgp4jnbtfrehej.me.?8400?IN?RRSIG?NSEC3?7?2?8400?20181115151844?20181025141844?2208?me.?rPGaTz/LyNRVN3LQL3LO1udby0vy/MhuIvSjNfrNnLaKARsbQwpq2pA9?+jyt4ah8fvxRkGg9aciG1XSt/EVIgdLSKXqE82hB49ZgYDACX6onscgz?naQGaCAbUTSGG385MuyxCGvqJdE9kEZBbCG8iZhcxSuvBksG4msWuo3k?dTg=
;;?Received?586?bytes?from?199.249.127.1#53(b2.nic.me)?in?267?ms
這里的權威 DNS 服務是作者在域名提供商進行配置的,當有客戶端請求 draveness.me 域名對應的 IP 地址時,其實會從作者使用的 DNS 服務商 DNSPod 處請求服務的 IP 地址:
draveness.me.????????600?IN??A???123.56.94.228
draveness.me.???????86400???IN??NS??f1g1ns2.dnspod.net.
draveness.me.???????86400???IN??NS??f1g1ns1.dnspod.net.
;;?Received?123?bytes?from?58.247.212.36#53(f1g1ns1.dnspod.net)?in?28?ms
最終,DNS 解析器從 f1g1ns1.dnspod.net 服務中獲取了當前博客的 IP 地址 123.56.94.228,瀏覽器或者其他設備就能夠通過 IP 向服務器獲取請求的內容了。
從整個解析過程,我們可以看出 DNS 域名服務器大體分成三類,根域名服務、頂級域名服務以及權威域名服務三種,獲取域名對應的 IP 地址時,也會像遍歷一棵樹一樣按照從頂層到底層的順序依次請求不同的服務器。
膠水記錄在通過服務器解析域名的過程中,我們看到當請求 me. 頂級域名服務器的時候,其實返回了 b0.nic.me 等域名:
me.??????????172800??IN??NS??b0.nic.me.
me.?????????172800??IN??NS??a2.nic.me.
me.?????????172800??IN??NS??b2.nic.me.
me.?????????172800??IN??NS??a0.nic.me.
me.?????????172800??IN??NS??c0.nic.me.
...
就像我們最開始說的,在互聯網中想要請求服務,最終一定需要獲取 IP 提供服務的服務器的 IP 地址;同理,作為 b0.nic.me 作為一個 DNS 服務器,我也必須獲取它的 IP 地址才能獲得次級域名的 DNS 信息,但是這里就陷入了一種循環:
如果想要獲取 dravenss.me 的 IP 地址,就需要訪問 me 頂級域名服務器 b0.nic.me
如果想要獲取 b0.nic.me 的 IP 地址,就需要訪問 me 頂級域名服務器 b0.nic.me
如果想要獲取 b0.nic.me 的 IP 地址,就需要訪問 me 頂級域名服務器 b0.nic.me
……
為了解決這一個問題,我們引入了膠水記錄(Glue Record)這一概念,也就是在出現循環依賴時,直接在上一級作用域返回 DNS 服務器的 IP 地址:
$?dig?+trace?+additional?draveness.me
...
me.?????????172800??IN??NS??a2.nic.me.
me.?????????172800??IN??NS??b2.nic.me.
me.?????????172800??IN??NS??b0.nic.me.
me.?????????172800??IN??NS??a0.nic.me.
me.?????????172800??IN??NS??c0.nic.me.
me.?????????86400???IN??DS??2569?7?1?09BA1EB4D20402620881FD9848994417800DB26A
me.?????????86400???IN??DS??2569?7?2?94E798106F033500E67567B197AE9132C0E916764DC743C55A9ECA3C?7BF559E2
me.?????????86400???IN??RRSIG???DS?8?1?86400?20181116050000?20181103040000?2134?.?cT+rcDNiYD9X02M/NoSBombU2ZqW/7WnEi+b/TOPcO7cDbjb923LltFb?ugMIaoU0Yj6k0Ydg++DrQOy6E5eeshughcH/6rYEbVlFcsIkCdbd9gOk?QkOMH+luvDjCRdZ4L3MrdXZe5PJ5Y45C54V/0XUEdfVKel+NnAdJ1gLE?F+aW8LKnVZpEN/Zu88alOBt9+FPAFfCRV9uQ7UmGwGEMU/WXITheRi5L?h8VtV9w82E6Jh9DenhVFe2g82BYu9MvEbLZr3MKII9pxgyUE3pt50wGY?Mhs40REB0v4pMsEU/KHePsgAfeS/mFSXkiPYPqz2fgke6OHFuwq7MgJk?l7RruQ==
a0.nic.me.??????172800??IN??A???199.253.59.1
a2.nic.me.??????172800??IN??A???199.249.119.1
b0.nic.me.??????172800??IN??A???199.253.60.1
b2.nic.me.??????172800??IN??A???199.249.127.1
c0.nic.me.??????172800??IN??A???199.253.61.1
a0.nic.me.??????172800??IN??AAAA????2001:500:53::1
a2.nic.me.??????172800??IN??AAAA????2001:500:47::1
b0.nic.me.??????172800??IN??AAAA????2001:500:54::1
b2.nic.me.??????172800??IN??AAAA????2001:500:4f::1
c0.nic.me.??????172800??IN??AAAA????2001:500:55::1
;;?Received?721?bytes?from?192.112.36.4#53(g.root-servers.net)?in?110?ms
...
也就是同時返回 NS 記錄和 A(或 AAAA) 記錄,這樣就能夠解決域名解析出現的循環依賴問題。
服務發現講到現在,我們其實能夠發現 DNS 就是一種最早的服務發現的手段,通過雖然服務器的 IP 地址可能會經常變動,但是通過相對不會變動的域名,我們總是可以找到提供對應服務的服務器。
在微服務架構中,服務注冊的方式其實大體上也只有兩種,一種是使用 ZooKeeper 和 etcd 等配置管理中心,另一種是使用 DNS 服務,比如說 Kubernetes 中的 CoreDNS 服務。
使用 DNS 在集群中做服務發現其實是一件比較容易的事情,這主要是因為絕大多數的計算機上都會安裝 DNS 服務,所以這其實就是一種內置的、默認的服務發現方式,不過使用 DNS 做服務發現也會有一些問題,因為在默認情況下 DNS 記錄的失效時間是 600s,這對于集群來講其實并不是一個可以接受的時間,在實踐中我們往往會啟動單獨的 DNS 服務滿足服務發現的需求。
CoreDNSCoreDNS 其實就是一個 DNS 服務,而 DNS 作為一種常見的服務發現手段,所以很多開源項目以及工程師都會使用 CoreDNS 為集群提供服務發現的功能,Kubernetes 就在集群中使用 CoreDNS 解決服務發現的問題。
作為一個加入 CNCF(Cloud Native Computing Foundation)的服務 CoreDNS 的實現可以說的非常的簡單。
架構整個 CoreDNS 服務都建立在一個使用 Go 編寫的 HTTP/2 Web 服務器 Caddy · GitHub 上,CoreDNS 整個項目可以作為一個 Caddy 的教科書用法。
CoreDNS 的大多數功能都是由插件來實現的,插件和服務本身都使用了 Caddy 提供的一些功能,所以項目本身也不是特別的復雜。
插件作為基于 Caddy 的 Web 服務器,CoreDNS 實現了一個插件鏈的架構,將很多 DNS 相關的邏輯都抽象層了一層一層的插件,包括 Kubernetes 等功能,每一個插件都是一個遵循如下協議的結構體:
type?(
Plugin?func(Handler)?Handler
Handler?interface?{
ServeDNS(context.Context,?dns.ResponseWriter,?*dns.Msg)?(int,?error)
Name()?string
}
)
所以只需要為插件實現 ServeDNS 以及 Name 這兩個接口并且寫一些用于配置的代碼就可以將插件集成到 CoreDNS 中。
Corefile另一個 CoreDNS 的特點就是它能夠通過簡單易懂的 DSL 定義 DNS 服務,在 Corefile 中就可以組合多個插件對外提供服務:
coredns.io:5300?{
file?db.coredns.io
}
example.io:53?{
log
errors
file?db.example.io
}
example.net:53?{
file?db.example.net
}
.:53?{
kubernetes
proxy?.?8.8.8.8
log
errors
cache
}
對于以上的配置文件,CoreDNS 會根據每一個代碼塊前面的區和端點對外暴露兩個端點提供服務:
該配置文件對外暴露了兩個 DNS 服務,其中一個監聽在 5300 端口,另一個在 53 端口,請求這兩個服務時會根據不同的域名選擇不同區中的插件進行處理。
原理CoreDNS 可以通過四種方式對外直接提供 DNS 服務,分別是 UDP、gRPC、HTTPS 和 TLS:
但是無論哪種類型的 DNS 服務,最終隊會調用以下的 ServeDNS 方法,為服務的調用者提供 DNS 服務:
func?(s?*Server)?ServeDNS(ctx?context.Context,?w?dns.ResponseWriter,?r?*dns.Msg)?{
m,?_?:=?edns.Version(r)
ctx,?_?:=?incrementDepthAndCheck(ctx)
b?:=?r.Question[0].Name
var?off?int
var?end?bool
var?dshandler?*Config
w?=?request.NewScrubWriter(r,?w)
for?{
if?h,?ok?:=?s.zones[string(b[:l])];?ok?{
ctx?=?context.WithValue(ctx,?plugin.ServerCtx{},?s.Addr)
if?r.Question[0].Qtype?!=?dns.TypeDS?{
rcode,?_?:=?h.pluginChain.ServeDNS(ctx,?w,?r)
dshandler?=?h
}
off,?end?=?dns.NextLabel(q,?off)
if?end?{
break
}
}
if?r.Question[0].Qtype?==?dns.TypeDS?&&?dshandler?!=?nil?&&?dshandler.pluginChain?!=?nil?{
rcode,?_?:=?dshandler.pluginChain.ServeDNS(ctx,?w,?r)
plugin.ClientWrite(rcode)
return
}
if?h,?ok?:=?s.zones["."];?ok?&&?h.pluginChain?!=?nil?{
ctx?=?context.WithValue(ctx,?plugin.ServerCtx{},?s.Addr)
rcode,?_?:=?h.pluginChain.ServeDNS(ctx,?w,?r)
plugin.ClientWrite(rcode)
return
}
}
在上述這個已經被簡化的復雜函數中,最重要的就是調用了『插件鏈』的 ServeDNS 方法,將來源的請求交給一系列插件進行處理,如果我們使用以下的文件作為 Corefile:
example.org?{
file?/usr/local/etc/coredns/example.org
prometheus?????#?enable?metrics
errors?????????#?show?errors
log????????????#?enable?query?logs
}
那么在 CoreDNS 服務啟動時,對于當前的 example.org 這個組,它會依次加載 file、log、errors 和 prometheus 幾個插件,這里的順序是由 zdirectives.go 文件定義的,啟動的順序是從下到上:
var?Directives?=?[]string{
//?...
"prometheus",
"errors",
"log",
//?...
"file",
//?...
"whoami",
"on",
}
因為啟動的時候會按照從下到上的順序依次『包裝』每一個插件,所以在真正調用時就是從上到下執行的,這就是因為 NewServer 方法中對插件進行了組合:
func?NewServer(addr?string,?group?[]*Config)?(*Server,?error)?{
s?:=?&Server{
Addr:????????addr,
zones:???????make(map[string]*Config),
connTimeout:?5?*?time.Second,
}
for?_,?site?:=?range?group?{
s.zones[site.Zone]?=?site
if?site.registry?!=?nil?{
for?name?:=?range?enableChaos?{
if?_,?ok?:=?site.registry[name];?ok?{
s.classChaos?=?true
break
}
}
}
var?stack?plugin.Handler
for?i?:=?len(site.Plugin)?-?1;?i?>=?0;?i--?{
stack?=?site.Plugin[i](stack)
site.registerHandler(stack)
}
site.pluginChain?=?stack
}
return?s,?nil
}
對于 Corefile 里面的每一個配置組,NewServer 都會講配置組中提及的插件按照一定的順序組合起來,原理跟 Rack Middleware 的機制非常相似,插件 Plugin 其實就是一個出入參數都是 Handler 的函數:
type?(
Plugin?func(Handler)?Handler
Handler?interface?{
ServeDNS(context.Context,?dns.ResponseWriter,?*dns.Msg)?(int,?error)
Name()?string
}
)
所以我們可以將它們疊成堆棧的方式對它們進行操作,這樣在最后就會形成一個插件的調用鏈,在每個插件執行方法時都可以通過 NextOrFailure 函數調用下一個插件的 ServerDNS 方法:
func?NextOrFailure(name?string,?next?Handler,?ctx?context.Context,?w?dns.ResponseWriter,?r?*dns.Msg)?(int,?error)?{
if?next?!=?nil?{
if?span?:=?ot.SpanFromContext(ctx);?span?!=?nil?{
child?:=?span.Tracer().StartSpan(next.Name(),?ot.ChildOf(span.Context()))
defer?child.Finish()
ctx?=?ot.ContextWithSpan(ctx,?child)
}
return?next.ServeDNS(ctx,?w,?r)
}
return?dns.RcodeServerFailure,?Error(name,?errors.New("no?next?plugin?found"))
}
除了通過 ServeDNS 調用下一個插件之外,我們也可以調用 WriteMsg 方法并結束整個調用鏈。
從插件的堆疊到順序調用以及錯誤處理,我們對 CoreDNS 的工作原理已經非常清楚了,接下來我們可以簡單介紹幾個插件的作用。
LoadBalanceLoadBalance 這個插件的名字就告訴我們,使用這個插件能夠提供基于 DNS 的負載均衡功能,在 setup 中初始化時傳入了 RoundRobin 結構體:
func?setup(c?*caddy.Controller)?error?{
err?:=?parse(c)
if?err?!=?nil?{
return?plugin.Error("loadbalance",?err)
}
dnsserver.GetConfig(c).AddPlugin(func(next?plugin.Handler)?plugin.Handler?{
return?RoundRobin{Next:?next}
})
return?nil
}
當用戶請求 CoreDNS 服務時,我們會根據插件鏈調用 LoadBalance 這個包中的 ServeDNS 方法,在方法中會改變用于返回響應的 Writer:
func?(rr?RoundRobin)?ServeDNS(ctx?context.Context,?w?dns.ResponseWriter,?r?*dns.Msg)?(int,?error)?{
wrr?:=?&RoundRobinResponseWriter{w}
return?plugin.NextOrFailure(rr.Name(),?rr.Next,?ctx,?wrr,?r)
}
所以在最終服務返回響應時,會通過 RoundRobinResponseWriter 的 WriteMsg 方法寫入 DNS 消息:
func?(r?*RoundRobinResponseWriter)?WriteMsg(res?*dns.Msg)?error?{
if?res.Rcode?!=?dns.RcodeSuccess?{
return?r.ResponseWriter.WriteMsg(res)
}
res.Answer?=?roundRobin(res.Answer)
res.Ns?=?roundRobin(res.Ns)
res.Extra?=?roundRobin(res.Extra)
return?r.ResponseWriter.WriteMsg(res)
}
上述方法會將響應中的 Answer、Ns 以及 Extra 幾個字段中數組的順序打亂:
func?roundRobin(in?[]dns.RR)?[]dns.RR?{
cname?:=?[]dns.RR{}
address?:=?[]dns.RR{}
mx?:=?[]dns.RR{}
rest?:=?[]dns.RR{}
for?_,?r?:=?range?in?{
switch?r.Header().Rrtype?{
case?dns.TypeCNAME:
cname?=?append(cname,?r)
case?dns.TypeA,?dns.TypeAAAA:
address?=?append(address,?r)
case?dns.TypeMX:
mx?=?append(mx,?r)
default:
rest?=?append(rest,?r)
}
}
roundRobinShuffle(address)
roundRobinShuffle(mx)
out?:=?append(cname,?rest...)
out?=?append(out,?address...)
out?=?append(out,?mx...)
return?out
}
打亂后的 DNS 記錄會被原始的 ResponseWriter 結構寫回到 DNS 響應中。
LoopLoop 插件會檢測 DNS 解析過程中出現的簡單循環依賴,如果我們在 Corefile 中添加如下的內容并啟動 CoreDNS 服務,CoreDNS 會向自己發送一個 DNS 查詢,看最終是否會陷入循環:
.?{
loop
forward?.?127.0.0.1
}
在 CoreDNS 啟動時,它會在 setup 方法中調用 Loop.exchange 方法向自己查詢一個隨機域名的 DNS 記錄:
func?(l?*Loop)?exchange(addr?string)?(*dns.Msg,?error)?{
m?:=?new(dns.Msg)
m.SetQuestion(l.qname,?dns.TypeHINFO)
return?dns.Exchange(m,?addr)
}
如果這個隨機域名在 ServeDNS 方法中被查詢了兩次,那么就說明當前的 DNS 請求陷入了循環需要終止:
func?(l?*Loop)?ServeDNS(ctx?context.Context,?w?dns.ResponseWriter,?r?*dns.Msg)?(int,?error)?{
if?r.Question[0].Qtype?!=?dns.TypeHINFO?{
return?plugin.NextOrFailure(l.Name(),?l.Next,?ctx,?w,?r)
}
//?...
if?state.Name()?==?l.qname?{
l.inc()
}
if?l.seen()?>?2?{
log.Fatalf("Forwarding?loop?detected?in?\"%s\"?zone.?Exiting.?See?https://coredns.io/plugins/loop#troubleshooting.?Probe?query:?\"HINFO?%s\".",?l.zone,?l.qname)
}
return?plugin.NextOrFailure(l.Name(),?l.Next,?ctx,?w,?r)
}
就像 Loop 插件的 README 中寫的,這個插件只能夠檢測一些簡單的由于配置造成的循環問題,復雜的循環問題并不能通過當前的插件解決。
總結如果想要在分布式系統實現服務發現的功能,DNS 以及 CoreDNS 其實是一個非常好的選擇,CoreDNS 作為一個已經進入 CNCF 并且在 Kubernetes 中作為 DNS 服務使用的應用,其本身的穩定性和可用性已經得到了證明,同時它基于插件實現的方式非常輕量并且易于使用,插件鏈的使用也使得第三方插件的定義變得非常的方便。
總結
以上是生活随笔為你收集整理的dns服务器系统架构,详解 DNS 与 CoreDNS 的实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android继承父类的界面,Andro
- 下一篇: java 对象视图框架_Stripes视