DNS 是什么
DNS (Domain Name System), 也叫網域名稱系統,是互聯網的一項服務。它實質上是一個 域名 和 IP 相互映射的分布式數據庫,有了它,我們就可以通過域名更方便的訪問互聯網。
DNS有以下特點:
- 分布式的
- 協議支持TCP 和 UDP, 常用端口是53
- 每一級域名的長度限制是63
- 域名總長度限制是253
那么,什么情況下使用TCP,什么情況下使用UDP呢?
最早的時候,DNS 的UDP報文上限大小是512 字節, 所以當某個response大小超過512 (返回信息太多),DNS服務就會使用TCP協議來傳輸。后來DNS 協議擴展了自己的UDP協議,DNS client 發出查詢請求時,可以指定自己能接收超過512字節的UDP包, 這種情況下,DNS 還是會使用UDP協議
分層的數據庫結構
DNS 的結構跟Linux 文件系統很相似,像一棵倒立的樹。 下面用站長之家的域名舉例:

最上面的.是根域名, 接著是頂級域名com,再下來是站長之家域名chinaz 依次類推。 使用域名時,從下而上。 s.tool.chinaz.com. 就是一個完整的域名, www.chinaz.com. 也是。
之所以設計這樣復雜的樹形結構, 是為了防止名稱沖突。 這樣一棵樹結構,當然可以存儲在一臺機器上,但現實世界中完整的域名非常多,并且每天都在新增、刪除大量的域名,存在一臺機器上,對單機器的存儲性能就是不小的挑戰。 另外,集中管理還有一個缺點就是管理不夠靈活。 可以想象一下,每次新增、刪除域名都需要向中央數據庫申請是多么麻煩。 所以現實中的DNS 都是分布式存儲的。
根域名服務器只管理頂級域,同時把每個頂級域的管理委派給各個頂級域,所以當你想要申請com下的二級域名時,找com域名注冊中心就好了。 例如你申請了上圖的chinaz.com二級域名, chinaz.com 再向下的域名就歸你管理了。 當你管理chinaz.com的子域名時,你可以搭建自己的nameserver, 在.com注冊中心把chinaz.com的管理權委派給自己搭建的nameserver。 自建nameserver 和不自建的結構圖如下:

一般情況下,能不自建就不要自建,因為維護一個高可用的DNS也并非容易。據我所知,有兩種情況需要搭建自己的nameserver:
- 搭建對內的DNS。公司內部機器眾多,通過ip相互訪問太過凌亂,這時可以搭建對內的nameserver,允許內部服務器通過域名互通
- 公司對域名廠商提供的nameserver性能不滿意。雖然頂級域名注冊商都有自己的nameserver, 但注冊商提供的nameserver 并不專業,在性能和穩定性上無法滿足企業需求,這時就需要企業搭建自己的高性能nameserver,比如增加智能解析功能,讓不同地域的用戶訪問最近的IP,以此來提高服務質量
概括一下DNS的分布式管理, 當把一個域委派給一個nameserver后,這個域下的管理權都交由此nameserver處理。 這種設計一方面解決了存儲壓力,另一方面提高了域名管理的靈活性 (這種結構像極了Linux File System, 可以把任何一個子目錄掛載到另一個磁盤,還可以把它下面的子目錄繼續掛載出去)
頂級域名
像com這樣的頂級域名,由ICANN 嚴格控制,是不允許隨便創建的。頂級域名分兩類:
- 通用頂級域名
- 國家頂級域名
通用頂級域名常見的如.com、 .org、.edu等, 國家頂級域名如我國的.cn, 美國的.us。 一般公司申請公網域名時,如果是跨國產品,應該選擇通用頂級域名;如果沒有跨國業務,看自己喜好(可以對比各家頂級域的服務、穩定性等再做選擇)。 這里說一下幾個比較熱的頂級域,完整的頂級域參見維基百科。
me
me頂級域其實是國家域名, 是黑山共和國
的國家域名,只不過它對個人開發申請,所以很多個人博主就用它作為自己的博客域名(本博客也是這么來的~)
io
很多開源項目常用io做頂級域名,它也是國家域名。 因為io 與計算機中的 input/output 縮寫相同,和計算機的二機制10也很像,給人一種geek的感覺。相較于.com域名,.io下的資源很多,更多選擇.
DNS 解析流程
聊完了DNS 的基本概念,我們再來聊一聊DNS 的解析流程。 當我們通過瀏覽器或者應用程序訪問互聯網時,都會先執行一遍DNS解析流程。標準glibc提供了libresolv.so.2 動態庫,我們的應用程序就是用它進行域名解析(也叫resolving)的, 它還提供了一個配置文件/etc/nsswitch.conf
來控制resolving行為,配置文件中最關鍵的是這行:
hosts: files dns myhostname
它決定了resolving 的順序,默認是先查找hosts文件,如果沒有匹配到,再進行DNS解析。默認的解析流程如下圖:

上圖主要描述了client端的解析流程,我們可以看到最主要的是第四步請求本地DNS服務器去執行resolving,它會根據本地DNS服務器配置,發送解析請求到遞歸解析服務器(稍后介紹什么是遞歸解析服務器), 本地DNS服務器在 /etc/resolv.conf
中配置。 下面我們再來看看服務端的resolving流程:

我們分析一下解析流程:
- 客戶端向本地DNS服務器(遞歸解析服務器) 發出解析tool.chinaz.com域名的請求
- 本地dns服務器查看緩存,是否有緩存過tool.chinaz.com域名,如果有直接返回給客戶端;如果沒有執行下一步
- 本地dns服務器向根域名服務器發送請求,查詢com頂級域的nameserver 地址
- 拿到com域名的IP后,再向com nameserver發送請求,獲取chinaz域名的nameserver地址
- 繼續請求chinaz的nameserver, 獲取tool域名的地址,最終得到了tool.chinaz.com的IP,本地dns服務器把這個結果緩存起來,以供下次查詢快速返回
- 本地dns服務器把把結果返回給客戶端
遞歸解析服務器 vs 權威域名服務器
我們在解析流程中發現兩類DNS服務器,客戶端直接訪問的是 遞歸解析服務器
, 它在整個解析過程中也最忙。 它的查詢步驟是遞歸的,從根域名服務器開始,一直詢問到目標域名。
遞歸解析服務器通過請求一級一級的權威域名服務器,獲得下一目標的地址,直到找到目標域名的 權威域名服務器
簡單來說: 遞歸解析服務器
是負責解析域名的, 權威域名服務器
是負責存儲域名記錄的
遞歸解析服務器一般由ISP提供,除此之外也有一些比較出名的公共遞歸解析服務器, 如谷歌的8.8.8.8,聯通的114,BAT也都有推出公共遞歸解析服務器,但性能最好的應該還是你的ISP提供的,只是可能會有 DNS劫持
的問題
緩存
由于整個解析過程非常復雜,所以DNS 通過緩存技術來實現服務的魯棒性。 當遞歸nameserver解析過tool.chianaz.com 域名后,再次收到tool.chinaz.com查詢時,它不會再走一遍遞歸解析流程,而是把上一次解析結果的緩存直接返回。 并且它是分級緩存的,也就是說,當下次收到的是www.chinaz.com的查詢時, 由于這臺遞歸解析服務器已經知道chinaz.com的權威nameserver, 所以它只需要再向chinaz.com nameserver 發送一個查詢www的請求就可以了。
根域名服務器
遞歸解析服務器是怎么知道 根域名服務器
的地址的呢? 根域名服務器的地址是固定的,目前全球有13個根域名解析服務器,這13條記錄持久化在遞歸解析服務器中:

為什么只有13個根域名服務器呢,不是應該越多越好來做負載均衡嗎? 之前說過DNS 協議使用了UDP查詢, 由于UDP查詢中能保證性能的最大長度是512字節,要讓所有根域名服務器數據能包含在512字節的UDP包中, 根服務器只能限制在13個, 而且每個服務器要使用字母表中單字母名
智能解析
智能解析,就是當一個域名對應多個IP時,當你查詢這個域名的IP,會返回離你最近的IP。 由于國內不同運營商之間的帶寬很低,所以電信用戶訪問聯通的IP就是一個災難,而智能DNS解析就能解決這個問題。
智能解析依賴EDNS協議,這是google 起草的DNS擴展協議, 修改比較簡單,就是在DNS包里面添加origin client IP, 這樣nameserver 就能根據client IP 返回距離client 比較近的server IP 了
國內最新支持EDNS的就是DNSPod 了,DNSPod 是國內比較流行的域名解析廠商,很多公司會把域名利用DNSPod 加速, 它已經被鵝廠收購
域名注冊商
一般我們要注冊域名,都要需要找域名注冊商,比如說我想注冊hello.com,那么我需要找com域名注冊商注冊hello域名。com的域名注冊商不止一家, 這些域名注冊商也是從ICANN 拿到的注冊權, 參見如何申請成為.com域名注冊商
那么,域名注冊商
和 權威域名解析服務器
有什么關系呢? 域名注冊商都會自建權威域名解析服務器,比如你在狗爹上申請一個.com下的二級域名,你并不需要搭建nameserver, 直接在godaddy控制中心里管理你的域名指向就可以了, 原因就是你新域名的權威域名服務器默認由域名注冊商提供。 當然你也可以更換,比如從godaddy申請的境外域名,把權威域名服務器改成DNSPod,一方面加快國內解析速度,另一方面還能享受DNSPod 提供的智能解析功能
用bind搭建域名解析服務器
由于網上介紹bind搭建的文章實在太多了,我就不再贅述了, 喜歡動手的朋友可以網上搜一搜搭建教程,一步步搭建一個本地的nameserver 玩一玩。這里主要介紹一下bind 的配置文件吧
bind 的配置文件分兩部分: bind配置文件
和 zone配置文件
bind配置文件
bind配置文件位于/etc/named.conf
,它主要負責bind功能配置,如zone路徑、日志、安全、主從等配置

其中最主要的是添加zone的配置以及指定zone配置文件。 recursion
開啟遞歸解析功能, 這個如果是no, 那么此bind服務只能做權威解析服務,當你的bind服務對外時,打開它會有安全風險,如何防御不當,會讓你的nameserver 被hacker 用來做肉雞
zone配置文件
zone的配置文件在bind配置文件中指定,下圖是一份簡單的zone配置:

zone的配置是nameserver的核心配置, 它指定了DNS 資源記錄,如SOA、A、CNAME、AAAA等記錄,各種記錄的概念網上資料太多,我這里就不重復了。其中主要講一下SOA 和 CNAME 的作用。
SOA記錄
SOA 記錄表示此域名的權威解析服務器地址。 上文講了權威解析服務器和遞歸解析服務器的差別, 當所有遞歸解析服務器中有沒你域名解析的緩存時,它們就會回源來請求此域名的SOA記錄,也叫權威解析記錄
CNAME
CNAME 的概念很像別名,它的處理邏輯也如此。 一個server 執行resloving 時,發現name 是一個CNAME, 它會轉而查詢這個CNAME的A記錄。一般來說,能使用CNAME的地方都可以用A記錄代替, 那么為什么還要發明CNAME這樣一個東西呢? 它是讓多個域名指向同一個IP的一種快捷手段, 這樣當最低層的CNAME 對應的IP換了之后,上層的CNAME 不用做任何改動。就像我們代碼中的硬編碼,我們總會去掉這些硬編碼,用一個變量來表示,這樣當這個變量變化時,我們只需要修改一處
配置完之后可以用named-checkconf
和 named-checkzone
兩個命令來check我們的配置文件有沒有問題, 之后就可以啟動bind服務了:
$> service named start
Redirecting to /bin/systemctl restart named.service
我們用netstat -ntlp
來檢查一下服務是否啟動:

53端口已啟動,那么我們測試一下效果, 用dig解析一下www.hello.com
域名, 使用127.0.0.1 作為遞歸解析服務器

我們看到dig的結果跟我們配置文件中配置的一樣是1.2.3.4,DNS完成了它的使命,根據域名獲取到IP,但我們這里用來做示范的IP明顯是個假IP:)
用DNS 實現負載均衡
一個域名添加多條A記錄,解析時使用輪詢的方式返回隨機一條,流量將會均勻分類到多個A記錄。
www IN A 1.2.3.4
www IN A 1.2.3.5
上面的配置中,我們給www域添加了兩條A記錄, 這種做法叫multi-homed hosts
, 它的效果是:當我們請求nameserver 解析www.hello.com 域名時,返回的IP會在兩個IP中輪轉(默認行為,有些智能解析DNS會根據IP判斷,返回一個離client近的IP, 距離 請搜索DNS智能解析
)。
其實每次DNS解析請求時,nameserver都會返回全部IP,如上面配置,它會把1.2.3.4 和1.2.3.5 都返回給client端。 那么它是怎么實現RR的呢? nameserver 只是每次返回的IP排序不同,客戶端會把response里的第一個IP用來發請求。
** DNS負載均衡 vs LVS專業負載均衡**
和 LVS 這種專業負載均衡工具相比,在DNS層做負載均衡有以下特點:
- 實現非常簡單
- 默認只能通過RR方式調度
- DNS 對后端服務不具備健康檢查
- DNS 故障恢復時間比較長(DNS服務之間有緩存)
- 可負載的rs數量有限(受DNS response包大小限制)
真實場景中,還需要根據需求選擇相應的負載均衡策略
子域授權
我們從.com域下申請一個二級域名hello.com后, 發展到某一天我們的公司擴大了,需要拆分兩個事業部A和B, 并且公司給他們都分配了三級域名 a.hello.com
和 b.hello.com
, 域名結構如下圖:

再發展一段時間, A部門和B部門內部業務太多,需要頻繁的為新產品申請域名, 這個時候他們就想搭建自己的namserver, 并且需要上一級把相應的域名管理權交給自己, 他們期望的結構如下:

注意 第一階段 和 第二階段的區別: 第一階段, A部門想申請a.hello.com 下的子域名,需要向上級申請, 整個a.hello.com 域的管理都在總公司; 第二階段, A部門先自己搭建nameserver,然后總公司把 a.hello.com 域管理權轉交給 自建的nameserver, 這個轉交管理權的行為,就叫 子域授權
子域授權分兩部操作:
- A部門自建nameserver, 并且在zone配置文件中指定a.hello.com 的 權威解析服務器為自己的nameserver地址
- 總公司在nameserver 上增加一條NS記錄, 把a.hello.com 域授權給A部門的nameserver
第一步我們在用bind搭建域名解析服務器
里講過, 只要在 zone配置文件里指定SOA記錄就好:
@ IN SOA ns.a.hello.com admin.a.hello.com. (……)
第二步,在hello.com域的nameserver 上添加一條NS記錄:
a.hello.com IN NS ns.a.hello.com
ns.a.hello.com IN A xx.xx.xx.xx (自建nameserver的IP)
這樣當解析xx.a.hello.com 域名時, hello.com nameserver 發現配置中有NS記錄,就會繼續遞歸向下解析
DNS 調試工具
OPS 常用的DNS調試工具有: host, nslookup, dig
這三個命令都屬于 bind-utils 包, 也就是bind工具集, 它們的使用復雜度、功能 依次遞增。 關于它們的使用, man 手冊和網上有太多教程,這里簡單分析一下dig命令的輸出吧:

dig 的參數非常多, 功能也很多,詳細使用方法大家自行man吧
其他
DNS 放大攻擊
DNS 放大攻擊屬于DoS攻擊的一種,是通過大量流量占滿目標機帶寬, 使得目標機對正常用戶的請求拒絕連接從而掛掉。
思路
正常的流量攻擊,hack機向目標機建立大量request-response, 但這樣存在的問題是需要大量的hack機器。 因為服務器一般的帶寬遠大于家用網絡, 如果我們自己的家用機用來做hack機器,還沒等目標機的帶寬占滿,我們的帶寬早超載了。
原理
DNS 遞歸解析的流程比較特殊, 我們可以通過幾個字節的query請求,換來幾百甚至幾千字節的resolving應答(流量放大)
, 并且大部分服務器不會對DNS服務器做防御。 那么hacker們只要可以偽裝DNS query包的source IP, 從而讓DNS 服務器發送大量的response到目標機,就可以實現DoS攻擊。

但一般常用的DNS服務器都會對攻擊請求做過濾,所以找DNS服務器漏洞也是一個問題。 詳細的放大攻擊方法大家有興趣自行google吧,這里只做一個簡單介紹 :)
好用的公共遞歸解析DNS服務器
見: 公共DNS哪家強