一個基於 Docker 的負載均衡實例

作者:邵靖 騰訊工程師

前言

近年來,雲計算的概念席捲了整個 IT 圈。拋開忽悠概念的因素,雲計算的發展和應用極大地改變了 IT 產業的研發、運營和管理。作為雲計算最重要的核心技術之一,虛擬化技術的發展促成了這一巨大的變革,而容器技術作為最具有代表性的虛擬化技術革新,目前已經得到了業界的廣泛關注,其中以 Docker 最具有代表性,最為火爆,鵝廠目前很多應用也或多或少使用到了 docker。本文目地主要是給大家簡單安利一下目前最火的容器產品 Docker 及其所涉及的相關技術,並通過一個實際例子演示一下容器技術的典型應用場景。

簡單扯扯容器

虛擬化技術的核心在於資源劃分、隔離和管理。圍繞著這個核心,在不同層次上發展出了多種虛擬化技術,如以虛擬機為代表的硬體級虛擬化(Hardware Virtualization)技術以及以容器(Container)為代表的操作系統級虛擬化(Operating System Level Virtualization)技術。容器並不是一個全新的技術體系,下圖是從 Wikipedia 摘錄的容器實現全家福,可以看到此項技術可以追溯到 1982 年,並且覆蓋了多種操作系統。近年來,隨著 Docker 的快速崛起,容器技術再次吸引了 IT 業界的廣泛關注。

為什麼要用"再"?這就需要聊聊容器的特點以及跟虛擬機的區別和聯繫。隨著技術的發展,IT 基礎設施的能力越來越大,原來需要多個硬體單元完成的任務現在僅用一個硬體單元的一部分就能夠完成了,那麼為了資源的合理分配,將資源集合劃分為更細粒度的資源單元,就是虛擬化技術的意義。下圖是虛擬機和容器技術的對比示意圖。左圖中,在宿主機操作系統(Host OS)之上,通過 Hypervisor 將資源劃分為資源單元,每個單元有自己的操作系統(Guest OS)、工具和庫,在其上運行各自的 app 互不干擾,每個資源單元就是一個虛擬機;右圖中在宿主機操作系統之上,通過容器管理器(Container Manager)將一部分資源隔離出來形成資源單元,每個資源單元沒有自己的操作系統,有自己的工具和庫,在這之上運行自己的 app,這裡每個資源單元就是一個容器。

通過上圖的分析可以看到,虛擬機和容器技術在隔離的層次上並不相同,虛擬機有從操作系統開始的完整的基礎設施,而容器僅在工具、庫這個層次及以上形成了自己的特有區域。這個特點決定了兩者的屬性,容器是比虛擬機更輕量的資源單元。輕量,意味著快速啟停、遷移、分發等優良的性質,並且在資源總量一樣的情況下,能夠支持的容器單元數量將數倍於虛擬機。

下面扯點題外話,人類善於通過邏輯思維的手段從不同的事物中抽取出本質並加以推廣。舉個例子,運輸業是一個很古老的行業,運輸業的發展推動了人類的進步,當人類的運輸需求變得越來越複雜,遇到了下面的問題,如何用標準的交通工具運輸各種各樣需求不同的物品?

為了解決這個問題,人們發明了集裝箱,它將運輸品所需滿足的條件(光照、冷藏、密封等)限制在集裝箱之內,對外而言它只是一個提供標準運輸介面的單元,這樣的單元可以通過各種使用標準運輸介面的交通工具運輸。

看到這裡大家應該不難理解,通過類比,IT 攻城獅們再次發現虛擬化技術所蘊含的巨大潛力,虛擬化可以充當集裝箱的角色,將 app 運行所需滿足的條件(依賴、工具、庫等)限制在資源單元之內,對外提供標準的介面,並且能夠裝載於各類伺服器基礎設施。並且在比較了各類虛擬化技術後,容器輕量的特點讓它更適宜與成為集裝箱。在重新審視了容器技術的巨大價值之後,各方勢力紛紛開始佔山頭立門戶,在這個過程中諸多新的容器技術相繼發布,其中就以取名為碼頭工人的 Docker 最具代表性。

WoW,Docker 來了!它是誰?能幹嘛?

Docker 是誰?Docker 是由 dotCloud 公司在 2013 年開源的一款開源容器引擎,後來 Docker 火了乾脆公司就改名為 Docker 了……

Docker 是一種基於 Linux 內核隔離技術的容器實現,用 Go 語言編寫,其功能特性歸納為:

  • 資源隔離,包括文件系統、進程、網路等;
  • 資源控制;
  • 文件系統,具有寫時複製、日誌記錄、版本管理等;
  • 提供控制 API;
  • 提供鏡像分發、重用的生態系統。

Docker 的實現依賴了以下技術:

  • Namespace,利用 Linux 內核提供的 namespace 機制,容器能夠建立資源隔離單元,隔離內容包括進程(PID)、網路(Net)、進程交互(IPC)、文件目錄(Mnt)、hostname 及用戶和用戶組等。
  • CGroup,利用 CGroup 機制來處理不同容器之間競爭宿主機系統資源的問題,實現對資源的配額和度量。
  • LXC,在 Docker 發展的過程中,早期使用 LXC 來共享內核,實現容器的快速啟停以及減少內存的消耗,在後來的發展中,docker 主鍵用 libcontainer 替代並擴展了 LXC 的功能。
  • AUFS,Docker 默認使用 AUFS 構建容器的文件系統,提供寫時複製(Copy On Write)的特性,這是 Docker 鏡像保存、修改及分發的基礎。

基於 docker 提供的基礎功能和其開源的屬性,業界巨頭和初創公司紛紛以此為基礎開發出各種框架和工具,在分散式特性、網路特性、存儲特性以及管理特性等方面對 Docker 進行擴展和補充,鵝廠 IEG 內部使用的 Docker 管理平台以及 TEG 的 Gaia 系統都是很好的例子。關於 docker 技術原理的文章能夠很容易搜索到,本文就不在這裡展開了,下面將根據容器的特點介紹幾個典型的應用場景,並給大家介紹如何從零開始寫一個簡單的基於 Docker 的負載均衡器。

根據前述,Docker 作為一種容器,能夠快速啟停、佔用較少的系統資源,同時根據其自身實現技術特點,又具備寫時複製、保存分發等特點。所以不難想到以下幾種應用場景很適合使用 Docker 實現:

  • 平台即服務(PAAS),沙箱的完美替代品;
  • 自動測試及持續集成,測試妹紙笑開了花;
  • 構建標準化無狀態運行環境及快速部署,你知道重裝環境有多麼煩么;
  • 高可用(HA)和負載均衡(LB)系統,讓故障和攻擊陷入人民戰爭的汪洋大海,一會慢慢講。

誇了那麼多,難道 Docker 就沒有缺點么?不是的,目前 Docker 正在快速發展過程中,在人們使用的過程中逐漸暴露了 Docker 的很多問題,其中不乏一些很嚴重的問題:

  • 隔離性問題,docker 依賴 Linux 內核提供的隔離機制,相比虛擬機而言,級別和程度都有不少下降,這也是追求輕量帶來的副作用
  • 安全性問題,跟隔離性分不開,目前 Docker 存在諸如 root 許可權提升,共享宿主信息等安全漏洞,這阻礙了其推廣到企業級應用的腳步
  • 性能問題,由於引入了 AUFS,提供了很好的 COW 特性,但是這也會對 I/O 性能造成一定影響,因此不建議用 docker 負擔有狀態的任務
  • Docker 提供用於鏡像分發和重用的生態系統,全球開發者都能夠通過這個平台進行交流,但隨之而來的就是如何保證鏡像的質量、可靠性和安全性,PS.別人做的你敢用?

儘管存在諸多問題,但是這並不妨礙 docker 前進的腳步。業界也在期盼 docker 的快速成長。

別光看,動手做吧

感謝您能夠看到這裡,啰嗦結束之後,我們來動手做一個簡單的基於 Docker 的負載均衡器實例。

負載均衡,顧名思義就是對負載進行分流實現均衡的目的。在 web 網站以及 web service 發展的過程中,負載和處理能力的矛盾使得負載均衡成為必須考慮的問題,如下圖所示:

  1. 當請求負載在單節點處理能力之下時,沒有必要設置負載均衡器,所有的請求都由一台伺服器搞定;
  2. 當請求達到一定數量,超過了單台伺服器處理能力,那麼現在就需要添加多台伺服器,並且使用負載均衡器(Load Balancer)進行流量分發,保證業務請求平均地分散到各 web 伺服器;
  3. 業務的流量特點很多變,流量高峰何時到來誰也不知道,如果使用多台 web 伺服器在後台值班,這樣難免會造成資源的浪費,並且這樣也有可能無法應對流量峰值,因此需要一個自動的負載均衡器,它能夠實時檢測當前到來的業務流量,並且能夠控制後端資源池快速完成資源的申請、釋放以及路由切換,這樣就可以通過實時的流量檢測數據完成 web 伺服器的動態配置,在後端資源池能力足夠的情況下,輕鬆應對多變的請求量。

為了實現 3 的目標,我們的系統需要以下幾大功能模塊,如下圖所示:

  • 負載均衡器:負責分發流量請求,並且在節點數目變化時完成路由切換。在本實例中,我選取了開源的 HAProxy 作為負載均衡器的實現,它支持多種流量分發演算法,本例採用了簡單的輪詢(RoundRobin)模式。
  • 負載監控器:負責監控當前負載請求量,並且根據設定閾值決定下一步動作。本例中由 Python 實現,配合 Web 伺服器實時上報的當前流量狀況決定是否動態申請資源,實現 Scale-Out,具體代碼請移步微碼:

    github.com/kevinjs/dock
  • 資源控制器:封裝 docker 的操作 API,根據監控器的指令完成添加或刪除服務節點的具體操作,本例中由 Python 實現,具體代碼請移步微碼:

    github.com/kevinjs/dock
  • Web 伺服器:發揮 docker 的特點,將業務所需的 WebServer 功能組件,負載獲取上報模塊等打包成為鏡像並註冊於鏡像庫中,根據控制器的指令啟停。

Docker 服務鏡像的準備按照以下步驟進行:

  1. 安裝 docker 及相關服務組件;
  2. Docker pull 拉取空白的實例;
  3. 安裝所需的基礎組件,部署業務代碼;
  4. 將實例保存為鏡像(類比 OOM 中創建了一個類);
  5. Docker push 可以將此鏡像上傳到公共、私有鏡像庫,完成分發或重用(類比將 4 中創建的類發布,供其他人繼承或是實例化對象並使用)。

本例所使用的鏡像已經上傳至 docker 官方鏡像庫,可以通過以下 docker 命令下載使用:

docker pull kevinjs/ubuntu:py27tor2n

運行效果

通過模擬 http 請求訪問負載均衡器服務 IP,控制訪問量觀察後端服務節點的數目和響應情況,最後將數據可視化輸出如下圖所示,可以看到,隨著訪問量的上升,監控器準確地反饋了流量的變化,並在短時間內通知後端資源池添加服務節點,從一個服最終增加到 18 個服務節點,在每一時刻都盡量保證每個服務節點都分布到平均的負載壓力,並且在負載下降後及時減少服務節點以節省資源。需要解釋一下的是,從圖中可以看出每隔一一定時間訪問量有突降的情況,這是由於需要在自動添加服務節點後重啟負載均衡器 HAProxy 造成的,這裡是一個簡單的實現,如果換用能夠動態載入配置的負載均衡器方案,就能做到流量的平滑過渡。

下圖是總的訪問量與平均訪問量之間的對比,在總請求量暴漲的情況下,通過快速反饋調節後端 web 服務實例的數量,平均訪問量快速收斂,實現負載均衡。

推薦閱讀:

什麼是 Docker ? - 騰雲閣 - 騰訊雲

微服務架構: 微服務架構的核心概念 ( 一 )日誌易:金融支付行業日誌大數據分析案例解讀 - 騰雲閣 - 騰訊雲海雲捷迅的教育雲實戰經驗分享 - 騰雲閣 - 騰訊雲
推薦閱讀:

深度調查:24%的Docker鏡像都存在嚴重漏洞
如何編寫最佳的Dockerfile
Docker集群日誌收集:Syslog+Rsyslog+ELK
Docker學習資源匯總

TAG:Docker | 负载均衡 | 云计算 |