去哪兒網基於Mesos和Docker構建私有雲服務的實踐
【編者的話】本文深入介紹了去哪兒網利用Mesos和Docker構建私有雲服務的全過程,分享了從無狀態應用向有狀態應用逐步過度的經驗與心得。
平台概覽
2014年下半年左右,去哪兒完成了有關構建私有雲服務的技術調研,並最終拍定了Docker/Mesos這一方案。下圖1展示了去哪兒數據平台的整體架構:
1.jpg
圖1:去哪兒數據平台的整體架構
該平台目前已實現了如下多項功能:
每天處理約340億/25TB的數據;
90%的數據在100ms內完成處理;
最長3h/24h的數據回放;
私有的Elasticsearch Cloud;
自動化監控與報警。
為什麼選擇Docker/Mesos
目前為止,這個數據平台可以說是公司整個流數據的主要出入口,包括私有的Elasticsearch Cloud和監控報警之類的數據。那麼為什麼選擇Docker/Mesos?
選擇Docker有兩大原因。第一個是打包:對於運維來講,業務打完包之後,每天麵對的是用腳本分發到機器上時所出現的各種問題。業務包是一個比較上層的話題,這裏不做深入的討論,這裏講的“打包”指軟件的Runtime層。如果用Docker的打包機製,把最容易出現問題的Runtime包裝成鏡像並放在registry裏,需要的時候拿出來,那麼整個平台最多隻執行一個遠程腳本就可以了,這是團隊最看好的一個特性。第二個是運維:Docker取消了依賴限製,隻要構建一個虛擬環境或一個Runtime的鏡像,就可以直接拉取到服務器上並啟動相應的程序。此外Docker在清理上也較為簡單,不需要考慮環境卸載不幹淨等問題。
以常見的計算框架來說,它們本質上仍然屬於運行在其上的Job的Runtime。綜合上述情況,團隊選擇針對Runtime去打包。
選擇Mesos是因為它足夠簡單和穩定,而且擁有較成熟的調度框架。Mesos的簡單體現在,與Kubernetes相比其所有功能都處於劣勢,甚至會發現它本身都是不支持服務的,用戶需要進行二次開發來滿足實際要求,包括網絡層。不過,這也恰好是它的強項。Mesos本身提供了很多SDN接口,或者是有模塊加載機製,可以做自定義修改,平台定製功能比較強。所以用Mesos的方案,需要考慮團隊是否可以Hold住整個開發過程。
從框架層麵來看,Marathon可以支撐一部分長期運行的服務,Chronos則側重於定時任務/批處理。
以下圖2是Mesos的一個簡單結構圖:
2.jpg
圖2:Mesos結構
數據平台的最終目標架構如下圖3所示:
3.jpg
圖3:平台目標
組件容器化與部署
組件的容器化分為JVM容器化和Mesos容器化。JVM容器化需要注意以下幾方麵:
潛在創建文件的配置都要注意
java.io.tmpdir
-XX:HeapDumpPath
-Xloggc
-Xloggc會記錄GC的信息到製定的文件中。現在很少有直接用XLoggc配置的了(已經用MXBean方式替代了)。如果有比較老的程序是通過-Xloggc打印GC日誌的話,那麼要額外掛載volume到容器內。
時區與編碼
–env TZ=Asia/Shanghai
–volume /etc/localtime:/etc/localtime:ro
–env JAVA_TOOL_OPTIONS=”-Dfile.encoding=UTF-8 -Duser.timezone=PRC
時區是另一個注意點。上麵所列的三種不同的方法都可以達到目的,其中第一/三個可以寫在Dockerfile裏,也可以在docker run時通過–env傳入。第二種隻在docker run時通過volume方式掛載。另外,第三種額外設置了字符集編碼,推薦使用此方式。
主動設置heap
防止ergonomics亂算內存
這是Docker內部實現的問題。即使給Docker設置內存,容器內通過free命令看到的內存和宿主機的內存是一樣的。而JVM為了使用方便,會默認設置一個人機功能會根據當前機器的內存計算一個堆大小,如果我們不主動設置JVM堆內存的話,很有可能計算出一個超過 Memory Cgroup限製的內存,啟動就宕掉,所以需要注意在啟動時就把內存設置好。
CMS收集器要調整並行度
-XX:ParallelGCThreads=cpus
-XX:ConcGCThreads=cpus/2
CMS是常見的收集器,它設置並行度的時候是取機器的核數來計算的。如果給容器分配2個CPU,JVM仍然按照宿主機的核數初始化這些線程數量,GC的回收效率會降低。想規避這個問題有兩點,第一點是掛載假的Proc文件係統,比如Lxcfs。第二種是使用類似Hyper的基於Hypervisor的容器。
Mesos容器化要求關注兩類參數:配置參數和run參數。
需要關注的配置參數
MESOS_systemd_enable_support
MESOS_docker_mesos_image
MESOS_docker_socket
GLOG_max_log_size
GLOG_stop_logging_if_full_disk
Mesos是配置參數最多的。在物理機上,Mesos默認使用係統的Systemd管理任務,如果把Mesos通過Docker run的方式啟動起來,用戶就要關systemd_Enable_support,防止Mesos Slave拉取容器運行時數據造成混亂。
第二個是Docker_Mesos_Image,這個配置告訴Mesos Slave,當前是運行在容器內的。在物理機環境下,Mesos Slave進程宕掉重啟,、就會根據executor進程/容器的名字做recovery動作。但是在容器內,宕機後executor全部回收了,重啟容器,Slave認為是一個新環境,跳過覆蓋動作並自動下發任務,所以任務有可能會發重。
Docker_Socket會告訴Mesos,Docker指定的遠端地址或本地文件,是默認掛到Mesos容器裏的。用戶如果直接執行文件,會導致文件錯誤,消息調取失敗。這個時候推薦一個簡單的辦法:把當前物理機的目錄掛到容器中並單獨命名,相當於在容器內直接訪問整個物理機的路徑,再重新指定它的地址,這樣每次一有變動Mesos就能夠發現,做自己的指令。
後麵兩個是Mesos Logging配置,調整生成logging文件的一些行為。
需要關注的run參數
–pid=host
–privileged
–net=host (optional)
root user
啟動Slave容器的時候最好不加Pid Namespace,因為容器內Pid=1的進程一般都是你的應用程序,易導致子進程都無法回收,或者采用tini一類的進程啟動應用達到相同的目的。–privileged和root user主要是針對Mesos的持久化卷功能,否則無法mount到容器內,–net=host是出於網絡效率的考慮,畢竟源生的bridge模式效率比較低。
4.jpg
圖4:去哪兒數據平台部署流程圖
上圖4就是去哪兒數據平台部署的流程圖。
基於Marathon的Streaming調度
拿Spark on Mesos記錄子,即使是基於Spark的Marathon調度,也需要用戶開發一個Frameworks。上生產需要很多代碼,團隊之前代碼加到將近一千,用來專門解決Spark運行在Master中的問題,但是其中一個軟件經常跑到Master,對每一個框架寫重複性代碼,而且內部邏輯很難複用,所以團隊考慮把上層的東西全都跑在一個統一框架裏,例如後麵的運維和擴容,都針對這一個框架做就可以了。團隊最終選擇了Marathon,把Spark作為Marathon的一個任務發下去,讓Spark在Marathon裏做分發。
除去提供維標準化和自動化外,基於Spark的Marathon還可以解決Mesos-Dispatcher的一些問題:
配置不能正確同步;這一塊更新頻率特別慢,默認速度也很慢,所以需要自己來維護一個版本。第一個配置不能正確同步,需要設置一些參數信息、Spark內核核數及內損之類,這裏它隻會選擇性地抽取部分配置發下去。
基於attributes的過濾功能缺失;對於現在的環境,所設置的Attributes過濾功能明顯缺失,不管機器是否專用或有沒有特殊配置,上來就發,很容易占滿ES的機器。
按role/principal接入Mesos;針對不同的業務線做資源配比時,無法對應不同的角色去接入Mesos。
不能re-registery;框架本身不能重注冊,如果框架跑到一半掛掉了,重啟之後之前的任務就直接忽略不管,需要手工Kill掉這個框架。
不能動態擴容executor。最後是不能擴容、動態調整,臨時改動的話隻能重發任務。
整個過程比較簡單,如下圖5所示:
5.jpg
圖5:替代Spark Mesos Dispatcher
不過還是有一些問題存在:
Checkpoint & Block
動態預留 & 持久化卷
setJars
清理無效的卷
關於Checkpoint&Block,通過動態預留的功能可以把這個任務直接“釘死”在這台機器上,如果它掛的話可以直接在原機器上重啟,並掛載volume繼續工作。如果不用它預留的話,可能調度到其他機器上,找不到數據Block,造成數據的丟失或者重複處理。
持久化卷是Mesos提供的功能,需要考慮它的數據永存,Mesos提供了一種方案:把本地磁盤升級成一個目錄,把這個轉移到Docker裏。每次寫數據到本地時,能直接通過持久化卷來維護,免去手工維護的成本。但它目前有一個問題,如果任務已被回收,它持久化卷的數據是不會自己刪掉的,需要寫一個腳本定時輪巡並對應刪掉。
臨時文件
java.io.tmpdir=/mnt/mesos/sandbox
spark.local.dir=/mnt/mesos/sandbox
如果使用持久化卷,需要修改這兩個配置,把這一些臨時文件寫進去,比如shuffle文件等。如果配置持久化卷的話,用戶也可以寫持久化卷的路徑。
Coarse-Grained
Spark有兩種資源調度模式:細粒度和粗粒度。目前已經不太推薦細粒度了,考慮到細粒度會盡可能的把所有資源占滿,容易導致Mesos資源被耗盡,所以這個時候更傾向選擇粗粒度模式。
6.jpg
圖6:Storm on Marathon
上圖6展示了基於Storm的Marathon調度,Flink也是如此。結合線上的運維和debug,需要注意以下幾方麵:
源生Web Console
隨機端口
OpenResty配合泛域名
默認源生Web Console,前端配置轉發,直接訪問固定域名。
Filebeat + Kafka + ELK
多版本追溯
日常排錯
異常監控
大部分WebUI上看到的都是目前內部的數據處理情況,可以通過ELK查詢信息。如果任務曾經運行在不同版本的Spark上,可以把多版本的日誌都追蹤起來,包括日常、問題監控等,直接拿來使用。
Metrics
第三個需要注意的就是指標。比如Spark ,需要配合Metrics把數據源打出來就行。
ELK on Mesos
目前平台已有近50個集群,約100TB+業務數據量,高峰期1.2k QPS以及約110個節點,Elasticsearch需求逐步增多。
7.jpg
圖7:ELK on Mesos
上圖7是ELK on Mesos結構圖,也是團隊的無奈之選。因為Mesos還暫時不支持multi-role framework功能,所以選擇了這種折中的方式來做。在一個Marathon裏,根據業務線設置好Quota後,用業務線重新發一個新的Marathon接入進去。對於多租戶來講,可以利用Kubernetes做後續的資源管控和資源申請。
部署ES以後,有一個關於服務發現的問題,可以去注冊一個callback,Marathon會返回信息,解析出master/slave進程所在的機器和端口,配合修改Haproxy做一層轉發,相當於把後端整個TCP的連接都做一個通路。ES跟Spark不完全相同,Spark傳輸本身流量就比較大,而ES啟動時需要主動聯係Master地址,再通過Master獲取相應集群,後麵再做P2P,流量比較低,也不是一個長鏈接。
監控與運維
這部分包括了Streaming監控指標與報警、容器監控指標與報警兩方麵。
Streaming監控指標與報警
Streaming監控含拓撲監控和業務監控兩部分。
Streaming拓撲監控
業務監控
Kafka Topic Lag
處理延遲mean90/upper90
Spark scheduler delay/process delay
Search Count/Message Count
Reject/Exception
JVM
拓撲監控包括數據源和整個拓撲流程,需要用戶自己去整理和構建,更新的時候就能夠知道這個東西依賴誰、是否依賴線上服務,如果中途停的話會造成機器故障。業務監控的話,第一個就是Topic Lag,Topic Lag每一個波動都是不一樣的,用這種方式監控會頻繁報警,90%的中位數都是落在80—100毫秒範圍內,就可以監控到整個範圍。
容器監控指標與報警
容器監控上關注以下三方麵:
Google cAdvisor足夠有效
mount rootfs可能導致容器刪除失敗 #771
–docker_only
–docker_env_metadata_whitelist
Statsd + Watcher
基於Graphite的千萬級指標監控平台
Nagios
容器這一塊比較簡單,利用Docker並配合Mesos,再把Marathon的ID抓取出來就可以了。我們這邊在實踐的過程發現一個問題,因為Statsd Watcher容易出現問題,你直接用Docker的時候它會報一些錯誤出來,這個問題就是Statsd Watcher把路徑給掛了的原因。目前我們平台就曾遇到過一次,社區裏麵也有人曝,不過複現率比較低。用的時候如果發現這個問題把Statsd Watcher直接停掉就好。指標的話,每台機器上放一個statsd再發一個後台的Worker,報警平台也是這個。
其實針對Docker監控的話,還是存在著一些問題:
基礎監控壓力
數據膨脹
垃圾指標增多
大量的通配符導致數據庫壓力較高
單個任務的容器生命周期
發布
擴容
異常退出
首先主要是監控係統壓力比較大。原來監控虛擬機時都是針對每一個虛擬機的,隻要虛擬機不刪的話是長期匯報,指標名固定,但在容器中這個東西一直在變,它在這套體係下用指標並在本地之外建一個目錄存文件,所以在這種存儲機製下去存容器的指標不合適。主要問題是數據膨脹比較厲害,可能一個容器會起名,起名多次之後,在Graphite那邊對應了有十多個指標,像這種都是預生成的監控文件。比如說定義每一秒鍾一個數據點,要保存一年,這個時候它就會根據每年有多少秒生成一個RRD文件放那兒。這部分指標如果按照現有標準的話,可能容器的生命周期僅有幾天時間,不適用這種機製。測試相同的指標量,公司存儲的方式相對來說比Graphite好一點。因為Graphite是基於文件係統來做的,第一個優化指標名,目錄要轉存到數據庫裏做一些索引加速和查詢,但是因為容器這邊相對通配符比較多,不能直接得知具體對應的ID,隻能通配符查詢做聚合。因為長期的通配符在字符串的索引上還是易於使用的,所以現在算是折中的做法,把一些常用的查詢結果、目錄放到裏邊。
另一個是容器的生命周期。可以做一些審計或者變更的版本,在Mesos層麵基於Marathon去監控,發現這些狀態後打上標記:當前是哪一個容器或者哪一個TASK出了問題,對應擴容和記錄下來。還有Docker自己的問題,這樣後麵做整個記錄時會有一份相對比較完整的TASK-ID。
原文鏈接:基於Mesos/Docker構建數據處理平台
作者簡介:徐磊,去哪兒網平台事業部運維開發工程師,2015年加入去哪兒網,負責實時日誌相關的開發與運維工作。有多年電信、雲計算行業經驗,曾供職於紅帽中國。 本文來自徐磊在CCTC 2017上的演講整理。
最後更新:2017-09-11 22:03:48