<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          詳解 DNS 與 CoreDNS 的實(shí)現(xiàn)原理

          共 15855字,需瀏覽 32分鐘

           ·

          2020-09-18 14:46

          域名系統(tǒng)(Domain Name System)是整個(gè)互聯(lián)網(wǎng)的電話(huà)簿,它能夠?qū)⒖杀蝗死斫獾挠蛎g成可被機(jī)器理解 IP 地址,使得互聯(lián)網(wǎng)的使用者不再需要直接接觸很難閱讀和理解的 IP 地址。


          我們?cè)谶@篇文章中的第一部分會(huì)介紹 DNS 的工作原理以及一些常見(jiàn)的 DNS 問(wèn)題,而第二部分我們會(huì)介紹 DNS 服務(wù)?CoreDNS?的架構(gòu)和實(shí)現(xiàn)原理。

          DNS

          域名系統(tǒng)在現(xiàn)在的互聯(lián)網(wǎng)中非常重要,因?yàn)榉?wù)器的 IP 地址可能會(huì)經(jīng)常變動(dòng),如果沒(méi)有了 DNS,那么可能 IP 地址一旦發(fā)生了更改,當(dāng)前服務(wù)器的客戶(hù)端就沒(méi)有辦法連接到目標(biāo)的服務(wù)器了,如果我們?yōu)?IP 地址提供一個(gè)『別名』并在其發(fā)生變動(dòng)時(shí)修改別名和 IP 地址的關(guān)系,那么我們就可以保證集群對(duì)外提供的服務(wù)能夠相對(duì)穩(wěn)定地被其他客戶(hù)端訪(fǎng)問(wèn)。

          DNS 其實(shí)就是一個(gè)分布式的樹(shù)狀命名系統(tǒng),它就像一個(gè)去中心化的分布式數(shù)據(jù)庫(kù),存儲(chǔ)著從域名到 IP 地址的映射。

          工作原理

          在我們對(duì) DNS 有了簡(jiǎn)單的了解之后,接下來(lái)我們就可以進(jìn)入 DNS 工作原理的部分了,作為用戶(hù)訪(fǎng)問(wèn)互聯(lián)網(wǎng)的第一站,當(dāng)一臺(tái)主機(jī)想要通過(guò)域名訪(fǎng)問(wèn)某個(gè)服務(wù)的內(nèi)容時(shí),需要先通過(guò)當(dāng)前域名獲取對(duì)應(yīng)的 IP 地址。這時(shí)就需要通過(guò)一個(gè) DNS 解析器負(fù)責(zé)域名的解析,下面的圖片展示了 DNS 查詢(xún)的執(zhí)行過(guò)程:

          dns-resolution

          1. 本地的 DNS 客戶(hù)端向 DNS 解析器發(fā)出解析 draveness.me 域名的請(qǐng)求;

          2. DNS 解析器首先會(huì)向就近的根 DNS 服務(wù)器?.?請(qǐng)求頂級(jí)域名 DNS 服務(wù)的地址;

          3. 拿到頂級(jí)域名 DNS 服務(wù)?me.?的地址之后會(huì)向頂級(jí)域名服務(wù)請(qǐng)求負(fù)責(zé)?dravenss.me.域名解析的命名服務(wù);

          4. 得到授權(quán)的 DNS 命名服務(wù)時(shí),就可以根據(jù)請(qǐng)求的具體的主機(jī)記錄直接向該服務(wù)請(qǐng)求域名對(duì)應(yīng)的 IP 地址;

          DNS 客戶(hù)端接受到 IP 地址之后,整個(gè) DNS 解析的過(guò)程就結(jié)束了,客戶(hù)端接下來(lái)就會(huì)通過(guò)當(dāng)前的 IP 地址直接向服務(wù)器發(fā)送請(qǐng)求。

          對(duì)于 DNS 解析器,這里使用的 DNS 查詢(xún)方式是迭代查詢(xún),每個(gè) DNS 服務(wù)并不會(huì)直接返回 DNS 信息,而是會(huì)返回另一臺(tái) DNS 服務(wù)器的位置,由客戶(hù)端依次詢(xún)問(wèn)不同級(jí)別的 DNS 服務(wù)直到查詢(xún)得到了預(yù)期的結(jié)果;另一種查詢(xún)方式叫做遞歸查詢(xún),也就是 DNS 服務(wù)器收到客戶(hù)端的請(qǐng)求之后會(huì)直接返回準(zhǔn)確的結(jié)果,如果當(dāng)前服務(wù)器沒(méi)有存儲(chǔ) DNS 信息,就會(huì)訪(fǎng)問(wèn)其他的服務(wù)器并將結(jié)果返回給客戶(hù)端。

          域名層級(jí)

          域名層級(jí)是一個(gè)層級(jí)的樹(shù)形結(jié)構(gòu),樹(shù)的最頂層是根域名,一般使用?.?來(lái)表示,這篇文章所在的域名一般寫(xiě)作?draveness.me,但是這里的寫(xiě)法其實(shí)省略了最后的?.,也就是全稱(chēng)域名(FQDN)dravenss.me.。

          dns-namespace

          根域名下面的就是?comnet?和?me?等頂級(jí)域名以及次級(jí)域名?draveness.me,我們一般在各個(gè)域名網(wǎng)站中購(gòu)買(mǎi)和使用的都是次級(jí)域名、子域名和主機(jī)名了。

          域名服務(wù)器

          既然域名的命名空間是樹(shù)形的,那么用于處理域名解析的 DNS 服務(wù)器也是樹(shù)形的,只是在樹(shù)的組織和每一層的職責(zé)上有一些不同。DNS 解析器從根域名服務(wù)器查找到頂級(jí)域名服務(wù)器的 IP 地址,又從頂級(jí)域名服務(wù)器查找到權(quán)威域名服務(wù)器的 IP 地址,最終從權(quán)威域名服務(wù)器查出了對(duì)應(yīng)服務(wù)的 IP 地址。

          $?dig?-t?A?draveness.me?+trace

          我們可以使用 dig 命令追蹤?draveness.me?域名對(duì)應(yīng) IP 地址是如何被解析出來(lái)的,首先會(huì)向預(yù)置的 13 組根域名服務(wù)器發(fā)出請(qǐng)求獲取頂級(jí)域名的地址:

          .????????????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

          根域名服務(wù)器是 DNS 中最高級(jí)別的域名服務(wù)器,這些服務(wù)器負(fù)責(zé)返回頂級(jí)域的權(quán)威域名服務(wù)器地址,這些域名服務(wù)器的數(shù)量總共有 13 組,域名的格式從上面返回的結(jié)果可以看到是?.root-servers.net,每個(gè)根域名服務(wù)器中只存儲(chǔ)了頂級(jí)域服務(wù)器的 IP 地址,大小其實(shí)也只有 2MB 左右,雖然域名服務(wù)器總共只有 13 組,但是每一組服務(wù)器都通過(guò)提供了鏡像服務(wù),全球大概也有幾百臺(tái)的根域名服務(wù)器在運(yùn)行。

          在這里,我們獲取到了以下的 5 條 NS 記錄,也就是 5 臺(tái)?me.?定義域名 DNS 服務(wù)器:

          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

          當(dāng) DNS 解析器從根域名服務(wù)器中查詢(xún)到了頂級(jí)域名?.me?服務(wù)器的地址之后,就可以訪(fǎng)問(wèn)這些頂級(jí)域名服務(wù)器其中的一臺(tái)?b2.nic.me?獲取權(quán)威 DNS 的服務(wù)器的地址了:

          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

          這里的權(quán)威 DNS 服務(wù)是作者在域名提供商進(jìn)行配置的,當(dāng)有客戶(hù)端請(qǐng)求?draveness.me?域名對(duì)應(yīng)的 IP 地址時(shí),其實(shí)會(huì)從作者使用的 DNS 服務(wù)商 DNSPod 處請(qǐng)求服務(wù)的 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?服務(wù)中獲取了當(dāng)前博客的 IP 地址?123.56.94.228,瀏覽器或者其他設(shè)備就能夠通過(guò) IP 向服務(wù)器獲取請(qǐng)求的內(nèi)容了。

          從整個(gè)解析過(guò)程,我們可以看出 DNS 域名服務(wù)器大體分成三類(lèi),根域名服務(wù)、頂級(jí)域名服務(wù)以及權(quán)威域名服務(wù)三種,獲取域名對(duì)應(yīng)的 IP 地址時(shí),也會(huì)像遍歷一棵樹(shù)一樣按照從頂層到底層的順序依次請(qǐng)求不同的服務(wù)器。

          膠水記錄

          在通過(guò)服務(wù)器解析域名的過(guò)程中,我們看到當(dāng)請(qǐng)求?me.?頂級(jí)域名服務(wù)器的時(shí)候,其實(shí)返回了?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.
          ...

          就像我們最開(kāi)始說(shuō)的,在互聯(lián)網(wǎng)中想要請(qǐng)求服務(wù),最終一定需要獲取 IP 提供服務(wù)的服務(wù)器的 IP 地址;同理,作為?b0.nic.me?作為一個(gè) DNS 服務(wù)器,我也必須獲取它的 IP 地址才能獲得次級(jí)域名的 DNS 信息,但是這里就陷入了一種循環(huán):

          1. 如果想要獲取?dravenss.me?的 IP 地址,就需要訪(fǎng)問(wèn)?me?頂級(jí)域名服務(wù)器?b0.nic.me

          2. 如果想要獲取?b0.nic.me?的 IP 地址,就需要訪(fǎng)問(wèn)?me?頂級(jí)域名服務(wù)器?b0.nic.me

          3. 如果想要獲取?b0.nic.me?的 IP 地址,就需要訪(fǎng)問(wèn)?me?頂級(jí)域名服務(wù)器?b0.nic.me

          為了解決這一個(gè)問(wèn)題,我們引入了膠水記錄(Glue Record)這一概念,也就是在出現(xiàn)循環(huán)依賴(lài)時(shí),直接在上一級(jí)作用域返回 DNS 服務(wù)器的 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

          ...

          也就是同時(shí)返回 NS 記錄和 A(或 AAAA) 記錄,這樣就能夠解決域名解析出現(xiàn)的循環(huán)依賴(lài)問(wèn)題。

          服務(wù)發(fā)現(xiàn)

          講到現(xiàn)在,我們其實(shí)能夠發(fā)現(xiàn) DNS 就是一種最早的服務(wù)發(fā)現(xiàn)的手段,通過(guò)雖然服務(wù)器的 IP 地址可能會(huì)經(jīng)常變動(dòng),但是通過(guò)相對(duì)不會(huì)變動(dòng)的域名,我們總是可以找到提供對(duì)應(yīng)服務(wù)的服務(wù)器。

          在微服務(wù)架構(gòu)中,服務(wù)注冊(cè)的方式其實(shí)大體上也只有兩種,一種是使用 Zookeeper 和 etcd 等配置管理中心,另一種是使用 DNS 服務(wù),比如說(shuō) Kubernetes 中的 CoreDNS 服務(wù)。

          使用 DNS 在集群中做服務(wù)發(fā)現(xiàn)其實(shí)是一件比較容易的事情,這主要是因?yàn)榻^大多數(shù)的計(jì)算機(jī)上都會(huì)安裝 DNS 服務(wù),所以這其實(shí)就是一種內(nèi)置的、默認(rèn)的服務(wù)發(fā)現(xiàn)方式,不過(guò)使用 DNS 做服務(wù)發(fā)現(xiàn)也會(huì)有一些問(wèn)題,因?yàn)樵谀J(rèn)情況下 DNS 記錄的失效時(shí)間是 600s,這對(duì)于集群來(lái)講其實(shí)并不是一個(gè)可以接受的時(shí)間,在實(shí)踐中我們往往會(huì)啟動(dòng)單獨(dú)的 DNS 服務(wù)滿(mǎn)足服務(wù)發(fā)現(xiàn)的需求。

          CoreDNS

          CoreDNS 其實(shí)就是一個(gè) DNS 服務(wù),而 DNS 作為一種常見(jiàn)的服務(wù)發(fā)現(xiàn)手段,所以很多開(kāi)源項(xiàng)目以及工程師都會(huì)使用 CoreDNS 為集群提供服務(wù)發(fā)現(xiàn)的功能,Kubernetes 就在集群中使用 CoreDNS 解決服務(wù)發(fā)現(xiàn)的問(wèn)題。

          cncf-logo

          作為一個(gè)加入 CNCF(Cloud Native Computing Foundation) 的服務(wù) CoreDNS 的實(shí)現(xiàn)可以說(shuō)的非常的簡(jiǎn)單。

          架構(gòu)

          整個(gè) CoreDNS 服務(wù)都建立在一個(gè)使用 Go 編寫(xiě)的 HTTP/2 Web 服務(wù)器?Caddy · GitHub?上,CoreDNS 整個(gè)項(xiàng)目可以作為一個(gè) Caddy 的教科書(shū)用法。

          coredns-architecture

          CoreDNS 的大多數(shù)功能都是由插件來(lái)實(shí)現(xiàn)的,插件和服務(wù)本身都使用了 Caddy 提供的一些功能,所以項(xiàng)目本身也不是特別的復(fù)雜。

          插件

          作為基于 Caddy 的 Web 服務(wù)器,CoreDNS 實(shí)現(xiàn)了一個(gè)插件鏈的架構(gòu),將很多 DNS 相關(guān)的邏輯都抽象層了一層一層的插件,包括 Kubernetes 等功能,每一個(gè)插件都是一個(gè)遵循如下協(xié)議的結(jié)構(gòu)體:

          type?(
          ????Plugin?func(Handler)?Handler

          ????Handler?interface
          ?{
          ????????ServeDNS(context.Context,?dns.ResponseWriter,?*dns.Msg)?(int,?error)
          ????????Name()?string
          ????}
          )

          所以只需要為插件實(shí)現(xiàn)?ServeDNS?以及?Name?這兩個(gè)接口并且寫(xiě)一些用于配置的代碼就可以將插件集成到 CoreDNS 中。

          Corefile

          另一個(gè) CoreDNS 的特點(diǎn)就是它能夠通過(guò)簡(jiǎn)單易懂的 DSL 定義 DNS 服務(wù),在 Corefile 中就可以組合多個(gè)插件對(duì)外提供服務(wù):

          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
          }

          對(duì)于以上的配置文件,CoreDNS 會(huì)根據(jù)每一個(gè)代碼塊前面的區(qū)和端點(diǎn)對(duì)外暴露兩個(gè)端點(diǎn)提供服務(wù):

          coredns-corefile-example

          該配置文件對(duì)外暴露了兩個(gè) DNS 服務(wù),其中一個(gè)監(jiān)聽(tīng)在 5300 端口,另一個(gè)在 53 端口,請(qǐng)求這兩個(gè)服務(wù)時(shí)會(huì)根據(jù)不同的域名選擇不同區(qū)中的插件進(jìn)行處理。

          原理

          CoreDNS 可以通過(guò)四種方式對(duì)外直接提供 DNS 服務(wù),分別是 UDP、gRPC、HTTPS 和 TLS:

          coredns-servers

          但是無(wú)論哪種類(lèi)型的 DNS 服務(wù),最終隊(duì)會(huì)調(diào)用以下的?ServeDNS?方法,為服務(wù)的調(diào)用者提供 DNS 服務(wù):

          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
          ????}
          }

          在上述這個(gè)已經(jīng)被簡(jiǎn)化的復(fù)雜函數(shù)中,最重要的就是調(diào)用了『插件鏈』的?ServeDNS?方法,將來(lái)源的請(qǐng)求交給一系列插件進(jìn)行處理,如果我們使用以下的文件作為 Corefile:

          example.org?{
          ????file?/usr/local/etc/coredns/example.org
          ????prometheus?????#?enable?metrics
          ????errors?????????#?show?errors
          ????log????????????#?enable?query?logs
          }

          那么在 CoreDNS 服務(wù)啟動(dòng)時(shí),對(duì)于當(dāng)前的?example.org?這個(gè)組,它會(huì)依次加載?filelog、errors?和?prometheus?幾個(gè)插件,這里的順序是由 zdirectives.go 文件定義的,啟動(dòng)的順序是從下到上:

          var?Directives?=?[]string{
          ??//?...
          ????"prometheus",
          ????"errors",
          ????"log",
          ??//?...
          ????"file",
          ??//?...
          ????"whoami",
          ????"on",
          }

          因?yàn)閱?dòng)的時(shí)候會(huì)按照從下到上的順序依次『包裝』每一個(gè)插件,所以在真正調(diào)用時(shí)就是從上到下執(zhí)行的,這就是因?yàn)?NewServer?方法中對(duì)插件進(jìn)行了組合:

          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
          }

          對(duì)于 Corefile 里面的每一個(gè)配置組,NewServer?都會(huì)講配置組中提及的插件按照一定的順序組合起來(lái),原理跟 Rack Middleware 的機(jī)制非常相似,插件?Plugin?其實(shí)就是一個(gè)出入?yún)?shù)都是?Handler?的函數(shù):

          type?(
          ????Plugin?func(Handler)?Handler

          ????Handler?interface
          ?{
          ????????ServeDNS(context.Context,?dns.ResponseWriter,?*dns.Msg)?(int,?error)
          ????????Name()?string
          ????}
          )

          所以我們可以將它們疊成堆棧的方式對(duì)它們進(jìn)行操作,這樣在最后就會(huì)形成一個(gè)插件的調(diào)用鏈,在每個(gè)插件執(zhí)行方法時(shí)都可以通過(guò)?NextOrFailure?函數(shù)調(diào)用下一個(gè)插件的?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"))
          }

          除了通過(guò)?ServeDNS?調(diào)用下一個(gè)插件之外,我們也可以調(diào)用?WriteMsg?方法并結(jié)束整個(gè)調(diào)用鏈。

          coredns-plugin-chain

          從插件的堆疊到順序調(diào)用以及錯(cuò)誤處理,我們對(duì) CoreDNS 的工作原理已經(jīng)非常清楚了,接下來(lái)我們可以簡(jiǎn)單介紹幾個(gè)插件的作用。

          loadbalance

          loadbalance 這個(gè)插件的名字就告訴我們,使用這個(gè)插件能夠提供基于 DNS 的負(fù)載均衡功能,在?setup?中初始化時(shí)傳入了?RoundRobin?結(jié)構(gòu)體:

          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
          }

          當(dāng)用戶(hù)請(qǐng)求 CoreDNS 服務(wù)時(shí),我們會(huì)根據(jù)插件鏈調(diào)用 loadbalance 這個(gè)包中的?ServeDNS?方法,在方法中會(huì)改變用于返回響應(yīng)的?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)
          }

          所以在最終服務(wù)返回響應(yīng)時(shí),會(huì)通過(guò)?RoundRobinResponseWriter?的?WriteMsg?方法寫(xiě)入 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)
          }

          上述方法會(huì)將響應(yīng)中的?Answer、Ns?以及?Extra?幾個(gè)字段中數(shù)組的順序打亂:

          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 記錄會(huì)被原始的?ResponseWriter?結(jié)構(gòu)寫(xiě)回到 DNS 響應(yīng)中。

          loop

          loop 插件會(huì)檢測(cè) DNS 解析過(guò)程中出現(xiàn)的簡(jiǎn)單循環(huán)依賴(lài),如果我們?cè)?Corefile 中添加如下的內(nèi)容并啟動(dòng) CoreDNS 服務(wù),CoreDNS 會(huì)向自己發(fā)送一個(gè) DNS 查詢(xún),看最終是否會(huì)陷入循環(huán):

          .?{
          ????loop
          ????forward?.?127.0.0.1
          }

          在 CoreDNS 啟動(dòng)時(shí),它會(huì)在?setup?方法中調(diào)用?Loop.exchange?方法向自己查詢(xún)一個(gè)隨機(jī)域名的 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)
          }

          如果這個(gè)隨機(jī)域名在?ServeDNS?方法中被查詢(xún)了兩次,那么就說(shuō)明當(dāng)前的 DNS 請(qǐng)求陷入了循環(huán)需要終止:

          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 中寫(xiě)的,這個(gè)插件只能夠檢測(cè)一些簡(jiǎn)單的由于配置造成的循環(huán)問(wèn)題,復(fù)雜的循環(huán)問(wèn)題并不能通過(guò)當(dāng)前的插件解決。

          總結(jié)

          如果想要在分布式系統(tǒng)實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)的功能,DNS 以及 CoreDNS 其實(shí)是一個(gè)非常好的選擇,CoreDNS 作為一個(gè)已經(jīng)進(jìn)入 CNCF 并且在 Kubernetes 中作為 DNS 服務(wù)使用的應(yīng)用,其本身的穩(wěn)定性和可用性已經(jīng)得到了證明,同時(shí)它基于插件實(shí)現(xiàn)的方式非常輕量并且易于使用,插件鏈的使用也使得第三方插件的定義變得非常的方便。

          References

          • What is DNS? | How DNS works

          • 移動(dòng)互聯(lián)網(wǎng)時(shí)代,如何優(yōu)化你的網(wǎng)絡(luò) —— 域名解析篇

          • How Queries Are Processed in CoreDNS

          • Domain Name System

          • DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION · RFC1035

          • A fun and colorful explanation of how DNS works.

          • Root Servers

          • What is the DNS Protocol?

          • Root name server · Wikipedia

          • CoreDNS for Kubernetes Service Discovery, Take 2

          • Kubernetes DNS-Based Service Discovery

          • CoreDNS Manual





          K8S進(jìn)階訓(xùn)練營(yíng),點(diǎn)擊下方圖片了解詳情


          瀏覽 69
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲日韩小电影 | 内射19p | 岛国爱情动作片,91,麻豆 | 国模小丫 | 中国一级大黄片 |