1. 程式人生 > >Kubernetes(K8S)的服務發現和kube

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提供健康檢查功能。

20161028145508

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

不同點:

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

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

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

4.Etcd容器不見了。

20161028145516

相信有些人會好奇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。

20161028145524

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

20161028145533

  • kubedns實現——etcd替換

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

主要步驟是先將域名按“.”拆分,並將各部分顛倒順序生成一個path陣列。呼叫getRecordsForPath方法獲取DNS記錄並返回。

20161028145541

getRecordsForPath會呼叫cache的相關方法。這個cache會被初始化為一個TreeCache結構,定義如下:

20161028145549

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

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

20161028145556

  • 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操作代價較高,因此需要通過第三個引數來設定其執行間隔。

  • 最後一個引數用來設定處理事件的回撥。
20161028145607

kubernetes/pkg/dns/dns.go

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

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

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

20161028145615

kubernetes/pkg/dns/dns.go

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,在註釋中可以看出這一數字並未在大規模叢集中驗證。不過相信不久的將來我們就能看到這一驗證結果。