如何利用Docker/Ansible實現輕量集群服務部署?(內有視頻演示+代碼)

本文內容整理自網易雲信系統架構師周梁偉老師線上講座「網易即時通訊雲平台怎麼實現輕量私有化」。

周梁偉,2011年加入網易,負責雲信IM平台的架構設計和伺服器研發團隊,曾先後參與了雲存儲系統、日誌採集平台、通用網站數據分析平台、易信後台等基礎平台和產品系統的功能設計和開發。也從事過HBase集群運維、數據統計分析等大數據相關工作,對大數據技術在線上產品中的應用具有一定的實踐經驗。

本文約6000字,閱讀時間約8分鐘。

今天和大家分享的主題是如何用Docker/Ansible來做輕量私有化的技術方案。首先,簡單介紹一下所謂輕量私有化到底是怎麼回事。

我們這幾年在做公有雲平台,過程中也接觸到很多客戶。有許多客戶提出來說,你們的雲平台服務很好、線上也很穩定,但希望能把雲平台在自己的環境里部署起來,於是就有了今天要分享的題目。

今天分享的內容,大致分為三個部分:第一部分,做輕量私有化面臨什麼樣的問題;第二部分,面對這些問題,我們如何去解決,即方案;最後是關於實踐的部分。因平台沒有辦法做實時Coding的交互的過程,所以我簡單錄了一段視頻的教程,大家可以對照這個視頻看一下我們具體是怎麼做的。

因為這個過程會涉及到代碼的部分,所以我把sample的代碼已經提交到GitHub,後面跟大家分享一下,大家可以對照著一起看。

| 問 題

大概2011年左右,我剛開始參加工作的時候,其實很少聽到XX雲的說法,大家講的更多的是分散式系統。那個時候分散式系統非常火,我第一個工作就是一個分散式的文件系統。

  • 分散式系統的分類

有工作經驗的同學都知道,我們說的雲其實就是一種規模化的分散式系統,這裡我為分散式系統簡單做了一個分類,當然這裡的分類不一定準確,只是我個人對分散式集群系統的理解。

1.面向特定場景的服務集群/雲

這個場景是什麼?比如面向存儲場景,我們知道有HDFS/HBase系統;面向計算型,MapReduce/Yarn/Spark/Storm這樣一種計算平台;還有一些像Mesos這種資源調度型平台。這些就是面向特定場景的雲。

除了這些大家耳熟能詳的平台,在網易內部,我們有DDB(分散式資料庫)以及RDS、NOS。NOS是一個分散式的對象存儲服務,也是屬於特定場景的服務集群,或者稱為特定場景的雲。

2.面向通用場景的資源型雲服務

比如現在非常熱門的OpenStack這樣一個IaaS層的雲平台,或者像Ceph這樣面向存儲的雲平台,其實它們都屬於通用型。

像Ceph就是把存儲資源雲化,還有像Vita-Line這樣的網路虛擬化的一種雲服務,我們把這些稱為面向通用場景的資源型雲服務。

3.獨闢蹊徑的容器型的服務

這和今天講解的內容有關係了,獨闢蹊徑的容器型的服務其實就是Docker容器+K8S,對於Docker這種容器進行一個資源調度的雲平台。

4.最近非常火的FaaS平台,就是函數及計算的雲平台,這個和今天的內容不太相關,就不發散開了。

所以,有那麼多不同類型的雲服務,我們針對專門的場景提供PaaS層的平台服務,但我們要把這樣一個平台服務搬到一個企業內部或者一個獨立的環境裡面。如果把整個雲搬進去,可想像這個代價會非常大,而且從複製性上來說,這不具有非常高的可複製性。

在線上運營那麼多年的雲平台後,我們也總結了一些經驗、積累了一些技術棧,開始思考能不能用更加簡單的方式實現輕量級私有化服務。

  • 輕量私有化面臨的問題

所以我們要的模式是什麼樣的?這是我們的服務面臨的問題:

1.單機不夠用。在任何一個環境里,沒有單體的程序或者單機軟體可以滿足一個應用場景,這種產品非常少。

2. 規模化不高。我們規模化還沒有公有雲上做得那麼高,在私有雲環境里,我們的用戶大概是5萬或者10萬的量級。

其實對於現在的企業來說,5萬到10萬算是非常大的規模,但和互聯網相比,還是遠遠不夠的,所以企業私有化經常面臨的用戶規模就是幾萬這個量級,這個規模化程度要說高,也不是那麼高。

3.技術棧太複雜。我們公有雲平台涉及到的模塊非常多,在這些模塊里,每個服務實現的技術棧是不一樣的,語言是不一樣的,依賴的軟體和網路環境也是不一樣的。

面對這樣一個技術棧,我們如何把這個技術方案盡量簡化,做到可複製性,這就是我們面臨的挑戰。

剛剛說整個技術棧太複雜,我們的集群也太複雜,規模沒有那麼大,但也不小。

這裡以網易雲信為例簡單看一下。下圖是我們現在涉及到的一些服務架構,當然這裡面沒有列全,但是主要模塊都已經列出來了。

從這張圖可看到,我們的服務涉及到非常多的不同的服務模塊,比如說負責長連接的服務,負責做業務調度的服務,包括一些Web類的API的服務,還有對象存儲、文件上傳、文件下載以及媒體服務,還有一個分散式的文件系統,再往後還有處理消息漫遊的服務、推送的服務等各種各樣的服務。

在這些服務裡面,涉及到的技術棧不只是Java,有些其它的比如消息隊列,我們今天演示的就是RabbitMQ ,它的技術棧是在Erlang 層面,和我們之前熟悉的Java是完全不一樣的東西,像資料庫或者說緩存服務,那又是別的技術棧的東西。

所以,從這張圖裡我們發現,想要解決集群的快速部署 ,我們面臨的問題就是:這麼繁雜的一個集群,我們要怎麼通過技術讓它變得簡單。

於是,進入今天的重點分享環節,輕量集群部署應該怎麼做?

| 方 案

  • 輕量集群服務部署

如何部署輕量集群服務?我們總結了一下,有三句話。

1.標準OS提供計算資源。

因為是標準OS,所以能夠兼容物理機/雲主機這樣不同的環境。

2.Docker實現程序包封裝和運行時資源隔離。

前面說到,我們整個集群的搭建過程中,服務模塊是基於不同技術棧上來做的,可能有的是Erlang,有的是C,有的可能對 Linux的內核有要求, 比如有的要求4.x的版本,有的要求2.x的版本。

這樣不同程序包的整個依賴環境會非常複雜,對此,我們可以用Docker的形式來將這些程序包全部封裝起來,同時還能在運行時實現資源隔離, 我們對CPU和對內存都去做限定。

3.用Ansible實現集群分散式部署。

為什麼要在這裡用Ansible?我在前面說Docker時,很多同學自然而然會想到,那你用Docker,是不是應該用容器雲、K8S呢?非常對,本身容器服務或者微服務,其實非常適合K8S這樣的容器。

但在輕量私有化這樣一個限定的場景里,如果為了在幾台機子上部署一套雲平台,專門去搭建一個K8S,代價是非常大的。因為本身K8S需要做鏡像庫、資源調度、做控制節點,對成本的要求本身就比較高。

那麼既然要把這個東西脫離,我們就必須要有一個可以替代的東西,在這裡就是Ansible。

Ansible本身是用來做協同的多機同步控制處理的框架,可能做運維的同學會比較了解,它是大家日常做集群化的操作中非常有用的東西,我們就把它引入進來,解決我們的問題。

  • 常用的技術棧工具

1.Docker

Docker是什麼?Docker現在被稱為Moby,2017年4月在DockerCon上,官方做了這樣一個動作,後面就被商業化了,這裡說到的Docker是Docker CE或者叫Moby。

Docker是一個開源的應用容器引擎,讓開發者可以打包它們的應用以及依賴包到一個可移植的容器中,然後發布到任何流行的Linux機器上,也可以實現虛擬化,容器是完全使用沙箱機制,相互之間不會有任何介面。

那麼在Docker裡面有幾個術語,這裡簡單說一下:

  • Docker鏡像。Docker鏡像非常像管理源代碼用的GitHub,了解GitHub的同學都知道GitHub上可以用來做版本控制,對我們代碼可以做提交、開分支,可以做commit, 各種各樣的操作,非常簡單、非常快速。
  • 鏡像倉庫。Docker的鏡像倉庫類似於GitHub這樣一個的產品形態,可以幫助我們管理鏡像,那麼Docker鏡像是把我們所有程序包和我們的應用以及程序包依賴的依賴包全部打包成一個鏡像的形式、一個Image用來管理,我們可以理解為,它也是一個代碼,它是一個可執行的、二進位的代碼。
  • Container實例。Container實例就是把一個Docker鏡像給跑起來,在運行過程中,我們稱它為一個容器實例。
  • 數據卷Volume。我們可以把它理解為一個目錄,或者磁碟上的一個空間。因為Docker本來用了沙箱機制,所以它在運行時所需要用到的這些磁碟、本地文件或者產生的一些數據,如果在不做任何配置的情況下,它是在這個沙箱裡面的。而通過數據卷的形式,我們可以把數據載入出來,載入到宿主機上,把宿主機上某個特定的目錄,或者是某個網路上特定的目錄,載入到容器里。在容器裡面這個數據卷就可以當作一個硬碟一樣,快速使用。
  • Docker網路。Docker網路是用來把實例和宿主機之間做一個相互的隔離或者做一個橋接。同時在Docker網路裡面,比如我們在容器內部,開一個TomCat服務是8080埠,但我們宿主機上面同時運行的有三個實例,大家知道3個8080埠不可能同時在一台宿主機上開始,如果要去改容器鏡像,那有點複雜。

通過Docker網路,我們就可以把這個埠直接映射出來,比如把3個容器的8080埠分別映射到宿主機上,分別為8080、8081、8082,它能夠做到快速的網路映射轉換的過程。

  • Dockerfile。Dockerfile是用來描述Docker鏡像的整個構建過程,可以理解為Docker的一個代碼,通過這個代碼,可以構建出一個二進位的鏡像。

2. Kubernetes

講到Docker的話,必然要講到K8S,K8S全名是Kubernetes,它是Google開源的容器集群管理系統,用Go語言開發,提供應用部署、維護、擴展機制等功能,利用K8S能方便地管理跨機器運行容器化的應用。

其主要功能如下:

  • 使用Docker對應用程序包裝(package)、實例化(instantiate)、運行(run)。

  • 以集群的方式運行、管理跨機器的容器。
  • 解決 Docker 跨機器容器之間的通訊問題。
  • Kubernetes 具有自我修復機制,比如Docker在這個宿主機上,這個容器死掉之後,可以在另外一個宿主機上將這個容器迅速地拉起來,保證服務的計算容量和高可用,這個非常重要,使得容器集群總是運行在用戶期望的狀態。

3.Ansible

然而K8S比較重,引入輕量方案中不合適,於是我們找了一種替代品——Ansible。

Ansible是一種自動化運維工具,基於Python開發出來,它集合了眾多運維工具的優點,可以實現批量系統配置、進行程序部署、運行命令等功能。

Ansible 是基於模塊工作的,我們知道Eclipse也是基於模塊,基於模塊代表它可以擴展,社區也貢獻了非常多的模塊來擴展Ansible的能力,所以Ansible現在是非常強大的。

它本身沒有批量部署的能力,但模塊可以實現非常多的功能,主要有:

  • Host inventory:用來制定需要操作的被控機的列表,它可以在配置文件中快速修改列表,來調整需要操作的環節,下面實踐的部分會講到這些。
  • 連接插件Connection Plugins:負責和被監控端實現通信,被控機無 Agent,所以不存在初始化,或者Agent下線後導致被控機沒有辦法去執行命令這樣子的問題。
  • Host inventory:用來制定需要操作的被控機的列表,它可以在配置文件中快速修改列表,來調整需要操作的環節,下面實踐的部分會講到這些。
  • 藉助於各種模塊、核心模塊、Command 模塊、自定義模塊來實現最多的功能。
  • Playbook:劇本執行多個任務時,可以讓節點一次性運行多個任務,可以幫助我們快速集群化部署的效果。
  • 方案呈現

簡單給大家科普了一下我們需要用到的三個技術棧上的工具,接下里看一下網易雲信把這麼多的模塊、這麼複雜的架構全部打包起來,會變成什麼樣子。

如圖所示,最底層就是一個OS層,在OS層上面運行的是一個一個Docker容器,這些Docker容器運行狀態需要被監控,所以我們在OS層的宿主機上部署了MetricBeat,它是 ELK中用來做監控的Agent,可以幫助我們監控各個容器是否正常。

那麼在OS層中還需要解決的問題是什麼?一是許可權的管理,比如主機和主機之間需要互通;二是基礎性能管理,比如文件句柄數是否開夠了,還有文件目錄或者數據盤的許可權是否開放出來,包括網路層的一些參數需要進行一些性能調優;三是Docker環境的初始化,還有Supervisor的服務,因為我們任何一台機器或任何一種服務都有當機、故障重啟的風險,一旦伺服器重啟,你需要手動拉起服務,或者它掛掉以後沒有一個自動恢復的機制,這個在高可用上可能體驗不是那麼好,所以我們引入一個Supervisor,通過Supervisor作為一個後台,幫我們把這些服務保持一個健康的狀態。

最重要一點是,在OS層還需要做一個CPU和內存資源的隔離,這個也是藉助於Docker本身基於Cgroup做資源隔離的能力實現的。

在OS層之上,就是Docker容器,我們在每個Docker容器裡面寫的時候不是單個的進程,而是我們用的多個進程,因為我們理解任何一個服務其實都有可能不是一個單體程序,有可能是一個主程序加上N個輔助程序來完成,所以我們在這裡面把N個輔助程序通過Supervisor關聯到一起。

像圖上這個示例裡面有幾個程序,一個是我們的主進程,就是NIM Proc,還有處理日誌用的Rsyslog 或者Filebeat, Filebeat也是ELK裡面做日誌採集用的,還有Metricbeat,這是用來做指標採集的,把這些主進程或輔助進程都通過Supervisor管理起來。

在Docker里還要解決的一個問題是對依賴環境的差異化,比如:當前需要執行的程序需要JDK 1.7的版本,那麼我們可以把1.7的JDK封裝到這個鏡像里去,對於另外一個程序或者另外一個軟體如像ELK裡面的ElasticSearch,它需要的JDK版本是1.8以上,如果我簡單地把進程跑在宿主機上,可能需要去安裝多個JDK的版本,然後在應用裡面去單獨指定,這個過程可能會比較複雜。

如果沒有軟體衝突還好,一旦你依賴的是內核層或者底層庫,就會產生軟體衝突,代表你不能在同一個機器上運行。這種情況就可以用Docker去達到這樣一個目的。

再往上層就是一個個模塊被我們打包成一個個鏡像,這裡一個個方塊代表著單個的鏡像,這些鏡像都是用Dockerfile來描述,通過Dockerfile我們可以把這些鏡像一個個程序化地構建起來,構建完了之後,我們要把鏡像跑起來,跑起來之後變成了容器實例,一個個實例之間相互組合之後變成了整體的服務。把這些鏡像拉起來變成容器服務,這就是Ansible要做的事情。

我們看到最上層有非常多的文件,這些文件代表一個個YAML文件,代表了一個個Playbook。我們看到這裡一個個Playbook,比如DB_cluster,這個腳本是專門用來處理構建DB資料庫的集群。又如NOS.yml,NOS是我們網易對象存儲服務的簡稱,它通過這個腳本,可以在多機上部署起來一個對象存儲服務。

其它的也是類似這樣的服務,可以通過腳本化的形式管理起來。通過這張圖大家也可以看到我們整個技術方案,通過藉助這樣一個工具,把它做到一個可複製的程度。

| 實 例

下面就是實踐的部分:先簡單搭一個RabbitMQ的集群, 我們需要把單節點給布置起來,需要決定這個MQ的節點的角色,然後做不同的配置、cookie 的同步,做各種各樣的事情,最後還需要做一些用戶的初始化,整個部署的過程非常複雜。

對於一個經驗豐富的運維人員來說,部署一個集群也需要花費一段時間,過程中有可能踩到很多坑。我們希望部署一個MQ集群就像把大象關進冰箱一樣,只需要三步:打開冰箱門——放進大象——關上門。這個就是我們的目標。

https://github.com/williamleung/lite-deploy-sample。

具體過程也已經錄屏,大家可以參考:

undefined_騰訊視頻v.qq.com圖標

參照這個代碼怎麼用Docker和Ansible把整個過程自動化處理,這裡還涉及到一個多機部署的問題,在示例代碼中,使用Vagrant來創建虛擬機模擬了一個多機部署的環境。

Vagrant今天在分享里沒有講太多內容,感興趣的同學可以自己了解一下,簡單來說,Vagrant可以幫助你用代碼的形式快速維護虛擬機的集群環境。

實踐過程中有一個開發部署的模型,這個模型分兩個部分角色,研發人員做的工作和部署人員做的工作,完成後幫助大家解讀示例的代碼部分。

研發人員主要負責開發打包鏡像、開發部署腳本以及鏡像和腳本的分發;現場實施人員主要負責創建伺服器、目標環境初始化以及集群部署。

最後提供給大家幾個參考資料,以供大家學習:

  • Vagrant地址

app.vagrantup.com/boxes

  • 網易雲的容器服務地址

c.163.com/hub#

  • Ansible資源庫

Find, reuse, and share the best Ansible content

  • Daocloud容器加速鏡像地址:

DaoCloud 軟體中心 - 極速下載 DaoCloud 軟體

| 問答環節

【問題】:Docker 是必然的趨勢嗎!

答:是不是「必然」的趨勢我覺得每個人都有自己的看法,其實對於任何一個技術或者工具,我覺得都有自己的應用場景,如果能結合自己的業務把Docker用好更加重要。

比如在今天分享的這個case里,我們更加關注的是Docker對應用程序及其依賴庫進行封裝的能力,通過dockerfile可以將其固化下來支持不斷迭代;其次就是容器支持在運行時資源隔離,幫助我們更好地利用宿主機上的資源;但藉助K8S實現的容器服務調度的功能就沒有用到,這也是我們基於自己的業務場景做出的選擇。


推薦閱讀:

為什麼已經在美上市的公司最近要實現私有化,這個私有化由 CEO 向全公司發出邀請,這樣做的意義是什麼?
為什麼分眾傳媒宣布私有化後,其股票價格一直低於私有化要約的價格?
怎麼看待黨報刊文談李冰冰上協和:反映中澳醫療體系差別而非醫生水平?
人人遊戲私有化的暗處:一張拖欠已久的賬單
上市公司私有化的主要流程是怎樣的?

TAG:私有化 | Docker | 代码 |