搭建Kubernetes叢集時DNS無法解析問題的處理過程

simpleapples發表於2019-03-01

問題描述

在搭建Kubernetes叢集過程中,安裝了kube-dns外掛後,執行一個ubuntu容器,發現容器內無法解析叢集外域名,一開始可以解析叢集內域名,一段時間後也無法解析叢集內域名。

$ nslookup kubernetes.default
Server:    10.99.0.2
Address 1: 10.99.0.2 kube-dns.kube-system.svc.cluster.local

nslookup: can`t resolve `kubernetes.default`
複製程式碼

排查過程

在排查問題前,先思考一下Kubernetes叢集中的DNS解析過程,在安裝好kube-dns的叢集中,普通Pod的dnsPolicy屬性是預設值ClusterFirst,也就是會指向叢集內部的DNS伺服器,kube-dns負責解析叢集內部的域名,kube-dns Pod的dnsPolicy值是Default,意思是從所在Node繼承DNS伺服器,對於無法解析的外部域名,kube-dns會繼續向叢集外部的dns進行查詢,過程如圖。

搭建Kubernetes叢集時DNS無法解析問題的處理過程

Ubuntu容器是一個普通的Pod,在Linux系統中,/etc/resolv.conf是儲存DNS伺服器的檔案,普通Pod的/etc/resolv.conf檔案應該儲存的是kube-dns的Service IP。

nameserver 10.99.0.2  # 這裡儲存的是kube-dns的Service IP
search default.svc.cluster.local. svc.cluster.local. cluster.local.
options ndots:5
複製程式碼

檢視後發現/etc/resolv.conf檔案中儲存的是kube-dns的Service IP,證明這一步沒有問題,接下來檢視一下kube-dns的Pod,先進入kube-dns的Pod中檢查一下/etc/resolv.conf檔案,這裡儲存的應該是叢集外部的DNS伺服器地址,檢視後發現,這裡儲存的地址是127.0.0.53,進一步檢視kube-dns Pod的log,發現出現了非常多的i/o timeout錯誤。

 2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:38019->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:57567->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:52599->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:42539->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:46885->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:44189->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:56505->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:47320->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:42464->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:49203->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:58103->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:47148->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:36883->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:40968->127.0.0.53:53: i/o timeout
2018/07/11 07:12:47 [ERROR] 2 [www.baidu.com](http://www.baidu.com/). A: unreachable backend: read udp 127.0.0.1:55672->127.0.0.53:53: i/o timeout
複製程式碼

現在基本上可以發現問題的原因了,kube-dns只能解析叢集內部地址,而叢集外部地址應該發給外部DNS伺服器進行解析,由於kube-dns Pod中的/etc/resolv.conf檔案儲存的DNS伺服器地址是127.0.0.53,127...*都是迴環地址,也就是叢集外域名的DNS解析請求會再次傳送回kube-dns,導致形成一個迴圈,這也是一秒鐘會出現幾十次i/o timeout日誌的原因,請求會不斷的在kube-dns中迴圈,kube-dns就像一個黑洞一樣,吃掉了所有dns解析請求,不斷累積的請求最終會導致整個叢集的網路出現卡頓。

搭建Kubernetes叢集時DNS無法解析問題的處理過程

為什麼

雖然問題的原因找到了,但是為什麼kube-dns Pod中/etc/resolv.conf檔案儲存的DNS伺服器是127.0.0.53?

kube-dns Pod的dnsPolicy值是Default,檢視一下Kubernetes文件。

Default“: The Pod inherits the name resolution configuration from the node that the pods run on. See related discussion for more details.

所以kube-dns的/etc/resolv.conf檔案是從Node中繼承來的,檢視Node中的/etc/resolv.conf檔案,儲存的DNS伺服器地址確實是127.0.0.53,那麼下一個問題出現了,在Node中傳送DNS解析請求為什麼不會產生迴環的問題呢?

Node使用的是Ubuntu 18.04 Server,在這個版本的系統中,DNS解析請求並不是直接發給所在網路的DNS伺服器的,Ubuntu 18.04中有一個systemd-resolved服務,為本地應用程式提供了DNS解析服務,例如nslookup localhost,解析程式從/etc/resolv.conf檔案中找到DNS伺服器127.0.0.53,傳送解析請求,systemd-resolved會監聽在53埠上,捕獲到解析請求後,如果是自己可以解析的,例如localhost,會直接返回127.0.0.1,如果不能解析,才會傳送給外部伺服器,而外部伺服器的地址儲存在/run/systemd/resolve/resolv.conf檔案中,這個檔案是systemd-resolved伺服器的配置檔案,過程如圖。

搭建Kubernetes叢集時DNS無法解析問題的處理過程

怎麼破

理解了問題的來龍去脈,解決問題的辦法也就應運而生。在Kubernetes叢集中,kubelet是worker組建,負責管理Pod,根據kubernetes文件,kubelet預設會從Node的/etc/resolv.conf檔案讀取DNS伺服器地址,使得dnsPolicy是Default的Pod得以繼承,kubelet中的–resolv-conf引數可以指定這個配置檔案的地址。在Ubuntu 18.04中,將這個引數設定為systemd-resolved的DNS伺服器配置檔案/run/systemd/resolve/resolv.conf,Pod就會繼承真正的外部DNS伺服器。

總結

通過對問題的探究,也理解了Kubernetes叢集中DNS解析的完整過程,如圖。

搭建Kubernetes叢集時DNS無法解析問題的處理過程

* 在Ubuntu 16.04中也是類似的邏輯,只不過systemd-resolved換成了dnsmasq,監聽地址是127.0.1.1
* 在具體實踐過程中,也順便探究了CoreDNS和KubeDNS架構和解析邏輯上的區別,不過不在此問題的討論範圍,有興趣的朋友可以自己看一下。
* 如果Kubernetes叢集是安裝在NAT網路下的虛擬機器上,虛擬機器(也就是Kubernetes叢集中的Node)中/etc/resolv.conf檔案可能被修改為NAT的地址,也就不會出現上面這個問題。

參考內容

kubernetes.io/docs/refere…

kubernetes.io/docs/concep…

kubernetes.io/docs/tasks/…

www.freedesktop.org/software/sy…

github.com/kubernetes/…

github.com/kubernetes/…

相關文章