1. 程式人生 > >基於Docker的負載均衡和服務發現

基於Docker的負載均衡和服務發現

應用的容器化和微服務化帶來的問題

  • 在預設網路模型中,容器每次重啟後,IP會發生變動,在一個大的分散式系統保證IP地址不變是比較複雜的事情
  • IP頻繁發生變動,動態應用部署無法預知容器的IP地址,client端如何發現server端的訪問端點?

解決方案(根據客戶端是否有感知進行分類)

  • 客戶端的發現。client 訂閱註冊中心,有一個固定的註冊中心地址,client訂閱某個服務的註冊中心,註冊中心根據服務的執行狀態推送某個服務的訪問端點列表給client端。 該方案的實現舉例有dubbo,DNS的解析等。

  • 服務端的發現。服務端提供某個服務固定的訪問端點,客戶端直接訪問該端點即可達到與服務端通訊的目的,該訪問埠對接後端具有動態IP的容器,作為請求的入口,負責請求轉發到後端的容器。該方案的實現舉例就是各種對後端負載均衡的實現,包括LVS/Nginx/HAProxy等。我們可以認為如下公式基本成立
    "服務端的發現" = "負載均衡器" + "路由配置自動更新"


    對比客戶端的發現,服務端發現對客戶端無感知,由於很多已有的應用或者系統並不是按照類似Dubbo這種服務化的框架實現的,這些應用或者系統的客戶端對服務發現都是無感知的,因此服務端的發現就表現出了獨特的優勢。客戶端的服務發現方法中,DNS是一個例外,幾乎所有的客戶端都支援DNS。下面介紹客戶端DNS和服務端的負載均衡器做服務發現的幾個方案。

服務發現方案

  • DNS解析到多個IP
    • 優點:Docker 版本大於1.10即原生支援容器叢集內部DNS的服務發現。
    • 缺點:由於DNS TTL生效時間的存在,解析的結果不能做到實時,即使TTL設定為0,某些應用或者方法庫會快取DNS解析的結果,導致會解析到已經失效的地址上
  • 核心空間 LVS/IPVS
    • 優點:IPVS的方案是docker在未來會正式釋出的1.12版本中採用的方案,主要是做到了4層的負載均衡,請求的轉發實現在核心中,不需要二次拷貝請求和響應的內容,不需要解析和處理7層HTTP協議,效率高
    • 缺點:缺少7層負載均衡的支援,一個服務的負載均衡會佔用主機的一個埠,服務與服務之間暴露的埠如果相同會產生衝突
  • 使用者空間 Nginx
    • 優點:同時支援4層和7層負載均衡的代理,多程序模型方便利用多核效能,具有cache功能,亦可作為靜態檔案web伺服器,7層可以根據區分HOST欄位來複用同一個80埠,解決埠衝突的問題。7層負載會解析HTTP協議,支援多層路由,包括根據域名不同進行路由,根據路徑不同進行路由,內部重定向等多種HTTP協議層的功能。
    • 缺點:負載均衡排程演算法較少,對後端進行健康檢查的策略較少
  • 使用者空間 HAProxy
    • 優點:同時支援使用者空間4層和7層負載均衡的代理,是一個純粹的軟負載均衡器,支援Round-Robin,static-rr,least-conn,source-IP等多種排程演算法,對後端進行健康檢查的策略完備,包括TCP埠檢查,HTTP請求檢查,可執行程式檢查等帶外檢查。7層可以根據區分HOST欄位來複用同一個80埠,解決埠衝突的問題。7層負載會解析HTTP協議,支援多層路由,包括根據域名不同進行路由,根據路徑不同進行路由,內部重定向等多種HTTP協議層的功能。
    • 缺點:不能作為靜態檔案伺服器,不支援cache快取功能
    • aliyun容器服務的負載均衡解決方案

      經過前面優缺點的分析和結合aliyun自身產品的優勢,提供了以下解決方案。

      • docker自帶的DNS 服務發現,只要docker版本大於1.10(目前阿里雲支援的docker最新版本為1.11),就支援DNS的服務發現。其特點是為1)每個容器提供獨立的DNS解析;2)可以通過容器名與別名(aliases)方式在整個網路作用域內解析到某個容器的IP;3)通過連結別名的方式(link aliases)在容器的作用域內設定DNS解析,好處是不同容器的相同別名不會衝突。4)對外部的DNS解析請求進行代理。如下圖所示
      • 4層,提供阿里雲的雲產品SLB的負載均衡方案,底層是基於LVS做了改造和增強,SLB經受了長期線上實戰的考驗,穩定性和正確性可以得到保證。同時,容器服務在此基礎上能做到監控容器部署的情況,隨著服務的啟停,動態繫結SLB後端的埠(稍後解釋原理)。
      • 7層,提供基於haproxy的負載均衡方案,支援靈活的配置。容器服務在此基礎上能做到監控容器部署的情況,隨著服務的健康狀況,動態調整後端的負載(稍後解釋原理)。
      • 雖然我們4層主推SLB來做負載均衡,同時7層主推haproxy來做負載均衡,但是實際上SLB支援7層的負載均衡,底層基於Tengine,而HAProxy也支援使用者空間4層的負載均衡,使用者可以根據自己的情況,靈活選擇/組合方案。
      • SLB做到動態繫結的原理:swarm監聽容器的狀態,如果容器正常執行,則把容器加入到SLB的後端,如果容器發現異常,則把容器從SLB的後端摘下來。
        SLB_discovery

      • HAProxy實現動態服務發現的原理:HAProxy容器內除了有HAProxy軟體,還有指令碼程式監聽容器的狀態,根據容器的健康狀況重新生成負載均衡資訊,然後重新載入(reload) HAProxy,使得新的負載均衡資訊生效。
        haproxy_routing_discovery

      • 實現不停服rolling_update原理:平滑升級的關鍵在於每一時刻均有至少一個容器還能正常提供服務。1)需要部署多個容器,將容器分為A,B兩批更新。2)更新容器時,先將A批容器的路由從SLB或者HAProxy上面摘下來。3) 更新A批容器 4)A批容器健康檢查正常後,重新加入路由 5)摘下B批容器的路由 6)更新B批容器。

      • 實現灰度釋出原理:不通版本的服務可以共享同一路由資訊,通過調整SLB或者HAProxy權重的方式來做到灰度釋出。

      根據場景提供給使用者的服務形態

      • 簡單路由服務:基於HAProxy,我們加了一層wrapper,做到動態發現處於執行狀態的容器,加入到負載均衡中,我們稱之為簡單路由服務(routing service),其公網IP通過一個SLB對外進行暴露。主要解決如下需求:
        • 7層服務端點對公網暴露,即承接公網訪問叢集內使用7層協議的服務的流量。
        • 7層服務端點對內網暴露,即容器叢集內的負載均衡和服務發現:如下圖所示,叢集內的服務發現利用了Docker自帶的DNS resolver配合了HAProxy的負載均衡和健康檢查。圖中的LB即為簡單路由服務下的HAProxy容器,1)首先通過Docker自帶的DNS resolver將域名restserver.local解析到HAProxy容器的IP,此處會優先選擇當前節點的HAProxy容器進行負載均衡;2)RestClient請求域名restserver.local時,請求先走到代理容器LB;3)LB根據從Discovery Service獲取到的負載均衡資訊代理到提供相應服務的容器後端RestServer Contaienr
          inner_service_discovery
        • 不支援4層的路由
      • 自定義路由服務:作為一個可選的容器,實現跟簡單路由服務類似,解決如下需求:
        • 通過環境變數和標籤提供強大且靈活的配置支援,同時支援使用者基於該容器進行擴充套件(通過Dockerfile FROM的方式)。
        • 支援使用者空間的4層協議轉發
      • SLB路由服務:將SLB繫結到某個服務上面,後端隨服務的啟停動態配置。主要解決如下需求
        • 對外暴露的服務,使用4層協議,通過自定義SLB做4層的轉發
        • 容器間的4層或者7層通訊,流量特別大時,推薦使用自定義SLB(內網)服務。

      場景和對應的路由服務總結

      sn_and_ew_communication

      layers S-N (ingress 入口通訊) E-W (peer to peer 容器間通訊)
      layer 4 SLB路由服務(公網) 自定義路由服務/SLB路由服務(內網,防止迴路問題)
      layer 7 簡單路由服務 簡單路由服務/自定義路由服務

      如上述圖表所示,我們將容器叢集外進入容器叢集內的入口通訊稱為南北通訊,將叢集內容器和容器之間的流量成為東西通訊。我們根據不同的通訊形式和協議層提供不同的服務來滿足使用者的需求,例如對應南北通訊,如果是使用7層協議的服務,我們推薦使用者使用叢集的SLB進行流量轉發,最終的流量會轉發到每個主機的HAProxy容器上面,然後在分發到相應的處理請求的服務上。

      • 南北通訊
        • 指的是整個容器叢集入口的通訊。南北通訊的特點往往是通訊量比較大,因此我們首先用SLB將流量分散到各個主機節點。
      • 東西通訊
        • 指的是叢集內部,容器和容器間的通訊方式