Kubernetes DNS 簡介
環境
$ sudo lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.2 LTS
Release: 16.04
Codename: xenial
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.4", GitCommit:"7243c69eb523aa4377bce883e7c0dd76b84709a1", GitTreeState:"clean" , BuildDate:"2017-03-07T23:53:09Z", GoVersion:"go1.7.4", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.4", GitCommit:"7243c69eb523aa4377bce883e7c0dd76b84709a1", GitTreeState:"clean", BuildDate:"2017-03-07T23:34:32Z", GoVersion:"go1.7.4", Compiler:"gc" , Platform:"linux/amd64"}
介紹
從Kubernetes 1.3開始,DNS通過使用外掛管理系統cluster add-on
,成為了一個內建的自啟動服務。
Kubernetes DNS在Kubernetes叢集上排程了一個DNS Pod和Service,並配置kubelet,使其告訴每個容器使用DNS Service的Ip來解析DNS名稱。
什麼是DNS名稱
叢集中定義的每個Service(包括DNS Service它自己)都被分配了一個DNS名稱。預設的,Pod的DNS搜尋列表中會包含Pod自己的名稱空間和叢集的預設域,下面我們用示例來解釋以下。
假設有一個名為foo
bar
中。執行在bar
名稱空間中的Pod可以通過DNS查詢foo
關鍵字來查詢到這個服務,而執行在名稱空間quux
中的Pod可以通過關鍵字foo.bar
來查詢到這個服務。
支援的DNS模式
下面的章節詳細的描述了支援的記錄(record)型別和layout。
Services
普通(非headless)的Service都被分配了一個DNS記錄,該記錄的名稱格式為my-svc.my-namespace.svc.cluster.local
,通過該記錄可以解析出服務的叢集IP。
Headless(沒有叢集IP)的Service也被分配了一個DNS記錄,名稱格式為my-svc.my-namespace.svc.cluster.local
。與普通Service不同的是,它會解析出Service選擇的Pod的IP列表。
SRV records
SRV records用於為命名埠服務,這些埠是headless或者普通Service的一部分。對於每個命名埠,SRV record的格式為:_my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.local
。對於普通服務來說,這會解析出埠號和CNAMEmy-svc.my-namespace.svc.cluster.local
。對於headless服務來說,這會解析出多個結果,一個是service後端的每個pod,一個是包含埠號,和格式為auto-generated-name.my-svc.my-namespace.svc.cluster.local
的pod的CNAME。
向後相容性
kube-dns的之前版本,使用了格式為my-svc.my-namespace.cluster.local
(svc這一層是後面加上的)的名稱。但這種格式不再被支援了。
Pods
pod會被分配一個DNS記錄,名稱格式為pod-ip-address.my-namespace.pod.cluster.local
。
比如,一個pod,它的IP地址為1.2.3.4
,名稱空間為default
,DNS名稱為cluster.local,那麼它的記錄就是:1-2-3-4.default.pod.cluster.local
。
當pod被建立時,它的hostname設定在Pod的metadata.name
中(寫yaml的時候應該很清楚這點)。
在v1.2版本中,使用者可以指定一個Pod註解,pod.beta.kubernetes.io/hostname
,用於指定Pod的hostname。這個Pod註解,一旦被指定,就將優先於Pod的名稱,成為pod的hostname。比如,一個Pod,其註解為pod.beta.kubernetes.io/hostname: my-pod-name
,那麼該Pod的hostname會被設定為my-pod-name。
v1.2中還引入了一個beta特性,使用者指定Pod註解,pod.beta.kubernetes.io/subdomain
,來指定Pod的subdomain。比如,一個Pod,其hostname註解設定為“foo”
,subdomain註解為“bar”
,名稱空間為“my-namespace”
,那麼它最終的FQDN就是“foo.bar.my-namespace.svc.cluster.local”
。
在v1.3版本中,PodSpec有了hostname
和subdomain
欄位,用於指定Pod的hostname和subdomain。它的優先順序則高於上面提到的pod.beta.kubernetes.io/hostname
和pod.beta.kubernetes.io/subdomain
。
示例:
apiVersion: v1
kind: Service
metadata:
name: default-subdomain
spec:
selector:
name: busybox
clusterIP: None
ports:
- name: foo # Actually, no port is needed.
port: 1234
targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
name: busybox1
labels:
name: busybox
spec:
hostname: busybox-1
subdomain: default-subdomain
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox
---
apiVersion: v1
kind: Pod
metadata:
name: busybox2
labels:
name: busybox
spec:
hostname: busybox-2
subdomain: default-subdomain
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox
如果一個headless service中,多個pod都在同一個名稱空間裡,並且subdomain名稱也相同,叢集的KubeDNS還是會為每個Pod返回完整而合格的hostname。給定一個Pod,其hostname設定為busybox-1
,subdomain設定為default-subdomain
,同一個名稱空間中的headless Service名為default-subdomain
,那麼pod自己的FQDN就是“busybox-1.default-subdomain.my-namespace.svc.cluster.local”
。
在Kubernetes v1.2裡,Endpoint物件還使用了註解endpoints.beta.kubernetes.io/hostnames-map
。它的值就是json格式中的map[string(IP)][endpoints.HostRecord]
, 比如 ‘{“10.245.1.6”:{HostName: “my-webserver”}}’。如果Endpoint是用於headless service的,就會為其建立一個格式為…svc的記錄。以json格式為例,如果Endpoint用於名為“bar”的headless service,其中一個Endpoint的ip為“10.245.1.6”,就會建立一個名為“my-webserver.bar.my-namespace.svc.cluster.local”
的記錄,查詢該記錄就會得到“10.245.1.6”。這個Endpoint註解一般不需要終端使用者來指定,但可以被內部服務控制器使用,來實現上面的特性。
在v1.3中,Endpoint物件可以為任何一個Endpoint指定hostname和IP。hostname欄位會覆蓋endpoints.beta.kubernetes.io/hostnames-map
註解的值。
在v1.3中,以下註解被棄用了:pod.beta.kubernetes.io/hostname
,pod.beta.kubernetes.io/subdomain
, endpoints.beta.kubernetes.io/hostnames-map
。
如何測試DNS是否工作
建立一個簡單地Pod,使用測試環境
建立一個名為busybox.yaml檔案,使用下面的內容:
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
name: busybox
restartPolicy: Always
使用該檔案建立pod:
kubectl create -f busybox.yaml
等待pod進入running狀態
獲取pod狀態:
$ kubectl get pods busybox
你會看到:
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 7m
確認DNS是否工作
一旦pod處於running
狀態時,可以使用exec nslookup
來查詢狀態:
$ kubectl exec -ti busybox -- nslookup kubernetes.default
你應該看到類似的結果:
Server: 10.0.0.10
Address 1: 10.0.0.10
Name: kubernetes.default
Address 1: 10.0.0.1
如果出現上述結果,則說明DNS正常工作。
故障排查
如果nslookup
失敗,檢查以下選項:
檢查本地DNS配置
檢查pod的resolv.conf
檔案。
$ kubectl exec busybox cat /etc/resolv.conf
確認搜尋路徑和name sever被設定成類似下面的樣子(注意搜尋路徑可能因雲提供商不同而有所差異):
search default.svc.cluster.local svc.cluster.local cluster.local google.internal c.gce_project_id.internal
nameserver 10.0.0.10
options ndots:5
快速診斷
如下的錯誤表明kube-dns add-on或者相關服務有問題:
$ kubectl exec -ti busybox -- nslookup kubernetes.default
Server: 10.0.0.10
Address 1: 10.0.0.10
nslookup: can't resolve 'kubernetes.default'
或者
$ kubectl exec -ti busybox -- nslookup kubernetes.default
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
nslookup: can't resolve 'kubernetes.default'
檢查DNS pod是否執行
使用kubectl get pods
命令來確認DNS pod是否正在執行。
$ kubectl get pods --namespace=kube-system -l k8s-app=kube-dns
應該會有如下的結果:
NAME READY STATUS RESTARTS AGE
...
kube-dns-v19-ezo1y 3/3 Running 0 1h
...
如果沒有相關的pod執行,或者pod狀態為failed/completed,那麼就說明你的環境下,沒有預設部署DNS add-on,你需要手動部署它。
檢查DNS pod中的錯誤
使用kubectl log
命令來檢視DNS守護程式的日誌。
$ kubectl logs --namespace=kube-system $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name) -c kubedns
$ kubectl logs --namespace=kube-system $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name) -c dnsmasq
$ kubectl logs --namespace=kube-system $(kubectl get pods --namespace=kube-system -l k8s-app=kube-dns -o name) -c healthz
如果有任何可疑的日誌,每一行開頭的W,E,F字母分別表示警告、錯誤和故障。請搜尋這些錯誤日誌的條目,或者通過kubernetes issues頁面來報錯非預期的錯誤。
DNS服務是否啟動
使用kubectl get service
命令來檢視DNS服務是否已經啟動。
$ kubectl get svc --namespace=kube-system
你會看到:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
kube-dns 10.0.0.10 <none> 53/UDP,53/TCP 1h
...
該服務會預設地被建立,或者如果你手動建立了該服務,但是該服務卻並沒有在上述命令中出現,請檢視 debugging services page頁面獲取更多資訊。
是否暴露了DNS Endpoint?
可通過kubectl get endpoints
命令來確認是否暴露了DNS Endpoint。
$ kubectl get ep kube-dns --namespace=kube-system
你應該會看到下面的結果:
NAME ENDPOINTS AGE
kube-dns 10.180.3.17:53,10.180.3.17:53 1h
如果沒有看到Endpoint,那麼請檢視debugging services page頁面。
若要檢視更多的Kubernetes DNS示例,請在Kubernetes Github倉庫中檢視cluster-dns examples。
如何工作
執行的Kubernetes DNS pod包含3個容器——kubedns、dnsmasq和一個叫做healthz的健康檢查容器。kubedns程序監視Kubernetes master上Service和Endpoint的改變,並在記憶體中維護lookup 結構用於服務DNS請求。dnsmasq容器增加DNS快取,從而提升效能。healthz容器提供一個單點的健康檢查Endpoint,檢查dnsmasq和kubedns的健康程度。
DNS pod以服務的形式暴露出來,它擁有一個靜態IP。一旦被建立,kubelet就使用--cluster-dns=10.0.0.10
標識,將DNS配置資訊傳遞給每個容器。
DNS名稱也需要域。本地域是可以配置的,在kubelet中,使用--cluster-domain=<default local domain>
引數。
Kubernetes叢集的DNS服務(基於SkyDNS庫)支援forward lookup(A recoreds),service lookup(SRV records)和反向IP地址查詢(PTR recoreds)。
從node繼承DNS
當執行pod時,kubelet會預先考慮叢集的DNS服務,並在node本地的DNS設定中搜索路徑。如果node能夠解析DNS名稱,那麼pod也可以做到。
如果你希望在pod中使用不同的DNS,那麼你可以使用kubelet的--resolv-conf
引數。該設定意味著pod不會從node繼承DNS。設定該值為其他的檔案路徑,意味著會使用該檔案來配置DNS,而不是/etc/resolv.conf
。
已知的問題
Kubernetes安裝預設並不會使用叢集的DNS配置來設定Kubernetes node的resolv.conf
檔案,因為該程序依賴於發行版的配置。
Linux的libc有著3個DNS nameserver
和6個DNS搜尋記錄的限制,Kubernetes需要消耗一個nameserver和3個搜尋記錄。這意味著如果一個本地配置已經使用了3個nameserver或者使用了3個以上的搜尋記錄,那麼這些配置可能會丟失。有一個臨時方案,node可以執行dnsmasq
,它可以提供更多的nameserver選項,但不能提供更多的搜尋選項。你也可以使用kubelet的--resolv-conf
選項。
如果你使用的是Alpine 3.3或更早的版本,DNS可能不能正常的工作,這是已知的問題。可以檢視這裡獲取更多資訊。