Kubernetes(K8S)的服務發現和kube
大綱
- kube-dns的主要變化
- kube-dns的實現原理
- kubedns容器詳解
- dnsmasq容器簡介
- exechealthz容器簡介
主要變化
- 服務發現機制未變化
也就是說kube-dns對外的介面是基本沒變的。變化主要在於kube-dns外掛的內部組成,由原來的四個容器變為了三個。
Kube2sky通過K8S API監視K8S Service資源的變化,並根據Service的資訊生成DNS記錄寫入到etcd中。Skydns為叢集中的Pod提供DNS查詢服務,DNS記錄從etcd中讀取。Exechealthz提供健康檢查功能。
接下來我們再看一下1.4版本kube-dns的組成。對比兩張圖,可以很直觀的看到kube-dns對外介面沒有發生變化。Exechealthz是唯一保留的容器,依然提供健康檢查。
不同點:
1.會檢查兩個容器的健康狀態。
2.為叢集提供DNS查詢服務的容器由skydns變為了dnsmasq。
3.Kubedns容器替代了kube2sky來監視Service資源。
4.Etcd容器不見了。
相信有些人會好奇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。
SkyDNS Server在初始化的時候需要傳入一個Backend介面,其定義如下。SkyDNS基於Etcd實現了該介面,也會使用它初始化Server。得益於SkyDNS的良好設計,K8S只要實現該介面便可以接入SkyDNS來提供DNS查詢服務,並定製儲存功能。
- kubedns實現——etcd替換
在服務發現的流程中,主要用到了Records這個方法,下面我們就來看看K8S是如何實現這個方法。
主要步驟是先將域名按“.”拆分,並將各部分顛倒順序生成一個path陣列。呼叫getRecordsForPath方法獲取DNS記錄並返回。
getRecordsForPath會呼叫cache的相關方法。這個cache會被初始化為一個TreeCache結構,定義如下:
如下圖所示,TreeCache的結構類似於目錄樹。從根節點到葉子節點的每個路徑與一個域名是相對應的,順序是顛倒的。它的葉子節點只包含Entries,非葉子節點只包含ChildNodes。葉子節點中儲存的就是SkyDNS定義的msg.Service結構,可以理解為DNS記錄。
在Records介面方法實現中,只需根據域名查詢到對應的葉子節點,並返回葉子節點中儲存的所有msg.Service資料。K8S就是通過這樣的一個數據結構來儲存DNS記錄的,並替換了Etcd。
- 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操作代價較高,因此需要通過第三個引數來設定其執行間隔。
- 最後一個引數用來設定處理事件的回撥。
下面我們以新增Service事件的處理流程為例來簡單瞭解一下事件處理的程式碼。
在建立一個K8S Service資源後,newService方法最終會呼叫newPortalService方法,其程式碼如下。getSkyMsg函式會將Service的ClusterIP儲存到msg.Service結構中並返回,對應recordValue。recordLabel可以理解為一個摘要值,與ClusterIP是一一對應的,它將作為TreeCache葉子節點的key。
最後根據Service資訊找到對應的樹枝(如不存在會構建),並設定葉子節點。這樣一個新建的Service對應的DNS記錄就儲存到kubedns中了。
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,在註釋中可以看出這一數字並未在大規模叢集中驗證。不過相信不久的將來我們就能看到這一驗證結果。