服務總是在半夜出事嗎? — 令人不再害怕 On Call 的系統化做法

smalltown
Starbugs Weekly 星巴哥技術專欄
17 min readJun 6, 2022

Background

先來談談我剛加入公司第一週所發生的事情吧,那時候是晚上九點多的樣子,菜逼八如我正在摸索系統,突然發現 Slack 跳出一個公司服務倒站的訊息,我趕緊登入到相關機器裡面,發現某一台的硬碟被 Nginx Log 塞滿了,所以我就將 Log 資料給清除,並且重啟服務,隔天主管請我到一份文件寫一下事情發生的開始與結束時間,聽起來這應該是一個不到一小時就被解決掉的意外事故,但我們來看看其中有哪一些問題存在?!

  1. 要是我當下沒有注意到 Slack 訊息的話…公司主要服務是不是就沒有人發現他已經壞掉了?!
  2. 我登入機器後為什麼知道要去看硬碟空間使用狀況,怎麼查是 Nginx 造成的,為什麼刪除 Log 後要重啟服務?
  3. 事故發生當下沒有人去做任何通知使用者的事情,事件發生完後也沒有做任何的檢討,只有簡單紀錄服務不正常的時間點,那麼之後會不會再次發生?

聽完之後就可以發現,此意外事故可以在一個小時內被解決掉,只是運氣好罷了,要是運氣不好的話,可能需要花更久的時間,而且未來再次發生也不意外,不過這在一個沒有幾個人的小公司其實好像很正常?!但隨著公司慢慢的擴張,On Call 文化在公司內也逐步地導入與建立起來

此篇文章便是想要談自己公司這一路上逐步將這個制度建立起來所做的事情與經驗,整篇文章會依照底下幾個大主題把自己覺得重要的事情提出來,希望大家看完這邊文章,對於 On Call 這件事情可以有一些新的看法與想法:

  • Monitoring
  • Organization Type
  • On Call
  • Incident Response
  • Root Cause Analysis

Monitoring

📚 Internal Monitoring

首先第一步要做的就是建立相對完整的監控機制,不過為什麼會需要監控呢?因為當一個服務沒有被監控時,就好像一個人眼睛看不見一樣,服務提供者將無法得知服務的健康狀態,無法知曉是不是有什麼事件即將發生,或是已經發生了,沒有監控所提供的資訊,也無法在事件過後進行詳盡的調查;換言之,沒有監控,就沒有辦法去量測服務的健康狀況,結果就是所提供的服務沒有品質可言

其實要監控的東西包山包海,這邊列一些比較基本,通常都要有的項目供大家參考:

  • 以人的角度來看的話,需要去監測一般的使用者功能是否正常,效能是否符合預期,使用者遇到的錯誤有哪一些
  • 以網路角度來看的話,至少需要監測網路的速度與連線能力是否正常與符合預期
  • 以機器角度來看的話,不外乎 CPU, Memory, Disk 的使用量需要低於閥值

其實有些監控的閥值一開始要設定多少不知道是很正常的,但透過時時刻刻監控之下所收集到的資料,其實就會慢慢知道正常的數值應該落在多少,就可以去調整預期的閥值,並且研究如何去改善服務的品質,讓使用者可以擁有更佳的體驗

📚 External Monitoring

而且監控需要內外分明,試想有沒有可能外部使用者無法正常使用,但內部監控顯示一切正常?當然有可能,而且常常發生,因為從內部和從外部去實施監控時,常常會走不同的路徑來抵達服務本身所提供的端點

  • 例如外部使用者跟公司內使用的 DNS 服務不同,當外部 DNS 設定錯誤時,就必須透過外部監控才能發現,同理 TLS Certificate 也可能內部跟外部使用不同的憑證
  • 只有透過外部從不同的區域去做監控時,才有辦法真的得知不同地區使用者連到服務時的路由還有網路速度是否正常
  • 現在常常會在服務的前面加上 CDN 或是 WAF,要是沒有從外部做監控的話,其實無法得知 CDN 與 WAF 是否運行正常

📚 Monitoring As Code

自己公司是怎麼管理這些監控設定呢?以內部監控來說,我們使用的為 Prometheus Stack,所以可以根據定義 Prometheus Rule 來完成監控的相關設定,而且 Software 和 Site Reliability Engineer 都會需要去撰寫這些設定,因為有一些跟 Business 較相關的 Metric 監控會由 Software Engineer 來撰寫,SRE 則會撰寫跟 Infrastructure 比較相關的監控項目,大家各司其職對自己的工作內容做負責

而在外部監控的部分,我們則是花錢解決,透過訂閱外部相對知名的現成解決方案來達成,因為外部監控最重要的就是跨多個區域的監控功能,而且還要提供獨立於系統的公開服務狀況頁面,自己完成這些功能比直接花錢的成本來的更高;而這類型的服務目前也都有跟 Terraform 整合再一起,所以可以透過撰寫 Terraform 語法來完成設定

Organization Type

📚 Organization Scale

像上一段提到要設定的基本監控項目也不少個,而且要有人持續負責維護,假如人數太少其實無法達成,在這一點上只能根據資源去做取捨,把資源先投資在相對重要的項目上,也是自己公司一路走來持續在演進的方式

例如在幾年前當 SRE 團隊只有我一個人時,我先透過外部監控服務把重要的功能監控先完成,讓服務壞掉時會立刻打電話通知我,內部監控則是 CPU, Memory, Disk 先列優先工作項目去完成他,後來隨著 SRE 人數逐漸變多,才開始把其他的監控機制越補越完整

再來要談到一個很實際的問題,就是一個人不可能一週七天,每天都 24 小時一直在工作,總是要吃飯,洗澡,睡覺…等,但是服務卻隨時都可能遇到的問題,所以要如何即時獲取一個可以協助解決問題的人力資源呢?底下會討論幾種常見的模式供大家參考

📚 Traditional Way

有點規模或是有需要維護自己機房的公司,因為工作屬性差異大,或是具備資安要求,常常可以看到開發與維運部門是分開的,而維運部門會需要輪班,負責處理第一線事務居多,假如應付不了時,就會聯繫開發部門進行協助

而除了透過輪班來解決一天 24 小時需要休息的問題之外,要是維運人員分佈在世界上不同的區域,就可以運用不同時區的好處,例如台灣時間早上服務出問題由台灣團隊負責排除,台灣時間晚上服務掛點時,就變成歐美團隊去負責排除問題

📚 DevOps Way

近幾年 DevOps 觀念的興起再加上雲端服務越來越普及之下,軟體開發團隊對於自己服務所運行的環境掌握程度提高很多,尤其是這兩年來可以看到各式各樣的雲端資源都可以使用程式碼來定義,打破了以往開發與維運間的藩籬,當服務發生問題時,開發團隊本身就有能力與責任將問題自己解決掉,也因為要是開發出來的服務時常發生問題自己就會遭殃,所以對於有關維運面的改善需求也都會相對積極的去看待

目前自己公司就是採用類似的模式,畢竟我們的服務都在雲端比較多,不需要實體機房的維運人員,比較特殊的地方在於我們的 SRE 成員大部分時候是隸屬於特定的團隊之下,跟團隊一起開發跟維運服務,但是全部的 SRE 在橫向上是一個團隊,由同一個主管負責,讓彼此間的知識與資源可以共享

On Call

📚 On Call Engineer Responsibility

在 DevOps 模式之下,團隊成員人人都需要擔任 On Call 工程師,當輪到擔任 On Call 工程師時,通常會需要負責哪一些具體事項呢?

  • 第一個職責就是負責坦怪,也就是將一些無法自動化的常規維運工作給處理掉,讓團隊其他成員可以專心在主要開發事務上
  • 運氣不好 (或是說很好?!) 可能會遇到服務壞掉需要擔任主要 Owner 協助排除問題
  • 產出輪值時的報告,讓整個團隊的人都可以獲知這一段時間發生了哪一些事情
  • 最後也最重要的就是 Runbook 的撰寫與改善,讓團隊中的任何一位成員,遇到同樣的問題時,都有辦法根據 Runbook 將問題給排除掉

📚 On Call Engineer Right

輪到當 On Call Engineer 的時候好像很賽,會不會沒有人要當? 其實會先跟工程師進行溝通,畢竟大家都是希望提供出一個品質良好的服務,而且也不是固定某個人擔任,是每個人都需要論流,包含工程師主管

假如因為處理事件導致超時工作就可以申請加班費,在擔任 On Call Engineer 時原來的工作內容也可以先暫且擱置,把注意力跟時間先擺在上面提到 On Call Engineer 需要負責的工作任務上,而且也只有當過 On Call Engineer 的人,才有辦法對於系統有更全面性的認識,知道整個系統存在哪一些問題,如何去優化與改善

📚 On Call Escalate Model

上面有提過在設定監控時就會開始分類,假如監控的是有關商業邏輯面的,當服務出問題時就會直接通知軟體開發工程師,而假如是跟基礎設施比較相關的,就會通知 SRE 成員;會這樣做的原因在於術業有專攻,與其安排一個只是來接電話的人,倒不如在事件發生的第一時間通知到能夠解決問題的人,減少花在聯絡與溝通的時間上,盡可能縮短服務受影響的時間,根據自己的經驗,要如何準確的通知到 On Call 工程師有幾點需要注意

  1. On Call 工程師會輪調,怎麼知道這時候要打給該部門的哪一個工程師?
  2. 這個通知可能來自於任何尷尬的時間點,如何確認該接到通知的人真的有接到通知,要是他沒有接到通知的話,要怎麼辦?
  3. 假如這個事件需要跨團隊合作解決時,要如何有效率的把大家在最短時間拉在一起?

📚 Notification System

其實市面上有不少的 On Call 系統解決方案,例如 PagerDuty,Opsgenie…等,不過因為自己公司的 SRE 有開發類似的工具,所以就一直沿用至今,主要透過 Serverless 服務接收來自各種監控服務的 Webhook,然後再整合各種第三方服務來滿足上面提到的需求,緊接著讓我們來看看為什麼會需要 On Call 系統

📚 How to Handle Rotation

第一個要解決的問題就是 On Call 工程師會需要論調,通常一週輪調一次是一個不錯的策略,但假如團隊成員太少,就會常常輪到XD 每次都會安排主要與次要 On Call Engineer,當主要 On Call Engineer 真的有其他事情要忙或是漏接通知時,就會由次要 On Call Engineer 接手,再不行就打給工程師主管

不過這個班表一定不能夠用靜態的組態檔案來記錄,因為大家的輪值表會一直動態更動,不會一直依照一樣的順序,所以會建議一定要整合 Calendar 服務,讓整個團隊可以自己去設定輪值表,自己目前是整合 Google Calendar,根據 Calendar Event 的參加者來進行通知

📚 How to Improve Communication Efficiency

所以每個團隊在任何的時間點都會有一個 On Call Engineer,團隊內還有可能會知道這禮拜由誰負責輪值,但是不同團隊間要如何有效率的知道目前是哪一個人呢?每次要找人時還要打開 Google Calendar 查詢太累了,所以自己透過 Slack 有個叫做 User Group 的功能,把 Google Calendar 裡面的輪值人員同步到 Group 內,這樣一來跨團隊在溝通時就不再需要知道目前誰是 On Call Engineer,只要直接找到對應的 On Call User Group 就好

📚 How to Improve Notification Effectiveness

確定好 On Call Engineer 是誰,那當事件發生時,要如何有效率的通知到該名工程師呢?

  • 首先就是通知的訊息要有互動,不能電話接起來說一聲:系統壞掉了!就把電話掛斷,要讓接到通知的人透過回覆個聲音或是按按鈕確定他已經接到通知了,知道有事情發生了才可以
  • 要把通知的詳細狀態顯示出來,例如現在正打算通知誰,他有沒有接到通知了,假如一直沒有回應的話,下一個會打給誰
  • 讓一般人也可以方便地發出通知請求協助,自己是透過實作 Slack Command,例如在 Slack 打 /call 後面接上人名或是群組名稱,系統就會自動發出通知,避免在大半夜時,大家還彼此面面相覷問說你知不知道誰的電話是幾號啊?

能夠把這些要點給完成的話,其實就能夠有效率的通知到對的人了,而且在跨團隊或是跨部門通知時也沒什麼大問題

Incident Response

📚 Aware Incident Before Customer

Incident Response 從字面上來看的意思是如何回應事故,所以主要的核心精神就是要主動揭露目前服務已經有問題了,然後公開地跟使用者說明目前處理的進度,並嘗試安撫使用者的情緒;而不是什麼都不說,工程團隊默默地地修復,讓使用者浪費時間去做嘗試或是猜忌,最後導致使用者們在各種管道謾罵,不只正在努力修復的一面沒有被看到,反而還對公司形象造成負面效果

要是當初 SLA 有談好至少要幾個 9,沒有達成的話就要賠錢,所以事故發生時只要什麼都不說,就表示服務沒有問題,這樣就不會違反當初訂定的 SLA 標準,也就不用賠錢了,這樣對於公司來說來符合最佳利益吧?假如是獨佔的壟斷事業或許還可以這樣玩,不然自己覺得這樣的做法對於公司商譽也是會造成損傷

📚 Incident Severity Category

不過什麼樣的事故需要回應,要做什麼樣的回應?團隊應該在日常就先將服務發生事故時,使用者可能會遇到的狀況進行討論,並且根據嚴重程度的不同來進行分級,以影響層級分成三到四個等級,並且訂定出每一個等級發生時需要參與的人員與進行的任務有哪一些

因為只有在事件發生前先討論完整,並且適時地進行演練,那麼當事件真的發生時,On Call Engineer 與團隊其他成員才真的有機會根據 SOP 按表操課,在最短的時間內將問題排除,不會在半夜事故發生時,讓 On Call Engineer 一個人不知所措,任憑寶貴時間一分一秒地緩緩流逝

📚 Incident Handle Process

所以當 On Call Engineer 接到緊急通知時,他到底應該要做什麼事情呢?

  1. 就是他要先根據服務目前遇到的狀況進行簡單的分級,例如他發現網站整個無法存取,符合定義中最嚴重的等級
  2. 按照 Incident Response 文件定義把其他需要的角色也通知一輪
  3. 稍微討論過後,確定是否讓讓服務進入維護模式
  4. 由客服對外發布公告通知一般使用者與其他合作夥伴
  5. 同時 On Call Engineer 和團隊成員也一邊開始著手調查與修復問題,期間將調查進度的結果簡單地揭露
  6. 確定將問題排除驗證完成之後,就可以結束維護模式,然後再通知使用者問題已經解決了
  7. 事後再進行檢討與改善,避免一樣的問題再度發生

📚 Increase Incident Visibility from Internal

根據自己的從業經驗,以往事件發生時,常常會觀察到一個現象,就是資訊不夠透明與對稱,例如資料庫服務發生問題,工程師一定是戰戰兢兢地忙著調查與修復,所以這時候客服,PM 等相關對外聯絡窗口比較難馬上得知消息,而且通常彼此之間有著巨大的溝通障礙,導致要過好長一段時間,才能讓所有人都清楚地理解目前發生了什麼樣的事情

因次當事件發生時一定要在第一時間就先讓所有內部的人都知道,目前我們的做法是實作了一個 Slack Bot,只要跟他說有意外事故發生了,他就會馬上根據事情定義好的 Incident Response 流程開始引導相關人員開始作業:

  • 先自動建立一個 Slack Channel,並且將需要知道這件事情的人都拉進來 Channel 裏面
  • 同時根據定義的事故等級通知需要一起協助處理事故的各個角色,例如 Tech Lead, 客服…等
  • 大家一進到 Channel 裡頭,看到事件分類的等級就可以根據事先定義好的 SOP 開始分頭進行各自的任務
  • On Call Engineer 就只要專心地處理問題就好,頂多適時地回覆一些重大進展,然後 Tech Lead 或是 Engineer Manager 來幫忙溝通協調其他事情,這樣一來,就不會有人還不知道發生了大事,或是該做什麼事

📚 Increase Incident Visibility from External

上一頁提到的是如何提升意外事故在組織內部的能見度,那麼外部要如何做到呢?

  • 可以透過外部監控結果製作成公開的服務健康頁面讓使用者即時知道服務的健康狀態
  • 當服務發生問題的當下也可以透過收行動裝置的推播訊息與各種社群平台通知使用者
  • 總而言之,當有意外事故發生時,要讓所有的人都可以知道目前事件的狀況與處理進度,而不是默默地處理什麼也不講

Root Cause Analysis

意外事故處理完畢,服務恢復正常就沒事情了嗎?答案當然是否定的,每一個意外事故通常都要有一份 RCA,RCA 的全名為 Root Cause Analysis,他是一個用來找出事故發生真正原因的系統化做法,其實是一份簡單的文件,內容會詳述事故發生的時間點,導致的結果,如何發生的,為什麼會發生,然後大家根據這份文件一起來討論,假如要避免類似的事情再次發生要完成哪一些事情

所以具體要怎麼去準備 RCA 呢?讓我們舉個簡單的例子,假設今天因為忘記更新 TLS 憑證而導致服務異常

  • 那麼我在 RCA 要先寫上事故發生的簡易時間點,例如幾點收到服務壞掉的通知,幾點開始處理
  • 接著紀錄查詢過程中發現的資訊與原因
  • 最後大家再一起討論要如何避免該事故再次發生,例如加上對於 TLS Certificate 的監控,然後討論這些矯正措施的可行性,由誰幫忙去施行,與預計的完成時間點

這樣的一份文件除了可以釐清事故的發生與解決過程,並且避免再次發生之外,也可以成為 Runbook 的素材,讓其他人知道遇到相關問題時要怎麼辦

Conclusion

在最後檢討事故時,最忌諱的就是找個代罪羔羊出來,而不是認真的去討論要怎麼避免事故再次發生,這樣做會有兩個問題:

  • 問題沒有被真正的解決跟改善,只是找個人來背鍋,如此一來同樣的問題一定會再次發生
  • 當員工因為勇於承擔責任而被怪罪,那麼組織內慢慢就會養成多做多錯,少做少錯的氛圍,這樣一來,整個組織的負面風氣與文化就會開始形成

系統不可能不發生問題,服務發生事故時並不可怕,可怕的是沒有一套健康且完善的機制去處理他,希望大家看完這篇文章可以不再對於 On Call 這兩個字感到害怕,他是讓整體服務品質可以持續不斷的改善與進步的其中一個 重要環節; 也希望這篇文章可以不小心幫助到正嘗試在組織內部導入 Incident Response 的人 😊

--

--

smalltown
Starbugs Weekly 星巴哥技術專欄

原來只是一介草 QA,但開始研究自動化維運雲端服務後,便一頭栽進 DevOps 的世界裏,熱愛鑽研各種可以提升雲端服務品質及增進團隊開發效率的開源技術