基于 Mesos 和 Docker 構建企業級 SaaS 應用 Elasticsearch as a Service
1. 前言
Mesos從0.23版本開始提供了動態預留和持久化卷功能,可以利用本地磁盤創建數據卷并掛載到Docker容器內,同時配合動態預留的功能,保證容器,數據卷,機器三者的綁定,解決了Docker一直以來數據落地的問題。本文介紹了去哪兒網的ops團隊利用Mesos/Docker開發的Elasticsearch(ESAAS)平臺的建設的過程,以及實踐過程中的經驗總結。
2. 背景
從2015年底開始, 去哪兒網(以下簡稱Qunar)的Elasticsearch(以下簡稱ES)需求量暴增,傳統的ES部署方式大部分是以kvm虛機為ES節點, 需要提前創建好虛擬機, 然后在每臺機器上部署ES環境,費時費力, 并且最大的問題是集群擴容和多個集群的管理需要大量的人力成本。 隨著ES節點/集群越來越多, 這種方式 已經嚴重加劇了業務線的使用/維護成本, 并且也不符合OPS的運維標準。 因此我們就開始考慮,是不是能將ES做成一個云化的服務, 能夠自動化運維,即申請即用, 有條件的快速擴容/收縮資源。
近幾年Apache Mesos和Docker都是大家討論最多的熱門話題, 尤其是對于做運維的人來說,這些開源軟件的出現, 使得運維人員對機器集群的管理,資源的管理,服務的調度管理都大大的節省了成本,也使得我們的預想成為可能。借助Mesos平臺使我們提供的ES集群服務化,產品化。總的來說,我們的目標有這幾點:
● 快速集群構建速度
● 快速擴容和快速遷移能力
● ES使用/運維標準化
● 良好的用戶交互界面
3.技術調研/選型
我們調研了目前的三個產品/框架,從中獲取些功能方面的參考:
● Elastic Cloud
● Amazon Elasticsearch
● Elasticsearch on Mesos
前兩者是收費的服務并且沒有開源,我們從它們的文檔和博客中借鑒到了許多實用的功能點,可以解決我們目前階段的問題。我們根據這兩款商用的ES服務,結合內部的運維體系整理了需要實現的功能點:
● 集群統一管理
● 集群資源quota限定
● 數據持久化存儲
● 數據節點和節點資源快速水平/垂直擴容easily scale out
● 完整的集群監控和報警
● 完整的平臺監控和報警
● 統一的集群詳細信息和配置中心
● 集群行為和外圍插件的自助配置
● 集群的發布和配置管理
第三個是一個開源的Mesos框架,它的executor就相當于一個ES Node節點, 一個框架服務就是一個ES集群,框架內部處理節點間的相互發現。它支持自動化部署、集群的水平擴容、集群狀態實時監控以及查看集群信息,如shard/indices/node/cluster等,有web UI。 支持數據多副本容錯等, 但是也有局限,比如集群維度的數據持久化存儲,不能做到數據持久化到固定的節點,由于調度的策略,我們事先是無法預知節點在重啟后被調度到集群中的哪些機器上去, 所以,數據的持久化顯得尤為重要,例如,在集群重啟這種情況下, 如若不能保證ES節點在原先的節點上啟動, 那對于集群數據和集群服務都是毀滅性的,因為重啟之后, ES節點都“漂移”了,一致性不能得到任何保證。第二個局限就是無法完全自助化ES集群配置, 比如無法配置不同角色的節點,無法自定義數據存儲行為,包括索引分片,副本數量之類, 無法自定義安裝插件,無法自助script的使用等,不滿足業務線的使用需求。 除此之外, 還有一些, 比如ES節點縱向擴容(為某個實例添加更多的CPU和內存)、沒有Quota管理等未能支持的局限。 這就和我們預期的功能有些差距,不能完全滿足我們的需求。像這種自行實現一個調度框架的組織方式,缺少了許多靈活性,反而增加了我們許多開發的復雜程度,也使得我們的服務將會有較大局限。比如, 我們將來很可能的對服務的擴展和隨著業務而出現的新的功能和組織方式。
考慮到上述因素,我們決定利用Marathon來部署ES集群。相比較于前三者的局限性,Marathon是一個比較成熟并且比較通用的資源調度框架,并且對二次開發非常友好。尤其是1.0版本之后,增加了動態預留和對持久化卷的功能,這些都為ES節點數據的持久化存儲提供了強有力的支持。 鑒于這些特性,,我們決定采用Mesos + Marathon + Docker的方式, 構建我們自己的ESAAS服務。
4. 實施過程
下面是我們的ESAAS整個系統的總體結構圖,關于Mesos/Marathon這里不在這里過多的介紹了:
圖 1 總體結構圖
從總體結構圖中可以看到首先我們看看如何解決Quota分配和集群隔離問題。可以看到, 整個系統分了好多層, 最底層是一個一個的物理機, 這些物理機由上層的Mesos來管理,他們形成一個集群, 也就是一個資源池,Mesos管理著這些資源,包括cpu, 內存,磁盤和端口等。 Mesos上層是Marathon框架,它主要負責任務的調度。也是ESAAS系統最重要的一層,我們稱之為Root Marathon。, 它主要負責調度更上層的Sub Marathon。這樣做主要是考慮功能的隔離,以及為了后續的Quota等功能的實現。
在構建ESAAS的過程中,我們的工作主要圍繞著以下幾個核心的問題:
● 數據可靠性
● Quota分配
● 集群的隔離
● 服務發現
● 監控與報警
● 部署自動化
● ESAAS Console
如何解決Quota分配
Mesos中有Role(角色)的概念,Role是資源限制的最小單位, Mesos根據它來設定每個特定的Role能使用的最大資源,這其實就是限定資源的Quota。我們借助這一特性實現資源的隔離和分配。這里不僅涉及了靜態資源分配,即通過--resources配置,還涉及到了Mesos 0.27中的動態申請Quota功能。
Root Marathon不做資源的限制,即可以使用整個集群的資源。同時,我們會為每個Role都劃定Quota,并用Root-Marathon為每個Role構建一個獨享的Marathon,即圖2中的Sub Marathon。 , 這樣,每個Role可以使用自有的Sub Marathon在Quota限定范圍內的資源,并且享有邏輯上隔離的命名空間和路由策略,更不用擔心某一個Sub Marathon無節制的使用整個平臺的資源。
圖 2 Marathon嵌套示意圖
如何解決集群的隔離
使用嵌套結構來組織Marathon,除了方便根據Role來設置Quota之外,還有一個作用就是實現ES集群邏輯上的隔離。
我們的每一個Sub Marathon都負責維護了一個或多個完整的ES集群,為每一個Sub Marathon分配一個Role/Quota, 等同于每一個ES集群的group動態劃分資源池,系統的ES Group根據業務線來劃定。group與ES集群的關系是一對多的關系,即每個ES group內包含了多套相互隔離的ES集群。ESAAS最終的服務單元就是這一個個Sub Marathon所承載的Elasticsearch clusters group服務。基于Mesos + Marathon體系, 我們所有的組件都是跑在Docker容器里面。
圖 3 線上Marathon環境截圖
圖4是ESAAS系統中一個單臺物理機的結構快照。一臺物理機運行多個ES節點實例,使用不同的端口來隔離同一臺機器上的不同ES集群的實例間的通訊,利用Mesos的持久化卷隔離ES落地的數據。除了ES節點之外,機器上還有一些其他組件, 這些組件和ES共同協作來保證服務的穩定可靠。
圖4 物理機部署模塊示意圖
每一個Sub Marathon內部結構如圖5,包含了ES masternode、ES slavenode、bamboo/haproxy、es2graphite、pyadvisor以及StatsD,構成一個完整的集群模塊。
圖5 ESAAS集群模塊關系圖
如何解決服務發現
下面看看我們是如何解決Elasticsearch集群內服務發現問題的。首先說明一下我們選擇了ES的單播模式來構建集群,主要的目的是降低建群之間相互污染的可能性。我們的ES集群的標準配置是6個ES節點:3個masternode節點, 3個datanode節點。他們會作為兩個不同的Marathon APP存在。每一個APP有三個task, 每個task是一個ES node(無論是masternode還是datanode) 。之所以要將這兩種節點分開來作為兩個APP,主要的原因就是, 這兩種角色的節點會有不同的配置方式和不同的資源分配方式,master節點不做數據存儲,起到集群HA的作用, 所以我們為它分配了最小的cpu和內存資源。 datanode節點真正的存儲數據在配置上,也會有不同, 因此將這兩種節點分開來,方便管理。
兩種類型的ES節點,是通過bamboo + haproxy相互連接成一個集群的。bamboo是一個用于服務發現的開源工具,能根據Marathon的callback信息動態的reload Haproxy,從而實現服務自動發現。它的基礎原理就是注冊Marathon的callback來獲取Marathon事件。ES節點間的互連之所以要使用bamboo來進行服務發現的主要原因就是我們無法在節點被發布前預知節點被分配到了哪些Mesos Slave機器上。 因為節點的調度結果是由Marathon內部根據Slave可用的cpu,內存,端口等資源數量來決策,無法提前預知。這樣就有一個問題,在發布的時候, 三個masternode之間就無法預知對方的地址。為了解決這個問題, 我們使用bamboo + haproxy來動態的發現masternode的地址實現節點之間的互連。
bamboo + haproxy能做到動態服務發現, 那么他和ES節點之間是怎么協作的呢 ?為了能保證集群節點之間正確的互連, 在ES兩種類型的節點沒有部署之前,首先需要部署一個bamboo + haproxy工具用作服務發現。 bamboo會注冊Marathon callback從而監聽Marathon事件。haproxy也會啟動并監聽一個前端端口。在開始部署masternode的時候,由于有新的APP部署,Marathon就會產生一個事件從而callback所有注冊進入Marathon的接口,這時bamboo會根據這個callback調用Marathon api, 根據配置的規則去獲取指定APP中task的機器和端口信息,也就是masternode的機器和端口信息。這些信息得到之后,bamboo會去修改Haproxy的配置并reload Haproxy,將task機器和端口信息做為后端Server,使得Haproxy前端所監聽的端口可以正確的轉發到后端這些機器的這些端口上去。這里,我們使用4層tcp協議。
圖6 端口轉發示意圖
至此,服務發現的過程就已經完成。在bamboo服務部署完成之后,haproxy服務就已經啟動,所以,haproxy這個前端的端口是一直存在的。因此,在masternode啟動的時候,可以先調用Marathon api獲取到Haproxy這個前端的端口和機器信息,并將這個信息寫入masternode的配置文件中,節點在啟動的時候,就會以單播的方式去連接這個Haproxy的前端端口, 進而將請求轉發到后端三臺masternode節點上去。 可以看到,masternode的互相發現其實就是通過Haproxy再去連接自己,是一個環形連接。對于datanode,也是同樣的方式, 惟一區別于masternode的就是datanode不會去自己連接自己。
如何保證數據可靠性
ES是一個帶狀態的服務,在軟件層面提供了數據的冗余及分片,理論上是允許集群中部分節點下線并繼續對外提供服務。我們要盡量保證底層的Mesos/Marathon能夠充分利用ES的數據冗余能力,提高數據可靠性。低版本的Mesos/Marathon不支持動態預留和持久化卷,數據只能通過mount volume的方式駐留在宿主機的某個目錄,不同的集群間的數據目錄需要人工管理。而且默認的failover功能可能會導致datanode被調度到其他機器上,變成空節點。現在Mesos的動態預留和持久化卷功能可以解決這一問題,保證容器與數據綁定,“釘”在某個節點,保證實例重啟后直接本地恢復數據。同時為了盡最大可能保證數據不會丟失,我們做了以下規定:
● 每個索引至少有一個副本(index.number_of_replicas >= 1);
● 每個宿主上同一個集群的節點等于replica的個數。如果某個集群A的配置為index.number_of_replicas= 2,那么每個宿主上可以為A啟動2個節點實例
● index.routing.allocation.total_shards_per_node=2,禁止多個主shard集中同一實例。
除了數據層面的多份冗余外,我們還默認提供了snapshot服務, 業務線可以選擇定時/手工snapshot集群的數據到HDFS上。最后就是集群級的冗余,ESAAS允許申請熱備集群,數據雙寫到在線/熱備集群中,當在線集群出現故障時切換到熱備集群提供服務。
監控與報警
從上面可以看到,masternode,datanode,bamboo + haproxy這三個是組成一個ES集群必不可少的組件。 除此之外,我們還有一些用于監控的組件,如Sub Marathon結構圖所示的那樣, 我們提供了兩個維度的監控,一個是集群維度的監控,一個是容器維度的監控。
我們選擇了es2graphite(一個py腳本)來收集ES集群指標,,其主要原理就是通過ES的api獲取ES內部的各項指標,將收集的指標打到后端的監控系統——watcher中(watcher是Qunar OPSDEV基于graphite+grafana+nagios等開發的一套監控/報警系統),最后通過監控系統的指標看板展示集群當前狀態。如圖7所示,為某個ES集群在watcher上的監控看板:
圖7 ES監控截圖
為了監控容器的運行狀態,我們自主開發了容器指標收集工具——pyadvisor。pyadvisor會收集容器的cpu, 內存和io等信息,并將其發往后端的監控系統watcher。圖8為容器的監控看板截圖
圖8 容器監控截圖
監控的重要性不言而喻,它就相當于維護者的眼睛,通過這些監控,我們可以定位集群問題。除了監控,我們還提供了兩個集群的基礎報警,一個是集群狀態報警,一個是集群的gc時間報警。ES集群狀態有green,yellow,red三種狀態。我們的報警條件是非green就報警,這樣盡早發現可能出現的異常情況。GC時間報警指的是一分鐘內集群節點Full GC的時間,報警閾值我們會根據集群的使用場景去調節。GC時間指標在一定程度上往往標識著集群當前的health程度,過高的GC時間可能導致集群停止對外界請求的一切響應, 這對于一個線上服務往往是致命的。
自動化部署
ESAAS系統設計初衷有快速構建的要求,否則我們避免不了繁重的人力成本。整個系統的組件幾乎全部依賴于Docker,Docker容器的平臺無關特性使得我們可以提前將環境和可執行體打包成一個鏡像,這在很大程度上節約了我們為部署環境差異而付出的人力成本。快速的構建和發布是一個SaaS系統必須具備的特性。,我們使用jenkins來完成ESAAS系統的構建和發布工作。
對于ESAAS系統來說, 快速的構建就是快速的生成配置,發布就是按照規則順序創建Marathon APP, 圖9是一個Jenkins構建發布的流程圖:
ES集群的發布,由兩個Jenkins任務完成。首先,第一個Jenkins任務會調用集群初始化腳本,生成Marathon和ES的配置,并將這些配置提交Gitlab做歸檔管理。然后,第二個Jenkins任務(如圖10)會通過Gitlab API讀取配置,并使用Marathon API創建集群中的各組件(Marathon APP). 圖10是我們線上一個Jenkins發布任務的截圖(二次開發過的任務面板):
圖10 Jenkins job截圖
ESAAS Console
為了提供一個標準化的ES產品,統一用戶界面,我們模仿ES Cloud開發了ESAAS Console(以下簡稱Console)。ESAAS Console包括了集群概況展示,集群配置,操作日志查詢等功能。
集群概況頁匯總了節點地址,端口信息,配置Git地址,監控地址以及常用插件等信息(如圖11),方便用戶接入ES集群和插件工具的使用。
圖11 ESAAS Console截圖
同時,我們也將配置管理和插件管理整合到了Console中(如圖12所示)。用戶可以通過使用ES API修改集群配置。另外,ES插件多種多樣,相互搭配使用可以提高集群管理的靈活度,我們提供一鍵安裝插件功能,免去了手動為每一個ES節點安裝插件的麻煩。集群配置頁還提供了Kibana一鍵安裝功能(Kibana是一個ES數據可視化看板)。這些功能都大大降低了用戶使用ES的成本。
圖 12 ESAAS Console 截圖
所有對集群的操作都會記錄下來(如圖13),方便回溯問題。我們這些操作信息后端存儲也是使用的ESAAS, 一定程度的做到了服務閉環。
圖13 ESAAS Console截圖
5. 總結
截止到寫這篇文章時, ESAAS已經穩定運行了半年有余,為業務線提供了44組ES集群,涵蓋了離線/熱備/在線集群。并且和我們OPS提供的實時日志系統完成了功能的整合,支持線上日志的導入,雙寫以及ETL等,為業務線提供數據/服務閉環的平臺。目前ESAAS服務的幾個規模指標如下:
● ESAAS集群機器數量: 77臺服務器
● Datanode數據節點機器數量: 66臺服務器
● 當前托管的集群數量: 44個集群
● 當前數據存儲總量量: 120TB左右
● 當前覆蓋業務線: 30 個
● 最大的集群數據量: 25.6 T
遇到的問題和解決辦法
(1)Mesos Role 不能動態的創建
Mesos的Role不能動態的創建, 而我們ESAAS的資源隔離是根據Mesos的Role來完成. 我們提前創建了Role來解決這一問題
(2)Mesos slave 重啟之后重新加入集群, 原先跑在這臺 slave 上的task 將不可恢復
Mesos Slave節點由于某些原因機器需要重新啟動的時候, Mesos會將機器識別為新的Slave, 這就導致了機器重啟之后, 原來跑在該節點上的task不可恢復. 我們在研究了Mesos內部機制之后解決了這一問題,即持久化boot_id文件。
下一步計劃
我們的ESAAS系統還處于探索階段,仍然存在不少待解決的問題。比如:
● ESAAS服務的計費
● ES和平臺日志的收集,
● 獨立ES集群間的數據遷移服務
● 獨立ES集群間的IO/CPU優先級管理
來自:http://mp.weixin.qq.com/s?__biz=MzA3NDcyMTQyNQ==&mid=2649256130&idx=1&sn=3a341cc0f5d88662ac5c271a8a73ada0&chksm=8767a13cb010282a710ebe8ba4ea443eae72cafd211c9042d59d172e1eb44b866b898a7e90d3&scene=0