達達集團智能彈性伸縮架構的設計與落地實踐

文章來源:架構頭條,嘉賓 | 楊森,編輯 | 李忠良

面對節假日常規促銷、618/ 雙 11 購物節等配送業務訂單量的暴增,達達集團通過智能彈性伸縮架構和精細化的容量管理,有效地做到了業務系統對配送全鏈路履約和服務體驗的保駕護航。在 2021 年 4 月 25 日 ArchSummit 全球架構師峯會(上海站)上,達達集團雲平臺 DevOps & SRE 負責人楊森,發表了以《左手 VM ,右手 Container Serverless ,雲原生智能彈性伸縮架構和實踐》主題分享,希望對正在進行彈性擴縮容的你有所啓發。本文爲演講內容整理。

大家好,我是楊森,2019 年加入達達集團,目前負責 DevOps 和 SRE 兩個團隊,日常工作專注於分佈式系統的穩定性和觀察性以及容量、容器架構等。今天分享的主題是彈性容量。

首先介紹一下達達集團,達達集團去年在納斯達克上市,目前有兩個業務線,達達快送和京東集團的京東到家。去年達達快送共有 11 億的包裹量,每天有數十萬的騎士來配送,配送範圍覆蓋全國 2700 個城市,單日峯值大概近千萬。

1 架構設計——故障驅動

達達決定做彈性擴縮容,是由故障引發的。時間回到 2019 年的 8 月 7 號,那天是七夕節,當時鮮花單量比較多,大概在上午 9:20 的時候,線上已經出現問題,鮮花的派送需要優質的騎士,但是當時優質的騎士不多。在九點多的時候,已經有一定程度的單量積壓。

算法工程師先後擴展了 A、B 算法派單的服務,結果這兩個服務擴容之後,它以高於 N 倍的日常力量請求了 C 服務,結果把 C 服務 CPU 打爆了。

這時候,大家開始聯繫運維,耗費了 19 分鐘運維上線,但是上線之後僅僅擴容了兩臺,並且還失敗了一臺。這個時候,這一臺的失敗是壓死駱駝的最後一根稻草。

整個 C 服務對內、對外的響應都開始變得糟糕,整個 CPU 使用率已經達到了百分之九十幾,業務已經開始受損,接下來運維只能依靠手工擴展了 X 臺,然後又全部失敗了,最後又花了 11 分鐘手動拉起這些服務,這才解決整個問題。整個過程非常糟糕。

痛定思痛,達達決定解決這個問題。這次故障給達達帶來了以下兩方面的思考。

第一,當線上遇到問題的時候,應該擴容哪些服務?什麼時間點?擴容多少?需要多少時間?

第二,對於節日促銷,如果沒有壓測的話,怎樣做容量評估?

顯然如果有了彈性,似乎可以解決這兩個問題。

在這張圖中,黃色這條線是傳統的 IT 採購流程,不過這根線的結果和預期有偏差,常常導致在紅色的圓圈的時候,我們才聯繫去擴容,那個時候業務已經受損了。擴容上去之後,真實業務量下來了,資源又是浪費的。

這顯然並不是大家希望的,我們希望的系統容量是圖中綠色這條線。它可以隨着業務的需求變化,業務量上漲的時候直接擴容上去,業務量下來的時候,可以把成本相應地降低。

2 萬事開頭難——擴縮容初探

從簡單的地方先做起來,是我們的策略。

大概花了一兩個月的時間,我們先進行了擴容。實際過程很簡單,我們很好地利用了 CPU 指標。CPU 打爆了,就觀察 CPU 的指標,觀察每個服務的集羣指標,我們開發了 AutoScaler 的彈性系統。

根據這個指標,達達制定了一些簡單粗暴的規則。例如核心服務,當它的集羣 CPU 水位大於 30%,直接擴容一半,如果它大於 50%,直接擴容一倍,這個方案似乎是可以解決這個問題的。

第一版上線,擴容可以直接自動完成。整個過程非常簡單——輸入、決策與執行。

不過上線第一版之後,我們意外地發現,並不是所有的線上服務都需要立馬擴容,大部分服務的容量是冗餘的,從上圖右下部分,我們可以看到,紅色的部分顯示的縮容比正常的擴容要多。由此可以發現,我們對容量管控還沒有做到精細化。

這樣的做法顯然不足夠好。業務指標方面,僅僅考慮了單個 CPU 的指標,其他指標完全缺失,另外當前這個系統非常不夠靈活,顯然這不是很好的彈性系統。

怎麼樣做的更好?什麼樣的架構是更好的彈性架構呢?先來思考一個問題:我們做彈性擴縮容,爲了解決什麼問題?

實際上我們是因爲線上業務的供需匹配關係發生了變化。就達達具體的場景來講,在同城配送線上的訂單量和當前運力的匹配關係下,同時又要做到對業務穩定性負責;對即時體驗以及對商家履約的保證情況下,底層業務系統、相關組件的容量能否承擔業務的需求。

3 跨界思考——自動駕駛與彈性伸縮

我們在思考彈性伸縮時,常常會類比自動駕駛。汽車自動駕駛的時候,每時每刻路況都不一樣,它需要有很多輸入源,不管是激光雷達、紅外線還是高精地圖,同時它對這些數據進行融合,然後加上自己的決策,所以說自動駕駛系統一定有決策層和執行層。

對於做彈性伸縮,其實也是類似的。CPU 不是唯一的指標,還有其他的指標——單機的 PDS、對業務響應的時長、熔斷報錯日誌等,這些都可以作爲輸入源。感知層是這些數據庫,這些數據庫存儲在線上業務的一些系統;

決策層,AutoScaler 需要對這些數據做融合,然後配置一些規則。

最後是執行層,對於執行層面,我們適配了不同的環境。像自動駕駛一樣,自動駕駛有汽油車、燃油車、混動以及新能源,對應再彈性伸縮的執行層面,有虛機、容器以及 Severless Pod 等,在執行層面,我們做了很多適配的工作。

總的來講,整個從自動駕駛到彈性伸縮的過程,達達進行了相應的思考——感知、決策與執行。

4 彈性架構——感知、決策與執行

接下來,我會詳細地介紹 AutoScaler 在感知、決策及執行過程,都做了哪些工作?

首先是感知層面,我們如何瞭解業務是否有突增和離羣?

很多時候是監控系統承擔了這樣的工作。在實際的工作中,我們不管機器某個服務的實例數量以及 CPU Memory Disk Network 有沒有突增,甚至包括錯誤的日誌(熔斷、相應的 Exception ),我們把所有的業務監控數據做一層聚合,統一轉換格式爲 Open TSDB ,然後生成 metric。

右側下方這張圖很簡單,我們把單一服務分成了不同的 Group,每個 Group 有集羣 CPU 的一些指標,這是我們進行的一些聚合,我們做到了業務線的分流。這是感知層,我們匯聚了這些觀測指標,統一成 Open TSDB。當然這個指標可以通過 SQL 進行用戶自定義。

第二層決策層,架構共分爲了四層。

最上面是信息展示層,用戶可以自定義配置彈性規則和觀看彈性每日報表,包括實時消息通知;

第二層是決策執行層,這層分爲聚合層,信息收集層以及決策層,主要做數據聚合、格式轉換、結合彈性的策略做執行;

第三層是規則算法層,規則應該是怎麼樣去配置?如何做時序序列的分析以及做預測等;

最後一層是線上業務的數據,Consul 結合 CMDB 服務元數據,供 Aggregator 和 Collector 計算和生產 metric ,還有一些數據環境層,它會保留歷史的決策數據輔助下一次決策下發和算法預測。

前幾天網友還問我:“你開發的系統是不是過於複雜,你僅僅比 K8S 的 HPA (容器水平伸縮)增加了一些指標而已”。

的確是這樣的,但是我做這個架構,它是在沒有 K8s 完全式生產情況下,並且對彈性有苛刻需求的情況下做出來的。它是做了統一化,不同的指標,當然它的核心算法確實是 HPA 。

如果大家瞭解 HPA,一定可以看懂上圖。上圖顯示,現在有正在運行的三個實例,然後最小實例數需要保證兩個。彈性的公式也很簡單。比如現在線上有 4 臺機器,它的 CPU 使用率是 50%,現在 4×50%,那就是算力是 200%。但是如果規定服務 CPU 大於 40% 就要擴容,這樣的話 200% 除以 40% ,其實就是 5 臺機器,那就需要增量的一臺機器,也就是這個公式所表達的。

不過我將所有的指標都轉換成相應的格式,然後通過這個公式做計算,並且可以多指標做計算。這就是今天做的彈性和 HPA 不一樣的地方。

我們整個彈性擴縮容平臺配有統一的面板,研發可以自助配置它相應的服務。例如,是否需要自動擴容打開、是否需要將極限送容打開,以及相應的時間段、相應的雲、相應的鏈路以及它的指標是 CPU 還是某個隊列,都可以在這個地方配置。整個自動擴縮容的面板,研發可以自助配置,配置之後可以立刻生效,非常方便。

第三是執行層面,執行層面相對容易。

第一,當需要擴容的時候,需要做到單個服務的擴容,也要做到單個業務線上下游鏈路的同時擴容。我們在 Worker 設置了兩個模塊——Despatch 和 Providers。

Despatch 把服務能並行地擴容,保證它的冪等性,以及縮容的時候配有一些保護機制。Providers 統一了擴縮容的接口,然後適配了不同的發佈系統。例如自行開發的 Deployng、Tars、K8s,還有云廠商的 Severless,這是在這層做了一層對接。

爲大家展示一下彈性伸縮架構的實際運行情況,最左邊這張圖是擴縮容情況展示,這裏有消息通知機制;中間這張圖,線上擴縮容的每日報表會發到每一位研發 Leader;右下角這張圖是真實的服務擴縮容,我們從最後一張圖可以看出,業務在 11 點的時候開始抖動,達達擴容的實例數與上升趨勢擬合得較好,準確地捕捉到了業務量的抖動。

5 架構演練——彈性擴縮容演練

彈性系統需要保證全自動運行,所以我們會定期做一些彈性擴縮容演練。接下來與大家分享擴縮容時候遇到的一些問題。

第一,當你擴縮容的時候,肯定不是隻有幾個服務擴縮容,可能是批量地進行擴縮容,然後去驗證上下游擴容的系統是否 OK。這其中的問題是——你有沒有足夠的錢影響到限額?其次是擴縮容會帶來內部流量的突增,這會不會對其他業務有影響?

第二,針對彈性本身,Metric 的收集和聚合的數據是否準確?觀測的指標是最重要的,但是它的準確度要保證的;同時它的最小實例數、在線實例數,這是兩個問題——真實的場景中,我把服務擴進去的,我會把機器綁到了應用組裏,但事實上,他沒有對線上服務做承接流量,它仍然是佔了一個名額,這種情況其實它是擴容是失敗的。這個時候我們的最小實例數定位是真正在線上爲業務服務的機器。

第三,分時段擴容,比如說 6 點到 10 點之間是白天的高峯期,然後晚上 10 點到早上的 6 點是低峯期,我可以做極限縮容。但是它會帶來另外的問題——多 Metric 的協同,當多個規則在一起的時候,可能需要注意這些規則邏輯上是否正確。

網上有個段子——程序員的老婆吩咐程序員下班的時候,去購買 10 個包子,如果看到賣西瓜的就買一個,結果程序員就買了一個包子回家。這背後就是一個多 Metric 協同,這樣的結果肯定是不合理的。彈性也是這樣,設置多 Metric 時候,一定要考慮它的合理性。如果設置不合理,常常會因爲邏輯沒有達成一致,而導致頻繁地擴縮容。

第四,大規模擴縮容演練需要考慮到機器多 Region 均衡打散。全擴容到一個區了,另外一個區域卻很少,這是不合理的;同時在縮容的時候,不能把機器縮容到只有一個 Region,其他的沒有,這樣做無法保證高可用。

第五,如果擴縮容規模很大的話,需要注意對其他組件的依賴。比如 DB 緩存是不是有流量的壓力,因爲在達達的業務場景裏面,有很多緩存是多個業務資源共享。這時候要注意一下 Redis 是否會有一些流量的突增,會導致其他業務受損。包括還有 DB 的連接數一定要確認好,一定和業務確認好哪些事需要提前完成。

第六,在縮容的時候常常會觸發一些 Bug。一些服務的 IP 寫死,會導致業務直接報錯。

第七,縮容速率,縮容並沒有立馬就縮小下來,而是一條緩慢的下降曲線,這個地方是有策略的。因爲如果接下來迎來流量高峯的話,擴容可能會出問題,我們做了一種折中的考量——縮容的時候必須得慢一些,達達加入了縮容速率,縮容速率是剛纔擴縮容的個數再乘一個比例。

彈性系統,達達內部叫彈性降本項目,我們做彈性還有另一個目的——降本。所以在降本方面給大家提供一些經驗。擴容成功率、擴容耗時以及成本的可視化,可以作爲核心的指標來跟蹤彈性帶來的效果和收益。

針對彈性,我們做了很多指標。核心指標主要有擴容和擴容成功率以及成本可視化。

擴容成功率,擴容時長是必須要保障的。成本方面,針對單個應用,我們根據不同的原因、不同的廠商以及不同的部門,計算出來耗費的成本,並且做到追蹤。針對部門,達達也會做到每個服務做排行,當前的資產負債情況;應用級別以及部門級別均可以做到成本的可追蹤,這樣可以衡量彈性擴縮容的效果。

最後是極限縮容,爲了降低成本,肯定要做到一定的平衡。極限縮容是爲了達成容量和成本之間的平衡的橋樑。達到派送業務有時間效應,晚上的時候比較空閒。達達極限縮容的時間是晚上 10 點到早上 6 點之間,只保留兩臺機器;早上 6:00~10:00 之間,它的最小數是 12 臺機器,因此晚上可以降低一定的成本。

但是進行極限縮容必須非常謹慎,我們必須確保在早上 6 點的時候必須擴容 10 臺上來,否則的話,業務可能會有一些問題。爲了做到這一點,達達必須要保證擴容成功率是百分百。

6 彈性降本——極限縮容

這裏帶大家看看達達是如何拆分擴容成功率的。

首先把擴容的流程每一步做梳理,同時對每一步進行優化。比如,環境初始化固定到鏡像裏面,然後做鏡像加速;甚至把服務拉上線之後,做一些預 ping,防止一上來就報錯;還會有接口的一些預熱,但是這時候仍然在 VM 情況下是保證不了百分百成功的,然後我們又引入了 K8s 和 Serverless 的一些安全容器。

安全容器方面,達達除了使用虛機之外,還使用了很多產品。那麼怎麼樣保證事情能在多個環境下運行?

在真實的場景下,首先服務發現需要全網打通的,在 IP 層面要打 Ping;其次,和 VM 的標準化需要統一,簡單來講就是多進程的管理。

我們的服務是依賴於 Cousul 進程,只有 Cousul 進程先行起來,服務才能再起來。真實的場景是這樣——剛開始是基於 Dumb 寫了個 Sheele 腳本,但是這樣做不好進程、子進程的管理,後來基於 CSMD 的原理,然後用 C 語言寫了 entrypoint,可以去支持服務前後依賴順序的啓動和節點篩選。

後來又用 Golang 寫了 Dinit,這樣可以很好地管控容器以及副容器運行,保證了在 VM 肯定的 Severless 上面可以正常地運行。

同時我們還做到了原地重啓,因爲 K8s Pod 它是不支持服務原地重啓的,然後我們自己在 Dinit 層做了一次重啓,可以很好地支持原地重啓,這裏面用到了 VM、以及 VM 上面裝 Docker 這種形式以及包括 Severless 也用了 Firecracker 與 Kata 這兩種。

還有 Dinit 進行了一個配置。

首先,服務有一些進程依賴,我們做了簡單的一些語法。比如 main: excu_start 是它的主進程,但是它要依賴於 Consul 進程,Consul 進程的啓動命令是 Consul 裏面做執行,但是 Consul 有 post_start check,Consul 是不是真正地用到集羣裏?K8s 裏邊會有 life cycle,life cycle 裏面會有一些 post_start、pre_stop 這個動作,但這不是很靈活。我們類似地做這種事,可以支持 post_start、pre_start,可以做到進程層面的管理。

在我們的發佈平臺上,研發可以看到,比如這次服務有虛機,同時它有兩個在運行的 Pod,達達在發佈層面也做了統一適配,對於研發來講,它發佈的每個版本或迴歸版本或者重啓兩個版本,操作是一模一樣的。

這是簡單的發佈平臺,它支持多個雲原生,因爲我們做到了 Dinit 的多進程管理,所以我們能夠做到流程的統一化。這其中的問題是 VM 無法保證百分百地縮容,因此我們引入了一些東西。

第一種,正常 VM 發佈腳本基於 Ansible playbook 做的,同時 Severless Pod,如果把 SSD 集羣建立起來,也可以當做去用。

第二種,虛機上裝配 Docker,算法基本上全是這樣做。

第三種, K8s 以及雲廠商的 Severless 都支持 K8s 的 Pod 這種方式,通過統一的模板做這個事情,這邊做了一些初始化的操作。比如升級一些版本,通過 Volumes 共享,通過 Dinit 方式控制整個流量。

所有的這些安全容器,它背後都是兩個開源技術——Firecracker 和 Kata,當然還有 Google 的 gVisor,對於 Kata 其實雲廠商都有彈性實力,它其實就是託管的 K8s,然後以安全的、具備容器速度的、又有 VM 層面隔離的這些容器實例,可以直接擴容。

另外一種支線是 AWS 的 Firegate 以及 Ucloud,這些都是基於 Firecrackers 去做的。如果大家沒有上容器,但是需要容器速度的話,這可能是比較好的方式。

7 總結與展望

總結一下,圖中從左到右是整個問題的拆解,從業務的訂單量和運力以及對服務承載的承諾,我們需要對系統的容量做評估,然後把這個問題的分析拆解成匯聚 Metrix,然後傳遞給彈性系統,系統需要感知到這些變化,以及相應的策略的配置,最終觸發執行。在執行層面,爲了保證擴容百分百,引入了很多技術,做到了這些擴容和縮容的流程的統一。總體來講,這個過程是從上至下的思考、從下至上的執行。

最後展望未來,這個系統是在 2019 年開發的,目前已經穩定運行了 20 多個月,有將近 18000 次的擴縮容記錄。整個系統也對接了多種雲原生,包括全程全自動的,當然也做到了雙雲的支持。但是還有很多值得探索的地方,比如以下兩方面。

第一,引入 Facebook 的框架,結合歷史數據進行預測,目前帶寬已經做到 20% 的成本降低了。

第二,在 TSA 這塊進行優化。目前我們只是做時序的分析,並沒有對一些異常點做檢出,之後會在這方面下功夫。

今天的分享就到這裏,感謝大家。

 嘉賓介紹:

楊森:現就職於達達集團雲平臺部,負責 DevOps 團隊和 SRE 團隊,專注於提升分佈式系統的穩定性和可觀測性、容量彈性、故障自愈、多雲容器架構及效能平臺建設。曾供職於朗訊、英特爾、衆安保險等公司。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/vSvILCjBRBzcduAhbiK9Xw