1. 程式人生 > >Kubernetes服務發現和kube-dns外掛

Kubernetes服務發現和kube-dns外掛

大綱

kube-dns的主要變化

kube-dns的實現原理

kubedns容器詳解

dnsmasq容器簡介

exechealthz容器簡介

主要變化

服務發現機制未變化,也就是說kube-dns對外的介面是基本沒變的。變化主要在於kube-dns外掛的內部組成,由原來的四個容器變為了三個。

現在回顧一下上一期所講的1.2版本kube-dns的組成。

Kube2sky通過K8S API監視K8S Service資源的變化,並根據Service的資訊生成DNS記錄寫入到etcd中。Skydns為叢集中的Pod提供DNS查詢服務,DNS記錄從etcd中讀取。Exechealthz提供健康檢查功能。
Kubernetes服務發現和kube-dns外掛Kubernetes服務發現和kube-dns外掛

接下來我們再看一下1.4版本kube-dns的組成。對比兩張圖,可以很直觀的看到kube-dns對外介面沒有發生變化。Exechealthz是唯一保留的容器,依然提供健康檢查。

不同點

1.會檢查兩個容器的健康狀態。

2.為叢集提供DNS查詢服務的容器由skydns變為了dnsmasq。

3.Kubedns容器替代了kube2sky來監視Service資源。

4.Etcd容器不見了。

Kubernetes服務發現和kube-dns外掛Kubernetes服務發現和kube-dns外掛

相信有些人會好奇DNS記錄現在儲存在哪了呢?那為了回答這個問題以及瞭解新版kube-dns的工作原理,我們就進入下一章內容來了解其實現原理。

實現原理

kubedns容器的實現

本著“Talk is cheap, show me the code”原則,我們將會以原始碼分析的方式介紹其原理。對另外兩個容器會進行簡要介紹。

先來看一下原始碼位置,這裡列出的是kube-dns外掛相關的原始碼,不僅僅是kubedns容器的。之前的原始碼是集中在cluster/addons/dns下面的,那麼1.4版本中分成了三個目錄:

第一個目錄會有K8S DNS相關的README以及kubedns容器的Dockerfile。

第二個目錄存放kube-dns外掛的編排檔案。

第三個是kubedns原始碼目錄,kubedns容器使用的命令行就是從這構建出來的。

kubedns容器的功能

接入SkyDNS,為dnsmasq提供查詢服務
替換etcd容器,使用樹形結構在記憶體中儲存DNS記錄
通過K8S API監視Service資源變化並更新DNS記錄
服務10053埠

對功能有了大概瞭解之後,我們下面就結合原始碼來看看各個功能是如何實現的。

kubedns實現——SkyDNS接入

下面是kubedns啟動的部分程式碼,這部分顯示的是kubedns在啟動的時候會初始化一個SkyDNS Server,初始化的時候傳入了一個KubeDNSServer.kd。

Kubernetes服務發現和kube-dns外掛Kubernetes服務發現和kube-dns外掛

SkyDNS Server在初始化的時候需要傳入一個Backend介面,其定義如下。SkyDNS基於Etcd實現了該介面,也會使用它初始化Server。得益於SkyDNS的良好設計,K8S只要實現該介面便可以接入SkyDNS來提供DNS查詢服務,並定製儲存功能。

Kubernetes服務發現和kube-dns外掛Kubernetes服務發現和kube-dns外掛

kubedns實現——etcd替換

在服務發現的流程中,主要用到了Records這個方法,下面我們就來看看K8S是如何實現這個方法。

主要步驟是先將域名按“.”拆分,並將各部分顛倒順序生成一個path陣列。呼叫getRecordsForPath方法獲取DNS記錄並返回。
Kubernetes服務發現和kube-dns外掛Kubernetes服務發現和kube-dns外掛

getRecordsForPath會呼叫cache的相關方法。這個cache會被初始化為一個TreeCache結構,定義如下:
Kubernetes服務發現和kube-dns外掛Kubernetes服務發現和kube-dns外掛

如下圖所示,TreeCache的結構類似於目錄樹。從根節點到葉子節點的每個路徑與一個域名是相對應的,順序是顛倒的。它的葉子節點只包含Entries,非葉子節點只包含ChildNodes。葉子節點中儲存的就是SkyDNS定義的msg.Service結構,可以理解為DNS記錄。

在Records介面方法實現中,只需根據域名查詢到對應的葉子節點,並返回葉子節點中儲存的所有msg.Service資料。K8S就是通過這樣的一個數據結構來儲存DNS記錄的,並替換了Etcd。
Kubernetes服務發現和kube-dns外掛Kubernetes服務發現和kube-dns外掛

kubedns實現——監視Service

最後我們來看一下監視Service資源的相關程式碼。如下圖所示,這裡使用了k8s.io/kubernetes/pkg/client/cache包的NewInformer方法,這個方法在K8S原始碼裡會經常看到。其引數為:

第一個引數需要傳入ListWatch結構,它定義了List和Watch操作步驟。kd.kubeClient結構可以用來訪問K8S API,從程式碼中可以看出分別訪問了List和Watch API

第二個引數為監視的資源型別,程式碼中指定了Service資源

第三個引數為List操作的執行間隔。Watch操作是通知機制,只要監視的資源傳送變化

就會呼叫對應的回撥函式。List操作會獲取最新的全量資源與本地狀態進行比較來產生通知,可以避免網路原因導致的Watch丟失通知的情況。List操作代價較高,因此需要通過第三個引數來設定其執行間隔。

最後一個引數用來設定處理事件的回撥。
Kubernetes服務發現和kube-dns外掛Kubernetes服務發現和kube-dns外掛

下面我們以新增Service事件的處理流程為例來簡單瞭解一下事件處理的程式碼。

在建立一個K8S Service資源後,newService方法最終會呼叫newPortalService方法,其程式碼如下。getSkyMsg函式會將Service的ClusterIP儲存到msg.Service結構中並返回,對應recordValue。recordLabel可以理解為一個摘要值,與ClusterIP是一一對應的,它將作為TreeCache葉子節點的key。

最後根據Service資訊找到對應的樹枝(如不存在會構建),並設定葉子節點。這樣一個新建的Service對應的DNS記錄就儲存到kubedns中了。

Kubernetes服務發現和kube-dns外掛Kubernetes服務發現和kube-dns外掛

Kubedns容器的主要原理已經講解完了,下面我們簡要介紹一下其他兩個容器。

dnsmasq簡介

Dnsmasq是一款小巧的DNS配置工具
在kube-dns外掛中的作用:
通過kubedns容器獲取DNS規則,在叢集中提供DNS查詢服務
提供DNS快取,提高查詢效能
降低kubedns容器的壓力、提高穩定性
Dockerfile在GitHub上Kubernetes組織的contrib倉庫中,位於dnsmasq目錄下。
在kube-dns外掛的編排檔案中可以看到,dnsmasq通過引數–server=127.0.0.1#10053指定upstream為kubedns。

exechealthz簡介

在kube-dns外掛中提供健康檢查功能
原始碼同樣在contrib倉庫中,位於exec-healthz目錄下。
新版中會對兩個容器都進行健康檢查,更加完善。

總結

1.4版本kube-dns外掛的三個容器的功能如下:

kubedns容器
監視k8s Service資源並更新DNS記錄
替換etcd,使用TreeCache資料結構儲存DNS記錄並實現SkyDNS的Backend介面
接入SkyDNS,對dnsmasq提供DNS查詢服務

dnsmasq容器
對叢集提供DNS查詢服務
設定kubedns為upstream
提供DNS快取,降低kubedns負載,提高效能

exechealthz容器
定期檢查kubedns和dnsmasq的健康狀態
為k8s活性檢測提供HTTP API

相比於1.2版本,個人認為有了如下改進:

無狀態服務。1.2版本中,需要將Etcd的資料Volume出來才能保證Etcd容器重啟之後資料不會丟失,服務可以快速恢復。新版本中作為無狀態服務出現,通過增加冗餘來提高可靠性。即使kubedns容器重啟,dnsmasq快取機制也可以保證服務的可用性。

優化查詢效率。SkyDNS直接從記憶體中獲取DNS記錄。

完善健康檢查。1.2版本中只對kube2sky設定了健康檢查。

除了改進之外,還有一點不足,也是大家比較擔心的 「 記憶體佔用 」。目前在kube-dns編排檔案中預設設定了記憶體限制為170M,在註釋中可以看出這一數字並未在大規模叢集中驗證。不過相信不久的將來我們就能看到