理解OpenShift(1):網絡之 Router 和 Route
理解OpenShift(5):從 Docker Volume 到 OpenShift Persistent Volume
** 本文基於 OpenShift 3.11,Kubernetes 1.11 進行測試 ***
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 來解析。
1.2 Pod 所在宿主機上的 DNS 配置及服務
1.2.1 resolv.conf 文件
[root@node2 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。其中有兩個配置文件(具體含義請查閱有關文檔):
[root@node2 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 名字服務器:
[root@node2 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
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 查詢來發現可用的服務。其開源社區的地址是 https://github.com/skynetservices/skydns。社區版本的 SkyDns 將記錄保存在 etcd 中,在做查詢時從etcd 獲取數據並封裝成 DNS 結果格式給客戶端。
SkyDNS 的 server 部分支持被作為庫文件使用,此時可以為其實現其它后端。在OpenShift 中並沒有采用默認的 etcd 后端,而是基於 OpenShift API 服務實現了新的后端,其代碼在https://github.com/openshift/origin/blob/master/pkg/dns/ 。SkyDns 調用 OpenShift API 服務來獲取主機名、IP地址等信息,然后封裝成標准 DNS 記錄並返回給查詢客戶端。
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 節點上的:
[root@master1 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信息。
查詢結果:
[root@node2 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)其它實驗:查詢服務的所有端點
查詢結果:
[root@node2 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是否存在?
查詢結果:
[root@node2 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地址:
[root@node2 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地址:
[root@node2 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
感謝您的閱讀,歡迎關注我的微信公眾號: