理解OpenShift(2):網路之 DNS(域名服務)
理解OpenShift(1):網路之 Router 和 Route
OpenShift 叢集中,至少有三個地方需要用到 DNS:
- 一是Pod 中的應用通過域名訪問外網的時候,需要DNS來解析外網的域名
- 二是在叢集內部(pod 中或者宿主機上)通過服務的域名來訪問叢集內服務的時候,這也是通常所說的服務發現功能,需要通過服務域名來先發現(獲取其IP地址)再使用該服務
- 三是從叢集外部通過域名訪問部署在OpenShift pod 中的服務的時候,需要DNS來解析服務的外網域名
本文就從這三點出發,解釋 OpenShift 是如何實現這三種DNS功能的。
1. OpenShift 中的DNS 相關元件及其配置
1.1 Pod 中的 DNS 配置
在Linux 系統上,當一個應用通過域名連線遠端主機時,DNS 解析會通過系統呼叫來進行,比如 getaddrinfo()。
和任何Linux 作業系統一樣,Pod 的 DNS 定義在 resolv.conf 檔案中,其示例如下:
sh-4.2$ cat /etc/resolv.conf nameserver 172.22.122.9 search dev.svc.cluster.local svc.cluster.local cluster.local exampleos.com options ndots:5
其中,
- nameserver 欄位是 pod 所在的宿主機的主網絡卡的IP 地址。也就是說 pod 中發起的所有DNS 查詢請求都會被轉發到執行在宿主機的 53 埠上的DNS伺服器上。
- search 欄位指定當解析一個非FQDN域名時被附加的搜尋域(search domain)列表。其解釋如下:
- options ndots:5
預設地,許多DNS 解析器如果發現被解析的域名中有任何的點(.)就把它當做一個 FQDN 來解析;如果域名中沒有任何點,就把它當做 PQDN 來處理,並且會加上系統的預設domain name 和最後的點,來組成 FQDN。如果沒有指定預設的 domain name (通過 domain 欄位)或查詢失敗,則會將 search 欄位的第一個值當做預設domain name,如果解析不成功,則依次往下試,直到有一個成功或者全部失敗為止。
這個行為是通過 options ndots 來指定的,其預設值為1,這意味著只要被解析域名中有任何一個點(.),那麼它就會被當做 FQDN,而不會附加任何 search domain,直接用來查詢。OpenShift 環境中,這個值被設定為 5。這意味著,只要被解析域名中包含不超過五個點,該域名就會被當做PQDN,然後挨個使用 search domain,來組裝成 FQDN 來做DNS查詢。如果全部不成功過,則會嘗試將它直接作為 FQDN 來解析。
因此,這某些場景中,pod 中的DNS 查詢速度會降低應用的效能。解決方法主要有兩種,要麼直接使用 FQDN,要麼減小 ndots 的值,具體請檢視 Kubernetes 和 DNS 的有關文件。
1.2 Pod 所在宿主機上的 DNS 配置及服務
1.2.1 resolv.conf 檔案
[[email protected] cloud-user]# cat /etc/resolv.conf # nameserver updated by /etc/NetworkManager/dispatcher.d/99-origin-dns.sh # Generated by NetworkManager search cluster.local exampleos.com nameserver 172.22.122.9
在部署環境時,會在每個節點上部署 /etc/NetworkManager/dispatcher.d/99-origin-dns.sh 檔案。每當節點上的 NetworkManager 服務啟動時,該檔案會被執行。它的任務包括:
- 建立 dnsmasq 配置檔案 :
- node-dnsmasq.conf (在我的 3.11 版本環境上沒有建立該檔案,見下文分析)
- origin-dns.conf
- origin-upstream-dns.conf
- 當 NetworkManager 服務啟動時啟動 dnsmasq 服務
- 設定宿主機的所有預設路由 IP 為 Dnsmasq 的偵聽IP
- 修改 /etc/resolv.conf,設定搜尋域,以及將宿主機的預設 IP 作為 nameserver
- 建立 /etc/origin/node/resolv.conf
也就是說,宿主機上的 DNS 請求也會轉到本機上的 53 埠。
1.2.2 dnsmasq 及其配置
宿主機上的 53 埠上,dnsmasq 服務在route 預設路由的所有IP的53埠上偵聽。其中一個負責接受並處理宿主機上所有pod 中以及宿主機上的所有DNS查詢服務。
tcp 0 0 10.128.2.1:53 0.0.0.0:* LISTEN 906/dnsmasq
tcp 0 0 172.17.0.1:53 0.0.0.0:* LISTEN 906/dnsmasq
tcp 0 0 172.22.122.9:53 0.0.0.0:* LISTEN 906/dnsmasq
這些 IP 地址和預設路由IP 地址是符合的:
10.128.0.0 0.0.0.0 255.252.0.0 U 0 0 0 tun0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 172.22.122.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0 172.30.0.0 0.0.0.0 255.255.0.0 U 0 0 0 tun0
dnsmasq 服務的配置目錄為 /etc/dnsmasq.d。其中有兩個配置檔案(具體含義請查閱有關文件):
[[email protected] dnsmasq.d]# cat origin-dns.conf no-resolv domain-needed no-negcache max-cache-ttl=1 enable-dbus dns-forward-max=10000 cache-size=10000 bind-dynamic min-port=1024 except-interface=lo # End of config
檔案 origin-upstream-dns.conf 中定義了上游(upstream) DNS 名字伺服器:
[[email protected] dnsmasq.d]# cat origin-upstream-dns.conf server=172.22.122.3 server=172.22.122.2 server=172.22.122.4
這些上游伺服器的地址是從 DHCP 伺服器中獲取到的(我的OpenShift 環境搭建在OpenStack虛擬機器中。前兩個地址是OpenStack neutron 網路的 DNSmasq 地址,最後一個是單獨搭建的 bind9 DNS 伺服器地址)。
在早期版本中(我的OpenShift版本是 3.11),還有一個配置檔案 node-dnsmasq.conf :
server=/in-addr.arpa/127.0.0.1 server=/cluster.local/127.0.0.1
這意味著所有以 cluster.local 和 in-addr.arpa 結尾的域名,都會被轉到 127.0.0.1:53 上被解析。而其它的解析請求,會被轉到在 origin-upstream-dns.conf 中定義的上游 DNS 伺服器。
我的3.11版本環境中並沒有生成該檔案。從程式碼 https://github.com/openshift/origin/blob/master/pkg/dns/dnsmasq.go 看,OpenShift 中的 dnsmasq 在啟動時會自動新增這兩條記錄:
而 dnsIP 和 dnsDomain 應該是在 /etc/origin/node/node-config.yaml 中的如下配置:
dnsBindAddress: 127.0.0.1:53 dnsDomain: cluster.local從 dnsmasq 日誌中也能看到相關記錄:
Dec 3 14:10:57 dnsmasq[29595]: using nameserver 127.0.0.1#53 for domain in-addr.arpa Dec 3 14:10:57 dnsmasq[29595]: using nameserver 127.0.0.1#53 for domain cluster.local
從上面的分析可見,在 node 節點上的 dnsmasq,其實只是一個DNS 查詢轉發器(轉到上游DNS 伺服器或者本機上的 SkyDns)和結果快取器,它本身並不儲存域名的原始記錄。
1.2.3 SkyDNS 及其配置
關於 SkyDNS:它是一個開源的構建在 etcd 之上的分散式服務宣告(announcement)和發現(discovery)服務。它利用 DNS 查詢來發現可用的服務。社群版本的 SkyDns 將記錄儲存在 etcd 中,但是OpenShift 並不是這麼實現的。從其程式碼 https://github.com/openshift/origin/blob/master/pkg/dns/ 看,SkyDns 應該是呼叫 OpenShift API 服務來獲取主機名、IP地址等資訊,然後組裝成標準 DNS 記錄並返回。
在 127.0.0.1:53 上,包裝在 openshift 程序中的 SkyDNS 在偵聽。tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN 17182/openshift
Node 節點上的 SkyDN 要麼從cache 中直接回答 DNS 查詢,要麼呼叫 OpenShift API 服務來獲取資料。
1.3 Master 節點上的 DNS 服務
resolv.conf 檔案同Node 節點上的:
[[email protected] cloud-user]# cat /etc/resolv.conf # nameserver updated by /etc/NetworkManager/dispatcher.d/99-origin-dns.sh # Generated by NetworkManager search cluster.local haihangyun.cn exampleos.com nameserver 172.22.122.5
dnsmasq 在多個IP 地址的 53 埠上偵聽,為本機上的以及本機上Pod 中的DNS查詢服務:
udp 0 0 10.128.0.1:53 0.0.0.0:* 866/dnsmasq udp 0 0 172.17.0.1:53 0.0.0.0:* 866/dnsmasq udp 0 0 172.22.122.5:53 0.0.0.0:* 866/dnsmasq
和 Node 節點不同,Master 節點上有兩個SkyDns 程序。一個在 127.0.0.1:53 偵聽,負責本機上的叢集內服務的DNS查詢,因為 Master 節點同時承擔 node 節點的角色:
udp 0 0 127.0.0.1:53 0.0.0.0:* 11700/openshift
Dec 3 14:50:41 dnsmasq[10607]: using nameserver 127.0.0.1#53 for domain cluster.local Dec 3 14:50:41 dnsmasq[10607]: using nameserver 127.0.0.1#53 for domain in-addr.arpa
另一個是在所有網絡卡的 8053 埠上偵聽,這是因為Master 還具有 master api 角色:
udp 0 0 0.0.0.0:8053 0.0.0.0:* 15096/openshift
對於這個 SkyDns 程序的作用尚不清楚,還需進一步研究。從已有資料上看看,所有節點上都需要安裝 SkyDns,並組成一個分散式叢集。因為 Master 節點上的 53 埠被另一個 SkyDns 程序佔用,因此換到了埠8053。
2. DNS 查詢流程
2.1 pod 內的應用通過域名訪問外網伺服器的DNS查詢流程
流程示意圖如最上面圖中的 1 和 2.1 部分所示。
dnsmasq 日誌:
Nov 21 11:03:44 dnsmasq[17788]: using nameserver 172.22.122.3#53 Nov 21 11:03:44 dnsmasq[17788]: using nameserver 172.22.122.2#53 Nov 21 11:03:44 dnsmasq[17788]: using nameserver 172.22.122.4#53 Nov 21 11:03:49 dnsmasq[17788]: query[A] www.sina.com from 172.22.122.13 Nov 21 11:03:49 dnsmasq[17788]: forwarded www.sina.com to 172.22.122.4 Nov 21 11:03:49 dnsmasq[17788]: forwarded www.sina.com to 172.22.122.2 Nov 21 11:03:49 dnsmasq[17788]: forwarded www.sina.com to 172.22.122.3 Nov 21 11:03:49 dnsmasq[17788]: reply spool.grid.sinaedge.com is 124.228.42.248
能看到 node 上的 dnsmasq 直接將查詢請求轉發給上游 DNS 名字伺服器。因為存在多個名字伺服器,所以是依次查詢,直到成功為止。從日誌看,其查詢順序和配置檔案中的順序是相反的。
2.2 Pod 內應用通過服務域名查詢其IP 地址
流程示意圖如上圖中的 1 + 2.2 + 3 部分所示。
日誌例項:
(1)從一個 pod 中 ping registry-console服務的域名 registry-console.default.svc.cluster.local。
(2)Node宿主機(IP 地址為 172.22.122.13)上的 dnsmasq 收到該查詢。
(3)dnsmasq 將查詢轉到 127.0.0.1:53 上的 SkyDns 服務。
(4)SkyDNS 做查詢。SkyDNS 能接收的域名格式:<prefix>.<service_name>.<namespace>.(svc|endpoints|pod).<base>,這意味著它支援查詢服務(svc)、端點(endpoints)和 pod 的 DNS資訊。
查詢結果:
[[email protected] cloud-user]# nsenter -t 4216 -n dig mybank.dev.svc.cluster.local ;; QUESTION SECTION: ;mybank.dev.svc.cluster.local. IN A ;; ANSWER SECTION: mybank.dev.svc.cluster.local. 30 IN A 172.30.162.172 ;; Query time: 1 msec ;; SERVER: 172.22.122.9#53(172.22.122.9) ;; WHEN: Mon Dec 03 11:43:01 CST 2018 ;; MSG SIZE rcvd: 62
dnsmasq 日誌:
Dec 3 14:19:44 dnsmasq[29595]: query[A] mybank.dev.svc.cluster.local from 10.128.2.128 Dec 3 14:19:44 dnsmasq[29595]: forwarded mybank.dev.svc.cluster.local to 127.0.0.1 Dec 3 14:19:44 dnsmasq[29595]: reply mybank.dev.svc.cluster.local is 172.30.162.172
(5)其它實驗:查詢服務的所有端點
查詢結果:
[[email protected] cloud-user]# nsenter -t 4216 -n dig jenkins.dev.endpoints.cluster.local ;; QUESTION SECTION: ;jenkins.dev.endpoints.cluster.local. IN A ;; ANSWER SECTION: jenkins.dev.endpoints.cluster.local. 30 IN A 10.128.2.81 jenkins.dev.endpoints.cluster.local. 30 IN A 10.131.1.70
dnsmasq 日誌:
Dec 3 14:20:48 dnsmasq[29595]: query[A] jenkins.dev.endpoints.cluster.local from 10.128.2.128 Dec 3 14:20:48 dnsmasq[29595]: forwarded jenkins.dev.endpoints.cluster.local to 127.0.0.1 Dec 3 14:20:48 dnsmasq[29595]: reply jenkins.dev.endpoints.cluster.local is 10.128.2.81 Dec 3 14:20:48 dnsmasq[29595]: reply jenkins.dev.endpoints.cluster.local is 10.131.1.70
(6)查詢 pod
待查詢的pod域名的格式為 <IP_with_dashes>.<namespace>.pod.<base>,SkyDns 會返回其IP 地址,但我沒明白這麼做的場景和價值,也許是確認pod是否存在?
查詢結果:
[[email protected] cloud-user]# nsenter -t 4216 -n dig 172-30-162-172.dev.pod.cluster.local ;; QUESTION SECTION: ;172-30-162-172.dev.pod.cluster.local. IN A ;; ANSWER SECTION: 172-30-162-172.dev.pod.cluster.local. 30 IN A 172.30.162.172 ;; Query time: 1 msec ;; SERVER: 172.22.122.9#53(172.22.122.9) ;; WHEN: Mon Dec 03 13:32:05 CST 2018 ;; MSG SIZE rcvd: 70
dnsmasq 日誌:
Dec 3 14:22:24 dnsmasq[29595]: query[A] 172-30-162-172.dev.pod.cluster.local from 10.128.2.128 Dec 3 14:22:24 dnsmasq[29595]: forwarded 172-30-162-172.dev.pod.cluster.local to 127.0.0.1 Dec 3 14:22:24 dnsmasq[29595]: reply 172-30-162-172.dev.pod.cluster.local is 172.30.162.172
(7)對比 FQDN 和 PQDN
這個 PQDN 被加上了搜尋域名再進行查詢,能返回正確的IP地址:
[[email protected] cloud-user]# nsenter -t 4216 -n ping mybank.dev.svc PING mybank.dev.svc.cluster.local (172.30.162.172) 56(84) bytes of data.
而這個 FQDN 被直接做DNS查詢,結果查詢失敗,未能獲取IP地址:
[[email protected] cloud-user]# nsenter -t 4216 -n ping mybank.dev.svc. ping: mybank.dev.svc.: Name or service not known
2.3 從外網通過服務域名訪問pod 中執行的服務
可以看出,該過程中只涉及到外部DNS將服務的公共域名解析為 OpenShift Router 所在節點的公網地址,後面 HAProxy 作為代理,直接通過 IP 訪問pod,並將結果返回客戶端。
參考文件:
- https://pracucci.com/kubernetes-dns-resolution-ndots-options-and-why-it-may-affect-application-performances.html
- https://www.redhat.com/en/blog/red-hat-openshift-container-platform-dns-deep-dive-dns-changes-red-hat-openshift-container-platform-36
感謝您的閱讀,歡迎關注我的微信公眾號: