從 GCP 故障看 17 Media 工程團隊的災難應變

GCP 在萬聖節隔天發生了三年來最大規模的故障,17 Media 也是嚴重受災戶之一,但它們是如何迅速恢復並改進的?請見本文詳述。
評論
評論

本文來自 17 Media Tech Blog,並獲作者授權轉載,作者Roy Chung-Cheng Lou,軟體和數學愛好者,喜歡透過技術來幫助人和企業。

Google Cloud Platform (GCP) 在萬聖節隔天發生了三年來最大規模的故障,在故障的 12 小時內,因為網路問題造成無法新增機器。這次故障也是個好機會來檢視 17 Media 的 Site Reliability Engineering (SRE) 團隊平日的防護及練習的成果。筆者有幸參與故障排除全程,本文將以一位 17 Media SRE 觀點來介紹當天發生經過,探討為何這次 17 Media 受影響特別大,以及日後的改進事項,與大家一同交流。

17 Media 系統概述

目前主要負擔線上流量的有十組伺服器,全都以容器透過 Google Kubernetes Engine (GKE) 運行,利用 CPU 用量自動擴容。每日尖峰及離峰流量差距約為六倍,尖峰約在晚上 11 點鐘左右,QPS 超過 10,000。這對開發團隊是相當刺激的一件事,因為每天晚上都是個小小的雙十一挑戰,如果不小心寫出了 bug,當天晚上就會收到很真實的用戶反饋。

1_QJmSnLR-Xx1K_y6HhYF8yw
17 Media 線上系統QPS流量圖,每日峰值約在 11:00 pm,凹下去那塊就是故障當晚的流量。

17 Media 的主要用戶分布於亞洲及美國,主力開發由台灣團隊負責,整個後端程式由 Golang 所撰寫。為了要讓開發團隊更為敏捷,SRE 每個上班日都會佈署新版本:透過 Jenkins 在凌晨四點自動將前一日 master branch 的代碼佈署到 staging 環境,SRE 則是在上班後將 staging 環境測試穩定後的代碼佈署到生產(production)環境。

發生經過

事故發生於 11/1 中午,當時一位 SRE 發現 staging 環境的伺服器無法正常部署新版本,一段時間後在 GCP 狀態頁面確定是 GCP 的故障,造成無法開啟新機器。這對我們意味著幾件事:

  1. 無法佈署新版本。
  2. 當 GKE 認為某個容器健康狀態異常而決定關閉時,將會無法開啟新機器替換。
  3. 當系統有擴容需求時,將會無法開啟新機器。

其中 3. 的影響最大。在故障剛發生時(12:00)系統處於離峰狀態,依照過往的流量波形,預估稍晚流量上升後系統會無法支撐。但由於以往 GCP 故障多在一小時內修復,當下我們不太擔心。

螢幕快照_2019-11-13_上午10_41_10
SRE team lead 在 Slack 的公告,為了要讓各團隊即時同步資訊所以翻譯成英日文。

數個小時過後從 GCP 狀態頁面仍看不出修復跡象,這時團隊開始緊張了,因為晚上就是高峰期,現有的系統資源肯定支撐不住流量。SRE 團隊很快討論後決定了以下應變措施:

  • 用戶限流
  • 關閉次要功能
  • 卸載運算需求低的容器,改運行關鍵容器
  • 跨雲佈署服務

由於無法佈署新版本,需要改動後端代碼的 hotfix 就不在考慮範圍內,所以這次的應變措施大多以運維的角度出發,跟以往與後端團隊合作的模式不同。以下分別介紹各項應變措施:

用戶限流

1_TnUj3u2Ugdy2LutMXhCsJg
當新上線用戶因為限流而被無法使用時,app 會顯示爆氣的 17 寶寶

這是一項限制用戶數的功能。系統會持續追蹤目前線上人數,當發現無法支撐過多的用戶時,會將超出上限的新上線用戶擋在系統外。新上線的用戶會看到超載訊息,而已經在線上的用戶可以繼續保有良好的直播體驗。

我們預估了當前系統可以支撐的用戶數並開啟限制功能。從事後看來這個措施效果最好,因為這是一個開發成熟的功能,當下只要定義人數上限就會產生效果。

關閉次要功能

目前 17 Media 線上服務大多為 CPU bounded。當 CPU 成為限制資源時,我們只好透過重新分配 CPU 的運用來讓重要的工作可以順利完成。對於直播平台來說,最重要的體驗就是直播本身,任何佔用 CPU 的非直播功能都可以關閉。但由於這些改動不能牽涉代碼改變(因為無法佈署新版本)所以可以關閉的功能有限。最後僅關閉了一些可以動態開關的功能,例如排行榜以及發送紅包。

卸載運算需求低的容器,改運行關鍵容器

當可以使用的運算資源無法擴充時,除了關閉次要功能外,另一個方式是重新分配各個服務所運行的容器數量。對於某些延遲要求比較低的服務(例如 job queue worker),可以進一步降低機器數,把運算資源分配給延遲要求高的服務(例如 API service)。

這個方式說起來容易做起來難。由於更換容器不屬於 SRE 團隊日常工作之一,實際執行上只能土法煉鋼進入一台一台機器手動更換,而且更換到第三台後就因為不明原因失敗了。相信大家都有聽過寵物跟牲畜的故事,一時要將牲畜當作一隻隻的寵物看待並不是件容易的事。

手動跨雲佈署

17 Media 的伺服器位於美西,剛好是各大雲服務供應商都有機房的位置。如果當下沒辦法在 GCP 開機器,那就在 AWS 開機器吧!

因為總故障時間長達 12 小時,前面幾項工作也很快的嘗試完了,我們大部分的時間花在跨雲佈署的準備。團隊已經好一陣子沒接觸 AWS 的容器服務,我們不確定哪個解決方案能在短時間解決問題,所以決定分頭嘗試不同的服務,包括 EKS / ECS / EC2 以及 Elastic Beanstalk,能夠嘗試的我們都試了。後來每個人分別遭遇了不同的問題,由於團隊對於 GCP 黏著度較深,導致這項方案完全失敗。現在反思當時應該要所有人專注在同一項 AWS 服務,或許能在時限內找出一項解決方案。

為何 17 Media app 受影響特別大?

這是全球性故障,我們原先預期全球各大 app 都會發生問題。但在網路上搜尋一輪的結果超出意料:各大 app 都運作正常。相較於其他公司,17 Media 受到的影響相當大。事後研究發現可能原因有二:

  • 故障發生於台灣時間中午 12:00,也就是美西時間凌晨 2:00,對於大部分美國的 app 來說已經過了尖峰時段,不會有擴容需求,受到的影響也低。相對 17 Media app 從下午過後流量就一路往上到半夜的高峰,無法擴容的影響相當大。
  • 17 Media 的主要功能集中在少數服務上,當服務所承載的一部分功能耗盡服務資源時(例如CPU),該服務上承載的其他功能也跟著異常,造成全面性災難。其他公司的 app 當下僅有部分功能異常。這也是大家所熟知的單體服務(monolith)與微服務(micro services)在災備隔離(fault isolation)上的差異,但實際發生時感受還是特別深刻。

改進事項

SRE 團隊的風格是快速嘗試且不責怪(blamelessness),在故障後的隔天上班日,團隊就開會檢討了當天的應變以及日後的改進。從事後反思當下的措施,真正奏效的反而是平日已經準備好的災難預備方案。這也讓我們認真檢討改進方案,若日後發生一樣的故障時服務能繼續運行。以下兩項是團隊討論之後得出的改進方案:混合雲及微服務,分別屬於 SRE 及 backend 的工作範疇:

混合雲佈署服務

混合雲佈署是個解決雲服務故障的好方法,畢竟各大雲服務商從來沒有同時故障過;當其中一個雲服務故障時,可以將流量導至另一個雲來排除問題。但由於其運維複雜度以及可能帶來的延遲上升,之前並不是 SRE 團隊的工作重心。這次故障讓我們認真思考混合雲的可能性。我們計畫從無狀態(stateless)服務開始,原因如下:

  • 無狀態服務特性:因為無狀態特性,這類服務很適合隨著流量動態增減機器。也因為這個特性,在這次故障受到影響的幾乎是無狀態的服務。
  • 影響範圍廣:17 Media 的服務大部分為無狀態,如果解決了這類服務的單點故障(single point of failure)問題等於解決了大部分的問題。

一般來說無狀態服務前面有一層 load balancer 分散流量,服務之下會有另一層服務或資料庫(database)來儲存或提供狀態。同時因為資料庫的存取延遲較長,前面多半有一層快取(cache)來降低延遲,同時降低資料庫負擔。也有人稱這為 3-tier architecture

1_AWmgcleesZNFYS4TDEU4eg
一般常見的 3-tier architecture 結構

這一類運維層面的改動有個重點,必須對開發人員無感,也就是不能造成開發人員額外負擔。

  • 對於下層服務的讀寫比例約為 9:1。
  • 資料庫讀寫:較慢,但因為前面放了快取所以對用戶影響較低。
  • 快取讀取:必須要快,因為大量的資料來自於快取。
  • 快取寫入:也需要快,但因為寫入比例低,對用戶影響較小。
  • 快取跟資料庫之間可以是最終一致(eventual consistency),但是誤差時間不能太長(例如不能超過一分鐘)。

基於以上假設,我們計畫做以下架構改動:在 AWS 建立另一組無狀態服務,前面由 weighted DNS 分散流量;在兩個雲服務商各建立一組快取以加速服務讀取速度,但寫入一律透過 GCP;資料庫讀寫依舊透過 GCP 。下圖描述了計畫中的改動。

1_9uZaXIE8DRqLSVRFI3w_FQ
計畫中的混合雲服務

在開發人員眼中,這個策略是非常合適的,因為開發人員對於資料庫及快取的假設不因這個策略而改變,也就是說工程師可以在不知道底層複雜度的前提下進行開發。原因如下:
GCP 這側的讀寫快取是位於同個雲內,所以快且穩定。

  • AWS 側的快取寫入 GCP,延遲較高。但因為寫入量少所以影響不大。
  • AWS 側的讀取來自同個雲內的快取,延遲低。但會有短暫的資料不一致:資料跨雲寫
  • GCP的快取後,需要一陣子才會同步回AWS的快取,這會造成讀取到錯誤的舊資料。但拿常用的快取服務 Redis 來說,如果兩個資料中心距離很近,兩座 Redis 之間資料差距遠小於一秒,對 17 Media 的應用場景來說可忽略不計。

在 SRE 眼中,這個策略略有風險:17 Media 的 DNS 服務直接面向用戶,切換 DNS weighting 的時候會有 DNS cache poisoning 的問題;但這可以透過後續架構微調解決(例如前面再搭一層 load balancer),所以目前較不擔心。

微服務

在之前提到,這次 17 Media 會成為重災戶,其中一個原因是主要功能集中在少數服務,解決方法就是將單體服務(monolith)分拆成微服務(micro services)。但要怎麼拆就是個大學問,拆分方式需要視團隊組成而決定,後端團隊正在討論不同拆分的可行性。

結語

在這次故障的處理過程中,讓我感到產品團隊的責任越來越重大。目前團隊支撐的已經是個多元的直播產品,平台上包括早期用戶大多來自台灣,系統故障的內部 Slack 訊息只需準備中文;但現在已經跨足許多地區,這次故障時內部更新訊息包含中英日等語言。

1_unNJk71Jm_gvf4LT_AGyBg

以上改進事項正在進行中,有興趣歡迎一起討論。也歡迎你參與進化的過程,17 Media 工程團隊持續徵才中,包括 backend 以及 SRE。在 17 你可以遇到頂尖好手,這是一個多元化 app 的壯大旅程,歡迎加入我們!

職缺列表:http://bit.ly/2qnPLwm

責任編輯:Chris




精選熱門好工作

PopDaily 資料分析師 –【行銷部】

數果網路股份有限公司
臺北市.台灣

獎勵 NT$15,000

iOS工程師

Omlet Arcade 美商歐姆雷特
臺北市.台灣

獎勵 NT$15,000

AI 智能客服訓練師

VeryBuy非常勸敗
臺北市.台灣

獎勵 NT$15,000