Kubernetes內部域名解析的那些事兒
前言
在kubernets環境中,服務發現大都是基於內部域名的方式。那麼就涉及到內部域名的解析。從1.11版本開始,kubeadm已經使用第三方的CoreDNS替換官方的kubedns作為叢集內部域名的解析元件。
kubernets中的4種DNS策略
None
表示空的DNS設定,這種方式一般用於想要自定義 DNS 配置的場景,往往需要和 dnsConfig 配合一起使用達到自定義 DNS 的目的。
Default
此種方式是讓kubelet來決定使用何種DNS策略。而kubelet預設的方式,就是使用宿主機的/etc/resolv.conf檔案。
同時,kubelet也可以配置指定的DNS策略檔案,使用kubelet引數即可,如:–resolv-conf=/etc/resolv.conf
ClusterFirst
此種方式是使用kubernets叢集內部中的kubedns或coredns服務進行域名解析。若解析不成功,才會使用宿主機的DNS配置來進行解析。
ClusterFistWithHostNet
在某些場景下,我們的 POD 是用 HOST 模式啟動的(HOST模式,是共享宿主機網路的),一旦用 HOST 模式,表示這個 POD 中的所有容器,都要使用宿主機的 /etc/resolv.conf 配置進行DNS查詢,但如果你想使用了 HOST 模式,還繼續使用 Kubernetes 的DNS服務,那就將 dnsPolicy 設定為 ClusterFirstWithHostNet。
策略配置示例
DNS策略,需要在Pod,或者Deployment、RC等資源中,設定 dnsPolicy 即可,以 Pod 為例:
apiVersion: v1 kind: Pod metadata: labels: name: cadvisor-nodexxxx hostip: 192.168.x.x name: cadvisor-nodexxxx namespace: monitoring spec: containers: - args: - --profiling - --housekeeping_interval=10s - --storage_duration=1m0s image: google/cadvisor:latest name: cadvisor-nodexxxx ports: - containerPort: 8080 name: http protocol: TCP resources: {} securityContext: privileged: true terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst nodeName: nodexxxx
kubernets中域名解析流程
# Pod中的resolv.conf的解析配置
[root@l-k8s01 ~]# kubectl exec -it nginx-deploy-5754944d6c-dtzpj cat /etc/resolv.conf
nameserver 10.96.0.2 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5
[root@l-k8s01 ~]# kubectl get svc -n kube-system |grep dns
kube-dns ClusterIP 10.96.0.2 <none> 53/UDP,53/TCP,9153/TCP 158d
a)檔案中配置的 nameserver 一般是k8s叢集內部的dns服務的ClusterIP,無法ping,但是可以訪問。
b)意味著叢集Pod內部的所有域名的解析,都要經過kubedns的虛擬ip 10.96.0.2 進行解析。
c)resolv.conf中search域分別是default.svc.cluster.local svc.cluster.local cluster.local,在kubernets中,域名的全稱必須是 service-name.namespace.svc.cluster.local 。
d)假如叢集中有一個svc(Service)名為a,在某個Pod中執行命令 curl a 時,在此Pod中會根據/etc/resolv.conf進行解析流程。選擇nameserver 10.96.0.2進行解析,將字串'a'帶入到/etc/resolv.conf檔案中不同的search域,依次進行查詢,如下:
a.default.svc.cluster.local -> a.svc.cluster.local -> a.cluster.local
先查詢 a.default.svc.cluster.local ,若找不到,則再查詢 a.svc.cluster.local ,依次往下進行,直到找到為止。
curl效率分析
在叢集中若存在一個名為a的svc,在Pod中curl a和curl a.default都能實現請求,那麼兩種方式哪個的效率高呢?
那肯定是curl a啦,因為發起此請求時,通過/etc/resolv.conf中第一列的search域就能直接找到 a.default.svc.cluster.local ,直接避免了下一級的查詢。
容器中訪問外部域名講述
下文將通過示例說明Pod訪問外部域名時發起的相應的請求資訊。
以請求baidu.com為例,因為DNS容器一般不具備bash,所以無法通過docker exec的方式進入容器抓包,所以此處採用 進入到DNS容器的網路中(不是發起DNS請求的容器)的姿勢去抓包,抓包姿勢準備好後,同時在某容器中訪問baidu.com,即可看到在進行的DNS查詢的過程中都產生了什麼樣的資料包。
### 實操
# 進入dns容器網路,準備好抓包姿勢
# 檢視Pod所在具體的node節點
[root@master1 ~]# kubectl get pods -n kube-system -o wide|grep dns
coredns-5c48579f88-8wprg 1/1 Running 16 30d 10.244.4.120 node1 coredns-5c48579f88-rsnpr 1/1 Running 0 30d 10.244.5.142 node2
# 這裡以node1上的容器為操作物件,所以到node1節點上進行操作
# 找到容器並列印對應的NS ID
[root@node1 ~]# docker ps |grep dns
a964bbb43534 c0f6e815079e "/coredns -conf /etc…" 2 days ago Up 2 days k8s_coredns_coredns-5c48579f88-8wprg_kube-system_b1e7f3c3-98eb-4843-b156-1d203f98bd74_16 fbd12d2f9c7c k8s.gcr.io/pause:3.1 "/pause" 5 days ago Up 5 days k8s_POD_coredns-5c48579f88-8wprg_kube-system_b1e7f3c3-98eb-4843-b156-1d203f98bd74_3
[root@node1 ~]# docker inspect --format "{{.State.Pid}}" a964bbb43534
21617
# 進入此容器的網路Namespace
[root@node1 ~]# nsenter -n -t 21617
# 抓包姿勢就緒
[root@node1 ~]# tcpdump -i eth0 udp dst port 53|grep 'baidu.com'
# 在另外的某容器中,進行域名查詢操作
說明:一般pod中沒有nslookup命令,故需要手動安裝,根據不同環境自選以下操作。
### Centos
]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
]# yum -y install bind-utils
### Debian
# cat /etc/issue
Debian GNU/Linux 9
# apt-get install dnsutils -y
root@jenkins-7d66bf7977-cm4x4:~# nslookup baidu.com 10.244.4.120
注意:10.244.4.120是node1上的dns pod在kubernets叢集中的內部通訊ip地址。因為環境中有兩個dns pod,將其指定要單個具體的容器,能夠使抓包資料完整。
# 隨後,在前面就緒的抓包姿勢視窗就能看到資料包的出現
[root@node1 ~]# tcpdump -i eth0 udp dst port 53|grep 'baidu.com'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 16:57:50.791154 IP 10.244.4.127.51794 > node1.domain: 55406+ A? baidu.com.infra.svc.cluster.local. (51) 16:57:50.792540 IP 10.244.4.127.56306 > node1.domain: 27958+ A? baidu.com.svc.cluster.local. (45) 16:57:50.793439 IP 10.244.4.127.59799 > node1.domain: 27048+ A? baidu.com.cluster.local. (41) 16:57:50.799463 IP 10.244.4.127.39116 > node1.domain: 2303+ A? baidu.com. (27)
說明:
a)資料包中顯示的 infra 是執行nslookup的pod的NameSpace;
b)根據資料顯示,在真正解析到 baidu.com 之前,經歷了baidu.com.infra.svc.cluster.local. > baidu.com.svc.cluster.local. > baidu.com.cluster.local. 三次DNS請求。
請求浪費的原因
上文在正確請求到baidu.com之前,有過三次無效請求,即意味著請求浪費,那為什麼會出現那種情況呢,請繼續往下看。
# Pod中的resolv.conf的解析配置
root@jenkins-7d66bf7977-cm4x4:/# cat /etc/resolv.conf nameserver 10.96.0.2 search infra.svc.cluster.local svc.cluster.local cluster.local host.com options ndots:5
# options ndots:5 解釋
如果查詢的域名包含的點".",不到5個,那麼進行DNS查詢,將使用非完全限定名稱(或者叫絕對域名),如果你查詢的域名包含點數大於等於5,那麼DNS查詢,預設會使用絕對域名進行查詢。
如果我們請求的域名是,a.b.c.d.e,這個域名中有4個點,那麼容器中進行DNS請求時,會使用非絕對域名進行查詢,使用非絕對域名,會按照 /etc/resolv.conf 中的 search 域,走一遍追加匹配:
a.b.c.d.e.cicd.svc.cluster.local. ->
a.b.c.d.e.svc.cluster.local. ->
a.b.c.d.e.cluster.local.
直到找到為止。如果走完了search域還找不到,則使用 a.b.c.d.e. ,作為絕對域名進行DNS查詢。
說明:
a)請求域名中點數少於5個時,先走search域,最後將其視為絕對域名進行查詢;
b)請求域名中點數大於等於5個時,直接視為絕對域名進行查詢,只有當查詢不到的時候,才繼續走 search 域。
優化請求浪費
使用全限定域名
當訪問某域名時,以 '.' 為字尾,即使用 完全限定域名(絕對域名),這樣發起的域名請求時將不會走search域進行匹配,而是直接使用整個原始域名字串為個體進行解析。
如:
nslookup baidu.com.
配置特定ndots
在kubernets中,ndots值預設是5。是因為,Kubernetes 認為,內部域名,最長為5,要保證內部域名的請求,優先走叢集內部的DNS,而不是將內部域名的DNS解析請求,有打到外網的機會,Kubernetes 設定 ndots 為5是一個比較合理的行為。
如果有特定業務需求,也可配置ndots,如下:
apiVersion: v1 kind: Pod metadata: namespace: default name: dns-example spec: containers: - name: test image: nginx dnsConfig: options: - name: ndots value: "1"