CoreDNS簡單除錯:解決你遇到的一般問題
最近在部署一些解決方案時,碰到了關於名稱解析上的一些問題,雖然有時候不難解決,但我發現如果清楚K8S有關於名稱解析的概念,會對除錯的時間與過程有很大的幫助。
透過本篇文章,除了幫自已留下問題查找的過程,同時也希望利用文章來整理自已對CoreDNS元件的一些觀念,同時也可以分享給大家參考。
以下是本文所將提到的章節:
- 基本架構與如何運作
- Kube-DNS & CoreDNS
- 問題的處理過程與方式
- 結論
本文為心得記錄,內容比較長,感謝大家願意花時間觀看。
1. 基本架構與如何運作
首先不免俗的還是來一個官方的說法:
CoreDNS也是DNS server的一種,用Go語言開發。
與其他DNS(ex. BIND…)不同,它非常彈性、並且幾乎所有的功能都全部都被放成Plugin的型式。
這些Plugin可以各別運行或是全部放在一起運行來實現DNS的功能。
透過官方的說法,我們可以利用CoreDNS的特性來選擇與組合這些Plugin(CoreDNS Plugin API),變成自訂版本的DNS解決方式。預設的CoreDNS安裝完就會帶有約30個Plugin一起安裝進系統。其他額外的Plugin可以到以下網址去找到自已需要的:
簡短說完CoreDNS的來歷,接下來說明kubernetes中,如何解析domain name的。
在K8S內,如果Pod在訪問在同一個Namespace下的Service,只要執行:
# curl aa-svc
可是如果訪問的對象在不同的namespace呢? 此時就要再加上domain的部分,如下:
# curl aa-svc.domain
由此可知,只要離開自已的namespace範圍時,就會需要做名稱解析了。
不管是不是在K8S cluster內,基本上DNS解析基本上會使用到:
- /etc/host.conf
- /etc/hosts
- /etc/resolv.conf
在訪問時,Pod會先查找/etc/resolv.conf的內容,裡面就是指定DNS server的位置,而內容則是由設定好dnspolicy: ClusterFirst的情況下,自動生成出來。裡面nameserver的IP則是DNS service的cluster IP。所以這個Pod內所有的解析都會與這個cluster IP傳送(不管是不是同一個namespace)。
預設的search domain(搜尋時會依序進行查找):
- namespace.svc.cluster.local
- svc.cluster.local
- cluster.local
最後簡單說明K8S的DNS Policies,一共有以下四種:
- ClusterFirstWithHostNet => 如果Pod使用hostnetwork: true時,會直接套用Node上的resolv.conf內容,如果要用Pod內自已的內容,就要用這個策略。
- ClusterFirst => Pod內的DNS優先使用K8S內的DNS服務(此處指CoreDNS)
- Default => 直接讓kubelet來決定用那種,預設是用Node內的resolv.conf的內容。
- None => 都不指定,直接使用dnsconfig的內容來自定義。
2. Kube-DNS & CoreDNS
本章節簡單說明二者的不同。
(1) Kube-DNS: 同樣提供DNS名稱解析的功能,但K8S 1.21版之後,kubeadm移除了對Kube-DNS的支援,只支援CoreDNS。而以下圖為Kube-DNS的基本架構圖:
以下簡單說明主要的三個元件:
- kubeDNS : 用來監聽K8S內的service與endpoint的變化
- dnsmesq : 區分domain是內部還是外部,內部的話發往10053 Port做Cache
- sidecar : 對kubedns與dnsmesq進行health check與監控指標收集
(2) CoreDNS: 請參閱上述內容,就不重覆內容。以下為CoreDNS基本運作流程圖:
(3) 優缺點:
※Kube-DNS
- 優點:有dnsmesq,效能上有一定確保
- 缺點:dnsmesq如果重啟,會先殺掉process才重新帶起服務,中間可能會出現查詢失敗。確認檢查內部檔案時,如果數量過多或太頻繁更新,有可能反而會導致dnsmesq必須要重新啟動。
※ CoreDNS
- 優點:可以根據需求使用自訂的Plugin。1.21版後預設支援的DNS。記憶體佔用的情況比Kube-DNS好。
- 缺點:Cache的效率沒有dnsmesq好,內部解析沒有kube-dns快。
3. 問題的處理過程與方式
#---------------------------------------------
# S3-1. 部署dnsutils
#---------------------------------------------
[master]# vim dnsutils.yaml
apiVersion: v1
kind: Pod
metadata:
name: dnsutils
namespace: default
spec:
containers:
- name: dnsutils
image: registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3
command:
- sleep
- "infinity"
securityContext:
capabilities:
add:
- NET_RAW
imagePullPolicy: IfNotPresent
restartPolicy: Always
[master]# kubectl create -f dnsutils.yaml -n default
[master]# kubectl get pod
#----------------------------------------------------
# S3-2. Ping
#----------------------------------------------------
[master]# kubectl exec -it dnsutils /bin/sh
/# ping kubernetes.default
/# ping default.svc.cluster.local
#----------------------------------------------------
# S3-3. 確認是否有將namespace的svc內容打入環境變數
#----------------------------------------------------
[master]# kubectl exec -it dnsutils -n default -- env | grep KUBERNETES
#----------------------------------------------------
# S3-4. nslookup
#----------------------------------------------------
/# nslookup kubernetes.default
/# nslookup default.svc.cluster.local
;; connection timed out; no servers could be reached
#----------------------------------------------------
# S3-5. 確認coredns pod status
#----------------------------------------------------
[master]# kubectl get pods -l k8s-app=kube-dns -n kube-system
=> 均為Running狀態
#----------------------------------------------------
# S3-6. 確認coredns pod logs
#----------------------------------------------------
[master]# for p in $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name); do kubectl logs --namespace=kube-system $p; done
=> 沒有錯誤訊息
=> 以下會出現較多訊息的原因是在"kubectl edit configmap coredns -n kube-system" 加入log參數
#----------------------------------------------------
# S3-7. 確認endpoint and pod binding
#----------------------------------------------------
[master]# kubectl get pods -l k8s-app=kube-dns -n kube-system -o wide
[master]# kubectl get endpoints kube-dns -n kube-system
#----------------------------------------------------
# S3-8. 確認是否為"Pod本身解析" or "Pod ok,但請求送到kube-dns無法轉發"
# 直接修改/etc/resolv.conf的nameserver換成別的
#----------------------------------------------------
Pod /etc/resolv.conf => 指向10.86.0.10 (Service “kube-dns” 的 cluterIP)
[lb01]# kubectl exec -it dnsutils /bin/sh
/# vi /etc/resolv.conf
nameserver 8.8.8.8
/# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=56 time=2.774 ms
64 bytes from 8.8.8.8: seq=1 ttl=56 time=2.571 ms
64 bytes from 8.8.8.8: seq=2 ttl=56 time=2.625 ms
64 bytes from 8.8.8.8: seq=3 ttl=56 time=2.512 ms
/# ping www.google.com
ping: bad address 'www.google.com'
#----------------------------------------------------
# S3-9. 確認kube-proxy是否有問題
#----------------------------------------------------
[master]# kubectl logs kube-proxy-6kdj2 --tail=5 -n kube-system
=> 確認是否有error
根據以下流程圖,可以發現Pod會先往CoreDNS(10.96.0.10)查詢,然後再轉送到外部進行解析。
#--------------------------------------------
# S3-10. 確認Pod是否可以正確轉送到外部解析
#--------------------------------------------
[master]# kubectl exec -it nginx-quic-deployment-c5f8b8b44-8hwm9 -- bash
/# yum update
最後請注意,在Pod內能Ping的IP不能是Service的Cluster IP,它是Virtual IP,如果要Ping其他服務IP時,請照以下方式:
[master]# kubectl describe pod <pod_name> => find IP
[master]# kubectl exec -it nginx-quic-deployment-c5f8b8b44-8hwm9 -- bash
/# ping 192.168.35.9
4. 結論
終於完成關於DNS解析的文章,當初看了非常多文件來了解到底Kubernetes內部名稱解析在做些什麼事,從以上文章可以了解到許多服務之間的溝通真的非常依賴CoreDNS,所以了解CoreDNS的運作方式,對於想要管理K8S cluster的管理者是至關重要的。
最後如果現在是使用Kube-dns方案的Cluster,官方在升級K8S時提供一個轉換成CoreDNS的支援,參考網址如下: