1. 程式人生 > >Kubernetes DNS 簡介

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

的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有了hostnamesubdomain欄位,用於指定Pod的hostname和subdomain。它的優先順序則高於上面提到的pod.beta.kubernetes.io/hostnamepod.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/hostnamepod.beta.kubernetes.io/subdomainendpoints.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可能不能正常的工作,這是已知的問題。可以檢視這裡獲取更多資訊。

References