1. 程式人生 > >攜程:我們是如何利用容器實現快速彈性伸縮的?

攜程:我們是如何利用容器實現快速彈性伸縮的?

作者簡介

吳毅挺

攜程系統研發部高階總監

2012年加入攜程,從零組建攜程雲平臺團隊,目前負責攜程私有云、虛擬桌面雲、網站應用持續交付等研發。

一、線上旅遊與彈性需求

近年來隨著大眾旅遊消費的火熱,攜程的業務每年呈高速增長,2016年Q4財報顯示攜程2016年全年營業收入同比增長76%,交通票務營業收入同比增長98%,酒店預訂營業收入同比增長56%,其他BU也有大幅增長,預計2018年攜程的 GMV 將突破10000億,並在2021年突破2萬億。

我們開發的私有云和持續交付平臺為攜程超過 20 個 BU/SBU 服務,為了同步支撐業務的高速發展,我們也需要不斷的技術革新,持續提升攜程運營、研發的效率,縮短產品從 idea 到交付使用者的時間。

旅遊出行的特點是季節性的,在平時流量都比較低,但節假日前一天流量會突然增高很多。因此每到節假日就會面臨成「倍」增長的擴容需求。圖 1 中可以明顯的看到流量的起伏情況。

圖 1

一般情況,臨時擴容的需求較多,縮容會比較少,因為流量一上去之後,在短時間內不會下來,另外一方面,哪怕是流量下來了,由於以往資源申請沒那麼靈活,一般都會佔用資源不釋放,只能通過一些運維手段來監測資源使用情況然後告知業務部門去主動縮容。

攜程目前大部分還是虛擬機器,我們想縮短提前擴容的時間,以目前的虛擬機器擴容方式,單個虛擬機器擴容(從分配資源、排程、網路、os基礎環境、應用部署等)至少是十分鐘級別的。

如果是每次擴容上千臺的話,確實需要一定的時間,而且還得看看有無足夠的資源,比如 1 月 1 日的流量提前一週就擴容好,但這不夠靈活,業務流量下來以後需要縮容,目前的速度還是不夠快。

針對這一場景,我們需要解決的問題是,能有更快的方案嗎?如何能夠做到更快速的彈性伸縮滿足業務的需求?答案是利用容器。

圖2

再舉個例子,攜程有個深度學習的小詩機專案,將訓練好的模型,對外提供服務,使用者只要上傳照片,後臺的 AI 機器人就會根據照片中的內容自動寫詩,這對於現行都市詞窮一族來說,瞬間提升了意境,蠻有意思的。

該專案希望在春節前上線,需要緊急擴容 1000 核,以滿足春節期間大流量的需求,春節過後立馬就可以縮容 90% 的資源。

目前我們通過容器可以做到 1000 核的資源,5 分鐘內完成 150 個容器例項的擴容,而且這還是 API 同步建立的速度,我們正在優化成非同步的方式,相信後續提高併發能力後,速度還可以得到大大的提升。

其實攜程的容器化已經進行一年多了,容器給我們最大的感覺是看起來簡單,但要做好很難,原理不是很複雜,但是要利用這個技術做出一個產品或服務,中間有非常多的細節需要完善,比如如何做到使用者體驗更好的 UI 視覺化、如何做到灰度釋出、容器監控、容器基礎映象版本管理等等。

二、攜程容器雲定位

攜程容器雲定位有以下 4 點:

1、打造極致的秒級持續交付體驗,服務20+BU

秒級意味著所有的擴容、縮容、回滾全部是秒級的,做到秒級是很難的,有很多需要突破的地方。比如,高速的映象下發系統;高效的排程系統,穩定的容器服務,高度自動化的網路服務。

2、提升資源利用率

為了提高伺服器資源利用率,我們採取賬單的形式,督促業務線提高資源利用率,優化程式碼架構。我們對採集到的實時監控資料進行統計分析,按照 CPU、記憶體、儲存、網路等多個緯度,按月計費,每個月會將賬單發給業務線的 CTO。

3、元件服務化(mysql/kv/mq/…)or PaaS 化

應用所需要依賴的很多元件能夠變成服務化,AWS 或者阿里雲也做了很多這種服務。攜程內部也在嘗試把一些公共元件服務化,例如,MySQL,Redis,RabbitMQ 等。

拿 MySQL 為例,我們讓使用者可以在測試環境快速部署 MySQL 例項,並且可以選擇性的將測試資料灌入。新建的 MySQL 例項也會自動在資料訪問中介軟體中完成註冊,方便開發人員、測試人員快速搭建測試環境和測試資料。

4、從自動化到一定程度智慧化

從自動化到一定程度智慧化指的是基礎設施變得更智慧,比如能夠具備一定的自我修復能力,如果是從上游到下游的一整套服務都具備智慧化修復能力的話,這是一個非常大的突破,對於提升運營效率和故障恢復速度至關重要;

三、容器部署基本原則

  • 單容器單應用
  • 單容器單 IP,可路由的 IP
  • 容器映象釋出
  • immutable infrastructure
  • 容器內部只執行 App,所有 agent 部署在host 層面-包括監控/ES/salt等agent

以上是攜程容器部署基本原則,看起來很容易,卻是我們很長時間實踐經驗的總結。

比如單容器單應用這個原則,歷史原因我們有混合部署的情況,單個vm部署多個應用,運維的複雜度會上升很多,比如:應用之間如何做到更好的資源隔離?釋出如何避免相互之間的衝突?等等。

使得我們的運維工具、釋出工具變得格外複雜,開發、測試的持續交付效率也受到極大影響;

容器映象釋出也是我們做的一個比較大的突破,過去是程式碼編譯成可釋出的包,直接部署到 vm 內部,而現在是編譯時直接生成容器的映象,不同環境其實部署的是同一個映象。

並且不允許部署之後單獨登陸進行配置修改,通過這種方式做到 immutable infrastructure ,保證開發、測試、生產環境的一致性,同時也保障了自動化擴容、縮容快速高效的進行。

是否在容器內部也執行各種運維 agent 也是我們經過實踐確定下來的;我們希望容器儘量簡單,儘可能只包含執行的應用本身,此外將所有的 agent 合併到 host 層面,也能在很大程度上提升伺服器資源利用率,agent數量下降一到兩個數量級;但配套的管理工具(eg: salt) 需要做一次升級改造才能適配新的模式;

四、容器編排選型&取捨

1、OpenStack

攜程除了容器之外的東西都是用 OpenStack 來管理的,OpenStack 可以用一個模組(nova-docker)來管理容器,攜程在 OpenStack 方面有多年的二次開發技術積累、也大規模的部署運維經驗,但最終沒有選用 OpenStack,因為 OpenStack 整體過於複雜,排程效率也比較低,API 排程是 10 秒以上,可以進行優化,但我們覺得優化代價太大,OpenStack 整體的複雜度也很高;

我們早期的胖容器(把容器當 vm 來使用,做程式碼包釋出)的確是用 OpenStack 來做的,原因是我們希望把注意力放在容器本身,以最低的代價將容器先用起來,積累開發、運維經驗;

而到了瘦容器階段(基於容器映象做釋出),我們發現 OpenStack 整體的設計理念從本質上講還是為虛擬機器隔離粒度的虛擬化方案設計的,而容器本身與 vm 其實差別很大,玩法也相去甚遠, 於是我們對 Mesos/K8s 進行評估;

回顧我們的容器排程探索之旅,基本上可以用以下三個階段來總結:

  1. 第一階段,需要最快的使用起來,用最熟悉的技術來解決部署和排程。
  2. 第二階段有新的需求,引入 mesos 和 chronos,提供分散式 cron  job 排程。
  3. 第三階段是使用 Python 重新實現 chronos, 並且單獨實現了 CExecutor 等元件。

    圖3

    OpenStack 用於管理 bm/vm 是很合適的,並且在網路方面有很成熟的支援,無論是 vlan+OVS 還是最新的SDN 都能適配,尤其各大廠商的支援力度都很大;這也是為什麼我們雖然不用 OpenStack 排程容器,但容器的網路其實還是用 neutron 來管理的;

    2、K8S

    K8S 有很多很先進的設計理念,比如有 replication  controller/Pod/Yaml 配置管理等,但這些理念在攜程都很難落地,因為跟現有的運維模式、釋出流程有較大的衝突。

    而且當前還缺乏大規模部署案例,網路尚無比較成熟的方案, 例如 L4/L7 負載均衡; 而在攜程 L4/L7 服務已經比較成熟穩定, 並且與我們現有的釋出系統Tars整合得非常好;

    3、Mesos

    Mesos 和 K8S 解決問題的思路是不一樣的,基於 Mesos 我們可以非常容易的開發出適合我們場景的排程框架,並且非常容易和我們現有的運維基礎服務對接整合;包括 L4/L7 負載均衡、釋出系統等;

    圖4

    五、容器網路選型

    • Neutron+OVS+VLan

    -DPDK

    -https硬體加速

    -單容器單IP,可路由

    • vxlan+SDN  controller

    -vxlan offload TOR 解封裝

    Neutron+OVS+VLan,這個模式非常穩定,對於網路管理也是非常的透明的。這也是攜程的選擇,現在的網路無論是胖容器還是容器輕量釋出都用這種模式。我們也在測試 DPDK 和 https 硬體加速的效果。

    我們也評估過類似 flannel 的網路,要求每個物理機獨立網段,通過這個特性來做路由;非常不靈活的一點是容器如果遷移到另外一臺物理機就需要換IP,無法滿足我們的需求;

    接下來會走 vxlan+ 基於BGP EVPN協議自研輕量級 SDN controller 的路線,vxlan offload TOR 解封裝提高效能;對於 OpenStack 可見的還是大二層網路(vlan), 而實際上是通過 underlay 的三層路由進行轉發;openstack與我們的控制器能實現元資料的一致性;關於這塊,後續我們也會有相應的分享單獨進行探討;

    如何配置容器網路

    圖 5

    如圖 5 用dwait 和 dresponse,基於 Go 開發,dwait 會通過 unix socket 請求外部服務dresponse 做容器的初始化。當這個容器起來的時候,它的網路沒有那麼獨立,在 Docker 裡面是需要依賴外部提供資訊的,所以需要知道哪個網段或者說建立的 neutronport 再配置到 Docker 容器內。這樣做後就不會有網路丟失的情況。

    六、Docker遇到的問題

    接下來分享一下我們碰到的一些比較經典的Docker/Mesos相關的問題

    1、Docker Issue

    圖 6

    在我們嘗試使用 Chronos 跑 cronjob 時,由於我們的 Job 執行頻率非常高,導致物理機上出現非常頻繁地容器建立和銷燬,容器的建立和銷燬比單個程序的建立和銷燬代價大,會產生很多次核心的呼叫,磁碟的分配銷燬,這對核心是一個非常大的壓力考驗。

    我們在實際操作過程中就遇到了一個 bug,如圖 6 這臺機器逐步失去響應,產生了 kernel soft lockup,慢慢的導致所有程序都死鎖了,最終物理機重啟了。為了避免頻繁建立銷燬容器,我們沒有在 Chronos 這種一個 task 一個容器的路上繼續走下去,我們自己研發了 mesos framework,改成了一個Job,一個容器的排程方式。

    2、Mesos Issue
    • running 的容器數量較多以後,無法再啟動新的容器kernel.keys.root_maxkeys debian default 200,centos default 1M
    • mesos docker inspect 執行低效,尤其是單機容器數量大
    • MESOS_GC_DELAY:6h 20K->1h
    • MESOS_DOCKER_REMOVE_DELAY 1m
    • docker force pull false
    • API 效能差,功能不完善,獲取非同步 event 困難
    • overall,很穩定,排程效能足夠

    Mesos  效能很穩定,基本上不需要修改 Mesos 程式碼,只需要在我們自己寫的 Framework 進行控制排程,控制如何啟動容器。

    3、CExecutor

    圖7

    1)自定義 CExecutor,Go 語言實現

    • 避免過於頻繁建立刪除容器,帶來的副作用;
    • cpuload 高而且抖動很大;
    • 頻繁啟停容器引發的 docker 和 kernel 的 bug;

    2)task:container

    -1:1 -> N:1

    3)容器持久化

    如圖 7,可以觀察得到前段抖動非常厲害(如果過渡頻繁地建立刪除容器,會帶來非常大的負擔,抖動會非常高),在用 1:1 排程之後就變得平緩了。所以攜程自定義 CExecutorr(Go 語言實現),避免過於頻繁建立刪除容器,帶來的副作用(抖動非常強、load非常高),之後就基本上處於水平線了。

    七、容器監控方案

    1、Mesos 監控

圖 8

圖 9

如圖 8-9 攜程用了很多開源技術,Telegraf、influxdb、Grafana 並做了一些擴充套件來實現 mesos 叢集的監控,採集 mesos-master 狀態、task執行數量、executor 狀態等等,以便當 mesos 叢集出現問題時能第一時間知道整個叢集狀態,進而進行修復。

此外, 我們還從 mesos 排程入手,做了一些應用層的監控,比如: 針對 cron job 型別的應用,讓使用者可以看到 job 應該在什麼時候執行,執行的過程,整個 job 的成功率,job 還有多個例項在跑等;

2、容器監控

圖 10

攜程監控團隊全新開發了一套監控系統 hickwall,也實現了對容器的監控支援;hickwall agent 部署在容器物理機上,通過 docker client 、cgroup等採集容器的執行情況,包括 CPU 、Memory、Disk IO 等常規監控項;

由於容器映象釋出會非常頻繁的建立、刪除容器,因此我們把容器的監控也做成自動發現,由 hickwall agent 發現到新的容器,分析容器的label資訊(比如: appid、版本等)來實現自動註冊監控;在單個容器監控的基礎上,還可以按照應用叢集來聚合顯示整個叢集的監控資訊;

除此之外,攜程還做了各個業務訂單量的監控,比如說今天有多少出票量、酒店間夜數,而我們可以非常精準地根據歷史的資訊預測未來的資料,比如說明天的這個時間點訂單量應該在多少、準確性在 95% 以上,一旦比預估的偏差太大的話,就會告警有異常,它把一個綜合的業務執行健康度提供給業務研發團隊,而不僅僅是單個容器的執行情況。

我們線上也會做一些壓測,比如說這個叢集下面有 10 臺機器,這 10 臺機器的負載均衡權重都是 0.1,我們會把其中一臺調高,看它的吞吐和響應的情況。測出它到了一定極限能提供的 QPS,就可以知道這個叢集還剩多少效能高容量,這個容量就是這個叢集還能承載多大的壓力。

比如說有 25% 的富餘,根據訂單的變化就可以知道還多多少或者還缺多少,這樣就能做到更好的擴縮容排程

目前基於 vm 的應用已經能基於容量規劃和預測實現自動擴容,後續容器的擴縮容也會接入,並且做到更實時的擴容和縮容排程;

此外,容器監控對於攜程創新工場的團隊是很有意義的,這些新孵化的BU對成本控制更嚴格,隨著容器上線,我們能為其提供價效比更高的基礎設施。

八、CDOS Overview

圖 11

CDOS 全稱是 CtripData Center Operating System, 我們希望通過 CDOS 來排程多個數據中心的資源,就好比一個作業系統排程各種資源給各個程序一樣,CDOS 會排程多個數據中心的計算、網路、儲存的資源給到不同的應用,滿足各個應用所需的冗餘度,並且會動態的維持這個冗餘度,一旦出現異常,可以自動嘗試修復,刪除出現問題的容器例項,並部署新的例項;這裡面會涉及到非常多的模組。

如圖 11 最上層是持續交付的釋出系統,這一層是跟應用交付相關的東西,開發人員、測試人員都會用的釋出系統。下面是針對不同執行模式的應用定製的兩套排程管理模組,一個是 cron Job,另一個是 long running service;兩者在管理、部署、釋出方面都有一些差異化;

底層資源分配是用 Mesos 來實現,歷史原因,我們還有大量的服務部署在 windows上, 因而需要同時支援 windows server container 和 docker。長期來看未必是繼續使用 Docker,因為 Docker 太激進了,目前已經有多種 container 實現方式可以選擇。

Windows 容器方面攜程也已經做了一些 POC,實現類似前面提到的 ovs + vlan 的網路接入,遵循單容器單 ip 等原則,並且投入覆蓋了部分的測試環境。

圖中右邊有很多服務,比如 L4/L7 負載均衡、 SOA 的服務,CMS 應用元資料管理、監控、日誌收集、映象管理和推送等模組;

由於我們採用容器映象釋出,要實現秒級交付,對於映象推送速度有很高的要求,當 base image 沒有變化時,只需要把應用新版本 build 出來的 app 層通過我們開發的 Ceph 同步模組推送的多個數據中心;

當 base image 也變更時,情況會更復雜,除了同步到各個資料中心的 Ceph 物件儲存,還需要預先下發到可能用到的 Docker 伺服器,才能以最快的方式啟動容器。

作者:吳毅挺

文章來源微信公眾號:高效運維