之前看到同溫層有些人在討論 Deploy on Friday 這件事,本來沒想跟風,偏偏昨晚要下班時,公司設定的 alert 響起,而且還是非常重大的那一種,只好把背包放下,跟其他同事協助將被灌爆的客服。老實說,這不是我們上版引起的,而是我們使用的 Firebase Real-time Database 不知道哪根蔥不對,竟然在周五晚上,餐廳最忙最需要我們系統的時候,搬移我們的資料庫到別台機器上,餐廳非常依賴的資料即時同步功能瞬間就異常大概半小時,於是想寫一下關於 Deploy on Friday 的想法。
當晚客服如預期般接到很多抱怨,我們也發 ticket 詢問 Google,得到的答覆是:原本 host 我們資料庫的那台機器,health check 出現異常,於是啟動了自動搬移機制 (淚)。話說,這算是 Firebase Deploy on Friday 嗎?
首先,我得說,我認為 devOps 是一種不分開發維運、打破籓籬、持續改善與盡早交付的精神。所以我們做很多自動化的測試 (不管是單元測試、整合測試、壓力測試都做)、自動化的布署、任何能讓上線比較沒壓力及降低風險的事。終極目標就是不管是周五也好,周末也罷,只要想 Deploy,隨時都能 Deploy。
但什麼時候上線,還是回歸到風險控管吧。無論花了多少資源進行測試,都無法保證一個新版本是沒有 bug 的,所以新版本上線後還是有一定的機率會出錯,對!不管是一個星期的哪一天,機率都是一樣的,所以哪一天上版都可能會出錯,但出錯造成的損失就不見得一樣。
不同的產業,不同的服務類型,就有不同的損失分布,像是 Facebook,壞了對我來說不痛不癢,不用它幾小時不是什麼大問題,這類對多數人來說不是 mission critical 的服務,哪天上新版損失是差不多的。但是像我一開始提到的,周五晚上以及周末,是餐廳業績最好也是最忙碌的時間,我們在周五上新版若出錯,對餐廳所造成的營業損失肯定是高於平日的,這時候還會選擇周五上新版?
當然,若已知上新版本的獲利遠大於損失,那就上啊!這還有什麼好說的。(笑)
如果說機率是一樣的,且已知某天上版造成損失是高的,是否有方式降低損失呢?這命題遠比為何不能在周五上版來的正向許多。
例如,我們的退版速度能多快?需要很複雜的步驟才能退版嗎?畢竟不是所有的情況都能夠快速地退版,像是 iOS app 或牽扯到資料庫 schema 變動都可能無法很快速地退版。iOS app 從送審到等待通過,這段時間的操控權不在開發者手上,甚至遇到聖誕長假,Apple 是不審理 app 新版本上架的 (我沒在聖誕長假期間送過緊急審查,不確定審不審),這都會影響是否要上新版本的決策。如果退版只是一個按鈕,或是能在幾分鐘內就完成,上新版或許就不是太大的問題,就看這損失我們是否願意承擔。
或是上新版後,我們可以用 feature toggle 控制損害範圍,像是新功能只有少數的使用者才能夠使用,當新功能出錯時,也只有這些使用者會受影響,或是反過來用,當新功能出錯時,不用退版就能將這功能關閉,讓使用者回到舊版本繼續使用服務。例如 Apple 就階段性發布自動更新的機制,以 1%、2%、 5%、10%、20%、50%、100% 的比例推送自動更新,還能夠暫停發布。也就是說若 API 上版也能夠用同的方式管控,就能控制損害的程度。
又或者是,調整系統架構,能讓上新版的影響範圍降到最小,我並不是在推崇 micro-service 架構或是什麼廠商的 serverless 方案,光是最基本的,系統架構模組化,讓修改的影響侷限在一個模組內,這樣就能夠大大降低上新版的風險,如果系統架構沒有好好的模組化,改一個東西或加一個新功能,動輒改上數十個檔案,我相信上版時心裡還是會怕怕的。
抱有持續不斷地改進的精神,努力降低上版的風險,最後,哪一天上版就僅僅是風險控管的問題了。
這個議題的另一個面向,很多人直指不想周末加班或 on-call,所以這題目可以更 general 一點,該不該在假日前上版,像是農曆新年連假、中秋連假、國慶連假,以我們的產品特性,我們都是較保守不上版,不是因為沒信心,而是找不到理由要在客戶最忙的時刻增加風險 (相較已穩定運行的舊版本)。
我曾在某年的最後一天,雖然那天放假,我仍跟老闆到某家在信義區的知名拉麵店,不是去吃拉麵,而是去查找為什麼平安夜那晚候位出單機的出單速度異常緩慢,說真的,這功能上線前我們也是做了不少的測試,他們剛開始用時,也曾在那邊站了數小時觀察使用情況,認為不會有問題,偏偏平安夜那晚有時候要 10 秒才出一張單。最後我們請店家不要使用 4G 分享器 (我相信很多人都有過跨年時手機訊號滿格卻沒有網路的情況),改使用獨立的 WIFI 就沒問題了。
我想說的是,當線上的系統出現問題,多數工程師還是願意犧牲自己的假日解決問題,但不代表當有不用加班的選擇時 (舊版本已穩定運行一段時間),卻要刻意拿石頭砸自己的腳 (放假前上新版本)。畢竟在上班日上新版,若出現問題,可以在上班日處理。
雖然有人提到,公司要有好的 on-call 機制,讓工程師更願意 on-call,像是提供加班費、補假、獎金等等。當我們在譴責公車公司讓司機疲勞駕駛時,卻又同時用這些機制鼓勵工程師加班?即便有這些獎勵,每個人的價值觀不同,也不是每個工程師都願意這樣砸自己的腳,我相信多數人不希望在帶小朋友去公園玩時,看到手機簡訊後跟小朋友說:
不好意思,爸爸昨天手賤,硬是要上新版本,但現在出了點問題,所以爸爸現在不能陪妳,得去公司處理點事情。
至於這樣的人多不多?這問題其實很簡單,在面試的時候只要問,您是否願意配合 on-call,即便有這麼多獎勵機制,我保證還是能夠刷掉一大票的人,即便是能力好的人也是有不願意配合的。這是價值觀選擇的問題,只靠獎勵機制說服不同世界的人是有難度的。
我倒覺得,應該要做的是平常多進行教育訓練,畢竟當系統越來越大,不是每個人都會參與所有的功能開發,當 on-call 時遇到不熟悉的系統功能,自然壓力會很大,透過這種方式,盡可能到減輕 on-call 時要面臨的壓力,會比較正向一點,像最近我們就辦了很多次 workshop,講解過去可能都是由資深人員處理的線上問題,讓其他人有機會可以處理。
最後總結,針對這議題,從 devOps 的角度看,團隊應抱有持續不斷地改進的精神,努力降低上版的風險,最後,哪一天上版就僅僅是風險控管的問題了。風險控管除了考量到損失,當然還要考慮到團隊要怎麼 on-call,on-call 的資源夠不夠應付上版後的突發狀況,才能做出適當的決策。
上面說這麼多,那我們有針對 Firebase Real-time Database 會無預警搬移機器這件事做什麼努力嗎?有的,像是之前用 shard,分散資料,讓單一 shard 影響的餐廳數減少。最近則是在進行架構調整,但由於 root cause 是非常底層的資料庫以及整個資料即時同步的機制,所以牽動到的範圍就非常大。不過搭配 feature toggle,我們架構調整的程式碼不是在一個獨立的分支上,而是一直持續不斷地回到主線上,能持續上線其它新功能而不受影響。目前正在 beta 環境上進行大規模的測試。改天有機會再來分享。