一、上節回顧
上一節,我帶你一起學習了網絡性能的評估方法。簡單回顧一下,Linux 網絡基於 TCP/IP協議棧構建,而在協議棧的不同層,我們所關注的網絡性能也不盡相同。
在應用層,我們關注的是應用程序的並發連接數、每秒請求數、處理延遲、錯誤數等,可以使用 wrk、Jmeter 等工具,模擬用戶的負載,得到想要的測試結果。
而在傳輸層,我們關注的是 TCP、UDP 等傳輸層協議的工作狀況,比如 TCP 連接數、TCP 重傳、TCP 錯誤數等。此時,你可以使用 iperf、netperf 等,來測試 TCP 或 UDP的性能。
再向下到網絡層,我們關注的則是網絡包的處理能力,即 PPS。Linux 內核自帶的pktgen,就可以幫你測試這個指標。
由於低層協議是高層協議的基礎,所以一般情況下,我們所說的網絡優化,實際上包含了整個網絡協議棧的所有層的優化。當然,性能要求不同,具體需要優化的位置和目標並不
完全相同。
前面在評估網絡性能(比如 HTTP 性能)時,我們在測試工具中指定了網絡服務的 IP 地址。IP 地址是 TCP/IP 協議中,用來確定通信雙方的一個重要標識。每個 IP 地址又包括了
主機號和網絡號兩部分。相同網絡號的主機組成一個子網;不同子網再通過路由器連接,組成一個龐大的網絡。
然而,IP 地址雖然方便了機器的通信,卻給訪問這些服務的人們,帶來了很重的記憶負擔。我相信,沒幾個人能記得住 Github 所在的 IP 地址,因為這串字符,對人腦來說並沒
有什么含義,不符合我們的記憶邏輯。
不過,這並不妨礙我們經常使用這個服務。為什么呢?當然是因為還有更簡單、方便的方式。我們可以通過域名 github.com 訪問,而不是必須依靠具體的 IP 地址,這其實正是域
名系統 DNS 的由來。
DNS(Domain Name System),即域名系統,是互聯網中最基礎的一項服務,主要提供域名和 IP 地址之間映射關系的查詢服務。
DNS 不僅方便了人們訪問不同的互聯網服務,更為很多應用提供了,動態服務發現和全局負載均衡(Global Server Load Balance,GSLB)的機制。這樣,DNS 就可以選擇離用
戶最近的 IP 來提供服務。即使后端服務的 IP 地址發生變化,用戶依然可以用相同域名來訪問。
DNS 顯然是我們工作中基礎而重要的一個環節。那么,DNS 出現問題時,又該如何分析和排查呢?今天,我就帶你一起來看看這個問題。
二、域名與 DNS 解析
域名我們本身都比較熟悉,由一串用點分割開的字符組成,被用作互聯網中的某一台或某一組計算機的名稱,目的就是為了方便識別,互聯網中提供各種服務的主機位置。
要注意,域名是全球唯一的,需要通過專門的域名注冊商才可以申請注冊。為了組織全球互聯網中的眾多計算機,域名同樣用點來分開,形成一個分層的結構。而每個被點分割開
的字符串,就構成了域名中的一個層級,並且位置越靠后,層級越高。
我們以極客時間的網站 time.geekbang.org 為例,來理解域名的含義。這個字符串中,最后面的 org 是頂級域名,中間的 geekbang 是二級域名,而最左邊的 time 則是三級域名。
如下圖所示,注意點(.)是所有域名的根,也就是說所有域名都以點作為后綴,也可以理解為,在域名解析的過程中,所有域名都以點結束。
通過理解這幾個概念,你可以看出,域名主要是為了方便讓人記住,而 IP 地址是機器間的通信的真正機制。把域名轉換為 IP 地址的服務,也就是我們開頭提到的,域名解析服務
(DNS),而對應的服務器就是域名服務器,網絡協議則是 DNS 協議。
這里注意,DNS 協議在 TCP/IP 棧中屬於應用層,不過實際傳輸還是基於 UDP 或者 TCP協議(UDP 居多) ,並且域名服務器一般監聽在端口 53 上。
既然域名以分層的結構進行管理,相對應的,域名解析其實也是用遞歸的方式(從頂級開始,以此類推),發送給每個層級的域名服務器,直到得到解析結果。
不過不要擔心,遞歸查詢的過程並不需要你親自操作,DNS 服務器會替你完成,你要做的,只是預先配置一個可用的 DNS 服務器就可以了。
當然,我們知道,通常來說,每級 DNS 服務器,都會有最近解析記錄的緩存。當緩存命中時,直接用緩存中的記錄應答就可以了。如果緩存過期或者不存在,才需要用剛剛提到
的遞歸方式查詢。
所以,系統管理員在配置 Linux 系統的網絡時,除了需要配置 IP 地址,還需要給它配置DNS 服務器,這樣它才可以通過域名來訪問外部服務。
比如,我的系統配置的就是 114.114.114.114 這個域名服務器。你可以執行下面的命令,來查詢你的系統配置:
cat /etc/resolv.conf nameserver 114.114.114.114
實際測試命令如下:
[root@69 ~]# cat /etc/resolv.conf ; generated by /sbin/dhclient-script nameserver 218.30.19.40 nameserver 61.134.1.4
另外,DNS 服務通過資源記錄的方式,來管理所有數據,它支持 A、CNAME、MX、NS、PTR 等多種類型的記錄。比如:
A 記錄,用來把域名轉換成 IP 地址; CNAME 記錄,用來創建別名; 而 NS 記錄,則表示該域名對應的域名服務器地址
簡單來說,當我們訪問某個網址時,就需要通過 DNS 的 A 記錄,查詢該域名對應的 IP 地址,然后再通過該 IP 來訪問 Web 服務。
比如,還是以極客時間的網站 time.geekbang.org 為例,執行下面的 nslookup 命令,就可以查詢到這個域名的 A 記錄,可以看到,它的 IP 地址是 39.106.233.176:
nslookup time.geekbang.org # 域名服務器及端口信息 Server: 114.114.114.114 Address: 114.114.114.114#53 # 非權威查詢結果 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.17
實際測試命令如下:
[root@69 ~]# nslookup time.geekbang.org Server: 218.30.19.40 Address: 218.30.19.40#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176
這里要注意,由於 114.114.114.114 並不是直接管理 time.geekbang.org 的域名服務器,所以查詢結果是非權威的。使用上面的命令,你只能得到 114.114.114.114 查詢的結果。
前面還提到了,如果沒有命中緩存,DNS 查詢實際上是一個遞歸過程,那有沒有方法可以知道整個遞歸查詢的執行呢?
其實除了 nslookup,另外一個常用的 DNS 解析工具 dig ,就提供了 trace 功能,可以展示遞歸查詢的整個過程。比如你可以執行下面的命令,得到查詢結果:
# +trace 表示開啟跟蹤查詢 # +nodnssec 表示禁止 DNS 安全擴展 $ dig +trace +nodnssec time.geekbang.org ; <<>> DiG 9.11.3-1ubuntu1.3-Ubuntu <<>> +trace +nodnssec time.geekbang.org ;; global options: +cmd . 322086 IN NS m.root-servers.net. . 322086 IN NS a.root-servers.net. . 322086 IN NS i.root-servers.net. . 322086 IN NS d.root-servers.net. . 322086 IN NS g.root-servers.net. . 322086 IN NS l.root-servers.net. . 322086 IN NS c.root-servers.net. . 322086 IN NS b.root-servers.net. . 322086 IN NS h.root-servers.net. . 322086 IN NS e.root-servers.net. . 322086 IN NS k.root-servers.net. . 322086 IN NS j.root-servers.net. . 322086 IN NS f.root-servers.net. ;; Received 239 bytes from 114.114.114.114#53(114.114.114.114) in 1340 ms org. 172800 IN NS a0.org.afilias-nst.info. org. 172800 IN NS a2.org.afilias-nst.info. org. 172800 IN NS b0.org.afilias-nst.org. org. 172800 IN NS b2.org.afilias-nst.org. org. 172800 IN NS c0.org.afilias-nst.info. org. 172800 IN NS d0.org.afilias-nst.org. ;; Received 448 bytes from 198.97.190.53#53(h.root-servers.net) in 708 ms geekbang.org. 86400 IN NS dns9.hichina.com. geekbang.org. 86400 IN NS dns10.hichina.com. ;; Received 96 bytes from 199.19.54.1#53(b0.org.afilias-nst.org) in 1833 ms time.geekbang.org. 600 IN A 39.106.233.176 ;; Received 62 bytes from 140.205.41.16#53(dns10.hichina.com) in 4 ms
實際測試命令如下:
[root@69 ~]# dig +trace +nodnssec time.geekbang.org ; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.3 <<>> +trace +nodnssec time.geekbang.org ;; global options: +cmd . 794 IN NS h.root-servers.net. . 794 IN NS k.root-servers.net. . 794 IN NS m.root-servers.net. . 794 IN NS d.root-servers.net. . 794 IN NS b.root-servers.net. . 794 IN NS j.root-servers.net. . 794 IN NS l.root-servers.net. . 794 IN NS g.root-servers.net. . 794 IN NS e.root-servers.net. . 794 IN NS f.root-servers.net. . 794 IN NS c.root-servers.net. . 794 IN NS a.root-servers.net. . 794 IN NS i.root-servers.net. ;; Received 228 bytes from 218.30.19.40#53(218.30.19.40) in 52 ms org. 172800 IN NS a0.org.afilias-nst.info. org. 172800 IN NS a2.org.afilias-nst.info. org. 172800 IN NS b0.org.afilias-nst.org. org. 172800 IN NS b2.org.afilias-nst.org. org. 172800 IN NS c0.org.afilias-nst.info. org. 172800 IN NS d0.org.afilias-nst.org. ;; Received 437 bytes from 199.7.83.42#53(199.7.83.42) in 254 ms geekbang.org. 86400 IN NS dns9.hichina.com. geekbang.org. 86400 IN NS dns10.hichina.com. ;; Received 85 bytes from 199.249.120.1#53(199.249.120.1) in 117 ms time.geekbang.org. 600 IN A 39.106.233.176 ;; Received 51 bytes from 140.205.81.16#53(140.205.81.16) in 26 ms
dig trace 的輸出,主要包括四部分。
第一部分,是從 114.114.114.114 查到的一些根域名服務器(.)的 NS 記錄。 第二部分,是從 NS 記錄結果中選一個(h.root-servers.net),並查詢頂級域名 org.的 NS 記錄。 第三部分,是從 org. 的 NS 記錄中選擇一個(b0.org.afilias-nst.org),並查詢二級域名 geekbang.org. 的 NS 服務器。 最后一部分,就是從 geekbang.org. 的 NS 服務器(dns10.hichina.com)查詢最終主機 time.geekbang.org. 的 A 記錄
這個輸出里展示的各級域名的 NS 記錄,其實就是各級域名服務器的地址,可以讓你更清楚 DNS 解析的過程。 為了幫你更直觀理解遞歸查詢,我把這個過程整理成了一張流程
圖,你可以保存下來理解。
當然,不僅僅是發布到互聯網的服務需要域名,很多時候,我們也希望能對局域網內部的主機進行域名解析(即內網域名,大多數情況下為主機名)。Linux 也支持這種行為。
所以,你可以把主機名和 IP 地址的映射關系,寫入本機的 /etc/hosts 文件中。這樣,指定的主機名就可以在本地直接找到目標 IP。比如,你可以執行下面的命令來操作:
cat /etc/hosts 127.0.0.1 localhost localhost.localdomain ::1 localhost6 localhost6.localdomain6 192.168.0.100 domain.com
或者,你還可以在內網中,搭建自定義的 DNS 服務器,專門用來解析內網中的域名。而內網 DNS 服務器,一般還會設置一個或多個上游 DNS 服務器,用來解析外網的域名。
清楚域名與 DNS 解析的基本原理后,接下來,我就帶你一起來看幾個案例,實戰分析DNS 解析出現問題時,該如何定位。
三、DNS 解析失敗-案例分析
1、環境准備
本次案例還是基於 Ubuntu 18.04,同樣適用於其他的 Linux 系統。我使用的案例環境如下所示:
機器配置:2 CPU,8GB 內存。 預先安裝 docker 等工具,如 apt install docker.io。
你可以先打開一個終端,SSH 登錄到 Ubuntu 機器中,然后執行下面的命令,拉取案例中使用的 Docker 鏡像:
docker pull feisky/dnsutils Using default tag: latest ... Status: Downloaded newer image for feisky/dnsutils:latest
然后,運行下面的命令,查看主機當前配置的 DNS 服務器:
cat /etc/resolv.conf nameserver 114.114.114.114
實際測試命令如下:
[root@69 ~]# cat /etc/resolv.conf ; generated by /sbin/dhclient-script nameserver 218.30.19.40 nameserver 61.134.1.4
可以看到,我這台主機配置的 DNS 服務器是 114.114.114.114。
到這里,准備工作就完成了。接下來,我們正式進入操作環節。
2、DNS 解析失敗
首先,執行下面的命令,進入今天的第一個案例。如果一切正常,你將可以看到下面這個輸出:
# 進入案例環境的 SHELL 終端中 $ docker run -it --rm -v $(mktemp):/etc/resolv.conf feisky/dnsutils bash root@7e9ed6ed4974:/#
注意,這兒 root 后面的 7e9ed6ed4974,是 Docker 生成容器的 ID 前綴,你的環境中很可能是不同的 ID,所以直接忽略這一項就可以了。
注意:下面的代碼段中, /# 開頭的命令都表示在容器內部運行的命令。
接着,繼續在容器終端中,執行 DNS 查詢命令,我們還是查詢 time.geekbang.org 的 IP地址:
/# nslookup time.geekbang.org ;; connection timed out; no servers could be reached
你可以發現,這個命令阻塞很久后,還是失敗了,報了 connection timed out 和 noservers could be reached 錯誤。
看到這里,估計你的第一反應就是網絡不通了,到底是不是這樣呢?我們用 ping 工具檢查試試。執行下面的命令,就可以測試本地到 114.114.114.114 的連通性:
/# ping -c3 114.114.114.114 PING 114.114.114.114 (114.114.114.114): 56 data bytes 64 bytes from 114.114.114.114: icmp_seq=0 ttl=56 time=31.116 ms 64 bytes from 114.114.114.114: icmp_seq=1 ttl=60 time=31.245 ms 64 bytes from 114.114.114.114: icmp_seq=2 ttl=68 time=31.128 ms --- 114.114.114.114 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 31.116/31.163/31.245/0.058 ms
這個輸出中,你可以看到網絡是通的。那要怎么知道 nslookup 命令失敗的原因呢?這里其實有很多方法,最簡單的一種,就是開啟 nslookup 的調試輸出,查看查詢過程中的詳
細步驟,排查其中是否有異常。
比如,我們可以繼續在容器終端中,執行下面的命令:
/# nslookup -debug time.geekbang.org ;; Connection to 127.0.0.1#53(127.0.0.1) for time.geekbang.org failed: connection refused. ;; Connection to ::1#53(::1) for time.geekbang.org failed: address not available.
從這次的輸出可以看到,nslookup 連接環回地址(127.0.0.1 和 ::1)的 53 端口失敗。這里就有問題了,為什么會去連接環回地址,而不是我們的先前看到的 114.114.114.114呢?
你可能已經想到了症結所在——有可能是因為容器中沒有配置 DNS 服務器。那我們就執行下面的命令確認一下:
/# cat /etc/resolv.conf
果然,這個命令沒有任何輸出,說明容器里的確沒有配置 DNS 服務器。到這一步,很自然的,我們就知道了解決方法。在 /etc/resolv.conf 文件中,配置上 DNS 服務器就可以了。
你可以執行下面的命令,在配置好 DNS 服務器后,重新執行 nslookup 命令。自然,我們現在發現,這次可以正常解析了:
/# echo "nameserver 114.114.114.114" > /etc/resolv.conf /# nslookup time.geekbang.org Server: 114.114.114.114 Address: 114.114.114.114#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176
到這里,第一個案例就輕松解決了。最后,在終端中執行 exit 命令退出容器,Docker 就會自動清理剛才運行的容器。
實際測試命令如下:
root@luoahong:~# docker pull feisky/dnsutils Using default tag: latest latest: Pulling from feisky/dnsutils 38e2e6cd5626: Pull complete 705054bc3f5b: Pull complete c7051e069564: Pull complete 7308e914506c: Pull complete 9b20820a1a69: Pull complete 8633a2284391: Pull complete 89ab6a8002a6: Pull complete Digest: sha256:a23534daa60aad8736823219852b6dbd9b51e84ddbaf42e38b54c68954719766 Status: Downloaded newer image for feisky/dnsutils:latest root@luoahong:~# docker run -it --rm -v $(mktemp):/etc/resolv.conf feisky/dnsutils bash root@e9ceb1af6733:/# nslookup time.geekbang.org ;; connection timed out; no servers could be reached root@e9ceb1af6733:/# ping -c3 218.30.19.40 PING 218.30.19.40 (218.30.19.40): 56 data bytes 64 bytes from 218.30.19.40: icmp_seq=0 ttl=56 time=4.203 ms 64 bytes from 218.30.19.40: icmp_seq=1 ttl=56 time=2.846 ms 64 bytes from 218.30.19.40: icmp_seq=2 ttl=56 time=2.683 ms --- 218.30.19.40 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 2.683/3.244/4.203/0.681 ms root@e9ceb1af6733:/# cat /etc/resolv.conf root@e9ceb1af6733:/# echo "nameserver 218.30.19.40" > /etc/resolv.conf root@e9ceb1af6733:/# nslookup time.geekbang.org Server: 218.30.19.40 Address: 218.30.19.40#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176
四、DNS 解析不穩定-案例分析
接下來,我們再來看第二個案例。執行下面的命令,啟動一個新的容器,並進入它的終端中:
docker run -it --rm --cap-add=NET_ADMIN --dns 8.8.8.8 feisky/dnsutils bash root@0cd3ee0c8ecb:/#
然后,跟上一個案例一樣,還是運行 nslookup 命令,解析 time.geekbang.org 的 IP 地址。不過,這次要加一個 time 命令,輸出解析所用時間。如果一切正常,你可能會看到如下輸出:
/# time nslookup time.geekbang.org Server: 8.8.8.8 Address: 8.8.8.8#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m10.349s user 0m0.004s sys 0m0.0
可以看到,這次解析非常慢,居然用了 10 秒。如果你多次運行上面的 nslookup 命令,可能偶爾還會碰到下面這種錯誤:
/# time nslookup time.geekbang.org ;; connection timed out; no servers could be reached real 0m15.011s user 0m0.006s sys 0m0.006s
換句話說,跟上一個案例類似,也會出現解析失敗的情況。綜合來看,現在 DNS 解析的結果不但比較慢,而且還會發生超時失敗的情況。
這是為什么呢?碰到這種問題該怎么處理呢?
其實,根據前面的講解,我們知道,DNS 解析,說白了就是客戶端與服務器交互的過程,並且這個過程還使用了 UDP 協議。
那么,對於整個流程來說,解析結果不穩定,就有很多種可能的情況了。比方說:
DNS 服務器本身有問題,響應慢並且不穩定; 或者是,客戶端到 DNS 服務器的網絡延遲比較大; 再或者,DNS 請求或者響應包,在某些情況下被鏈路中的網絡設備弄丟了。
根據上面 nslookup 的輸出,你可以看到,現在客戶端連接的 DNS 是 8.8.8.8,這是Google 提供的 DNS 服務。對 Google 我們還是比較放心的,DNS 服務器出問題的概率
應該比較小。基本排除了 DNS 服務器的問題,那是不是第二種可能,本機到 DNS 服務器的延遲比較大呢?
前面講過,ping 可以用來測試服務器的延遲。比如,你可以運行下面的命令:
/# ping -c3 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: icmp_seq=0 ttl=31 time=137.637 ms 64 bytes from 8.8.8.8: icmp_seq=1 ttl=31 time=144.743 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=31 time=138.576 ms --- 8.8.8.8 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 137.637/140.319/144.743/3.152 ms
從 ping 的輸出可以看到,這里的延遲已經達到了 140ms,這也就可以解釋,為什么解析這么慢了。實際上,如果你多次運行上面的 ping 測試,還會看到偶爾出現的丟包現象。
ping -c3 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: icmp_seq=0 ttl=30 time=134.032 ms 64 bytes from 8.8.8.8: icmp_seq=1 ttl=30 time=431.458 ms --- 8.8.8.8 ping statistics --- 3 packets transmitted, 2 packets received, 33% packet loss round-trip min/avg/max/stddev = 134.032/282.745/431.458/148.713 ms
這也進一步解釋了,為什么 nslookup 偶爾會失敗,正是網絡鏈路中的丟包導致的。碰到這種問題該怎么辦呢?顯然,既然延遲太大,那就換一個延遲更小的 DNS 服務器,
比如電信提供的 114.114.114.114。
配置之前,我們可以先用 ping 測試看看,它的延遲是不是真的比 8.8.8.8 好。執行下面的命令,你就可以看到,它的延遲只有 31ms:
/# ping -c3 114.114.114.114 PING 114.114.114.114 (114.114.114.114): 56 data bytes 64 bytes from 114.114.114.114: icmp_seq=0 ttl=67 time=31.130 ms 64 bytes from 114.114.114.114: icmp_seq=1 ttl=56 time=31.302 ms 64 bytes from 114.114.114.114: icmp_seq=2 ttl=56 time=31.250 ms --- 114.114.114.114 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 31.130/31.227/31.302/0.072 ms
這個結果表明,延遲的確小了很多。我們繼續執行下面的命令,更換 DNS 服務器,然后,再次執行 nslookup 解析命令:
/# echo nameserver 114.114.114.114 > /etc/resolv.conf /# time nslookup time.geekbang.org Server: 114.114.114.114 Address: 114.114.114.114#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.064s user 0m0.007s sys 0m0.006s
你可以發現,現在只需要 64ms 就可以完成解析,比剛才的 10s 要好很多。
到這里,問題看似就解決了。不過,如果你多次運行 nslookup 命令,估計就不是每次都有好結果了。比如,在我的機器中,就經常需要 1s 甚至更多的時間。
/# time nslookup time.geekbang.org Server: 114.114.114.114 Address: 114.114.114.114#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m1.045s user 0m0.007s sys 0m0.004s
1s 的 DNS 解析時間還是太長了,對很多應用來說也是不可接受的。那么,該怎么解決這個問題呢?我想你一定已經想到了,那就是使用 DNS 緩存。這樣,只有第一次查詢時需
要去 DNS 服務器請求,以后的查詢,只要 DNS 記錄不過期,使用緩存中的記錄就可以了。
不過要注意,我們使用的主流 Linux 發行版,除了最新版本的 Ubuntu (如 18.04 或者更新版本)外,其他版本並沒有自動配置 DNS 緩存。
所以,想要為系統開啟 DNS 緩存,就需要你做額外的配置。比如,最簡單的方法,就是使用 dnsmasq。
dnsmasq 是最常用的 DNS 緩存服務之一,還經常作為 DHCP 服務來使用。它的安裝和配置都比較簡單,性能也可以滿足絕大多數應用程序對 DNS 緩存的需求。
我們繼續在剛才的容器終端中,執行下面的命令,就可以啟動 dnsmasq:
/# /etc/init.d/dnsmasq start * Starting DNS forwarder and DHCP server dnsmasq [ OK ]
然后,修改 /etc/resolv.conf,將 DNS 服務器改為 dnsmasq 的監聽地址,這兒是127.0.0.1。接着,重新執行多次 nslookup 命令:
/# echo nameserver 127.0.0.1 > /etc/resolv.conf /# time nslookup time.geekbang.org Server: 127.0.0.1 Address: 127.0.0.1#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.492s user 0m0.007s sys 0m0.006s /# time nslookup time.geekbang.org Server: 127.0.0.1 Address: 127.0.0.1#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.011s user 0m0.008s sys 0m0.003s
現在我們可以看到,只有第一次的解析很慢,需要 0.5s,以后的每次解析都很快,只需要11ms。並且,后面每次 DNS 解析需要的時間也都很穩定。
案例的最后,還是別忘了執行 exit,退出容器終端,Docker 會自動清理案例容器。
實際測試命令如下:
root@luoahong:~# docker run -it --rm --cap-add=NET_ADMIN --dns 8.8.8.8 feisky/dnsutils bash root@8131109d361c:/# time nslookup time.geekbang.org Server: 8.8.8.8 Address: 8.8.8.8#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.217s user 0m0.008s sys 0m0.008s root@8131109d361c:/# time nslookup time.geekbang.org Server: 8.8.8.8 Address: 8.8.8.8#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.199s user 0m0.000s sys 0m0.015s root@8131109d361c:/# time nslookup time.geekbang.org Server: 8.8.8.8 Address: 8.8.8.8#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.156s user 0m0.010s sys 0m0.014s root@8131109d361c:/# ping -c3 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: icmp_seq=0 ttl=49 time=52.674 ms 64 bytes from 8.8.8.8: icmp_seq=1 ttl=49 time=52.476 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=49 time=57.927 ms --- 8.8.8.8 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 52.476/54.359/57.927/2.524 ms root@8131109d361c:/# ping -c3 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: icmp_seq=0 ttl=49 time=52.268 ms 64 bytes from 8.8.8.8: icmp_seq=1 ttl=49 time=52.333 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=49 time=65.540 ms --- 8.8.8.8 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 52.268/56.714/65.540/6.241 ms root@8131109d361c:/# ping -c3 114.114.114.114 PING 114.114.114.114 (114.114.114.114): 56 data bytes 64 bytes from 114.114.114.114: icmp_seq=0 ttl=64 time=25.421 ms 64 bytes from 114.114.114.114: icmp_seq=1 ttl=84 time=25.312 ms 64 bytes from 114.114.114.114: icmp_seq=2 ttl=67 time=23.852 ms --- 114.114.114.114 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 23.852/24.862/25.421/0.715 ms root@8131109d361c:/# echo nameserver 114.114.114.114 > /etc/resolv.conf root@8131109d361c:/# time nslookup time.geekbang.org Server: 114.114.114.114 Address: 114.114.114.114#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.076s user 0m0.003s sys 0m0.016s root@8131109d361c:/# time nslookup time.geekbang.org Server: 114.114.114.114 Address: 114.114.114.114#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.063s user 0m0.008s sys 0m0.008s root@8131109d361c:/# /etc/init.d/dnsmasq start * Starting DNS forwarder and DHCP server dnsmasq [ OK ] root@8131109d361c:/# echo nameserver 127.0.0.1 > /etc/resolv.conf root@8131109d361c:/# time nslookup time.geekbang.org Server: 127.0.0.1 Address: 127.0.0.1#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.266s user 0m0.004s sys 0m0.017s root@8131109d361c:/# time nslookup time.geekbang.org Server: 127.0.0.1 Address: 127.0.0.1#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.017s user 0m0.004s sys 0m0.009s root@8131109d361c:/# time nslookup time.geekbang.org Server: 127.0.0.1 Address: 127.0.0.1#53 Non-authoritative answer: Name: time.geekbang.org Address: 39.106.233.176 real 0m0.019s user 0m0.008s sys 0m0.008s
五、小結
今天,我帶你一起學習了 DNS 的基本原理,並通過幾個案例,帶你一起掌握了,發現DNS 解析問題時的分析和解決思路。
DNS 是互聯網中最基礎的一項服務,提供了域名和 IP 地址間映射關系的查詢服務。很多應用程序在最初開發時,並沒考慮 DNS 解析的問題,后續出現問題后,排查好幾天才能
發現,其實是 DNS 解析慢導致的。
試想,假如一個 Web 服務的接kou ,每次都需要 1s 時間來等待 DNS 解析,那么,無論你怎么優化應用程序的內在邏輯,對用戶來說,這個接口的響應都太慢,因為響應時間總是
會大於 1 秒的。
所以,在應用程序的開發過程中,我們必須考慮到 DNS 解析可能帶來的性能問題,掌握常見的優化方法。這里,我總結了幾種常見的 DNS 優化方法。
- 對 DNS 解析的結果進行緩存。緩存是最有效的方法,但要注意,一旦緩存過期,還是要去 DNS 服務器重新獲取新記錄。不過,這對大部分應用程序來說都是可接受的。
- 對 DNS 解析的結果進行預取。這是瀏覽器等 Web 應用中最常用的方法,也就是說,不等用戶點擊頁面上的超鏈接,瀏覽器就會在后台自動解析域名,並把結果緩存起來。
- 使用 HTTPDNS 取代常規的 DNS 解析。這是很多移動應用會選擇的方法,特別是如今域名劫持普遍存在,使用 HTTP 協議繞過鏈路中的 DNS 服務器,就可以避免域名劫持的問題。
- 基於 DNS 的全局負載均衡(GSLB)。這不僅為服務提供了負載均衡和高可用的功能,還可以根據用戶的位置,返回距離最近的 IP 地址