環境
$ 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
的Service,位於命名空間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可能不能正常的工作,這是已知的問題。可以查看這里獲取更多信息。