關於街口線上服務監控的那些小事
保持系統的高可用性及高穩定性以支持街口量級的成長
Author
- Dopiz Liu
主要負責支付及應用領域,認為「身為一位測試開發人員的工作職責不僅僅是完成測試項目,而是無所不用其極地保證產品的最高品質」
街口近年來的成長速度飛快,截至目前會員數的增長已超過 500 萬人。為了提供如此眾多的用戶完善的金融服務,我們不斷地拓展新的業務需求,因此內部的基礎設施及服務數量不斷地增加、甚至與第三方合作的功能串接也日益增長。為了支撐這些服務,我們必須時刻保持系統的可用性及穩定性,因此隨時監控各個服務就變得越趨重要。
而在達成穩定性建設目標下,有幾項時間指標可做為參考:
- 平均無故障時間 (Mean Time To Failure, MTTF):各故障 T1 時間的平均值,服務均正常可使用的時間長。
- 平均修復時間 (Mean Time To Recovery, MTTR):各故障 T2 + T3 總和的平均值,從發現服務異常到開始進行修復再到修復完成、服務恢復可用的時間長。
- 平均故障間隔 (Mean Time Between Failure, MTBF):各故障 T2 + T3 + T1 總和的平均值,即是每個故障的間隔時間。
在這些指標中,我們希望將平均修復時間 (MTTR) 縮至最短、平均故障間隔 (MTBF) 拉長,進而提升服務的高可用性。而為了達成前述目標,我們建立了許多監控的機制來觀測服務的健康程度,避免因為感知的速度太慢造成功能不可用的時長過長、甚至造成公司資損等情形發生。
怎麼樣算是良好的監控機制呢?
我們認為監控的方式有非常多種,但良好的監控機制應該能夠依序釐清以下幾個問題:
- 現在服務是否有問題?
— e.g. 支付成功率下降、訂單建立數量減少、銀行綁定失敗率升高
- 是哪裡發生了問題?
— e.g. 訂單建立 API 的 Response Time 過高、對外串接銀行 API 大量 HTTP Status Code 500 發生
- 是發生了什麼問題?
— e.g. 資料庫負載過大、上游業務的 Thread Pool 已滿,導致處理 Request 的性能下降
有興趣的話之後也會跟大家聊聊有關監控體系與穩定性建設的內容,這篇主要就針對我們目前執行 Health Check 的方式來作分享。
要達成 Health Check 的做法從最基礎的透過 command 執行 script (e.g. ping) 來確認 host 是否存活、確認與 Server 是否能正確建立 TCP socket;到程式架構中 framework 或是 Docker、Nginx 提供的 Health Check 機制等等,都有一定的效果。
在街口,除了 Infra 及 RD 團隊內各自使用的工具以外,SDET 在平常的監控場景主要分為三種 — 硬體監控、業務監控、服務監控,而對應所使用的工具分別為:
- Grafana
- Kibana
- Production URL Health Check
這篇的內容會簡述我們使用 Grafana 與 Kibana 的方式,以及實作 Production URL Health Check 的方法及原因。
Grafana
Grafana 是一套開源的分析與監控解決方案,其應用非常多。在我們的應用場域就包含了機器監控、壓力測試數據觀測等。而在監控場景下主要使用它來針對 Service 的硬體指標進行監控,包含最基礎的 CPU、Memory、Disk 使用率,以及 Network Traffic 流量、Socket Connection 連線數等數據。
並且透過硬體規格以及平時流量的經驗法則來設定各個指標的閾值,當有機器的某項指標超過我們所設定的閾值時,就會觸發 Alerting Rule 進而透過 Slack 以及 LINE Notify 通知相關人員進行處理。
Kibana
Kibana 中記錄並呈現了大量的 log 資訊,透過它可以分析、搜尋、視覺化 Elasticsearch 中的資料。而我們透過 filter 針對特定的日誌內容來間接觀測業務指標的數據,例如:購物節活動的每日參加人數、實時的交易成功筆數、AccountLink 逾時紀錄等等。
除了建立 metric/log 的 Dashboard 直接監控實時數據外,同樣的我們也會建立多種業務情境的 filter 及預期的閾值到 elastalert rules 中來進行業務指標的監控及通知。舉例來說,當 15 分鐘內透過 App 進入並瀏覽街享券頁面的用戶少於 1 次時,就會透過 Slack 通知,依此來查看服務是否有任何異常而導致平均用戶流量下降。
Production URL Health Check
最後一項則是監控服務指標的部分,我們建立了一套簡易的機制對這些 Service 的 Health Path 或是負擔最小的 API Path 定期進行 Request 以確保其存活;以及街口 App 內所能看得到的 Banner、各個行銷宣傳網頁甚至是從街口所導流出去的第三方網頁等,我們將期都加入列表中定期進行監控,以便發生問題時,我們能夠快速的將服務、網頁下架止血或是通報外部修復,避免使用者體驗不佳。
以下會介紹我們組成該機制所使用到的工具:
✔ ️Google Sheets
為什麼選擇使用 Google Sheets 作為儲存監控列表的工具?
起初其實只有針對特定幾個非常重要的 Service 進行監控,於是我們將這些 URL Path 直接寫死在 Code 之中。但很明顯地當我們開始有新增、修改或是暫時 Skip 特定監控的需求時,每次都要 Push Code 就會變得非常麻煩,並且一定要透過特定的 VPN 管道才能夠進行處理。
所以後來我們考慮了資料庫及 Google Sheets 兩種方案,衡量下選擇 Google Sheets 的原因是因為我們希望非技術人員也能夠快速地新增、查看目前進行監控中的服務,並且表格的特性也可以將其轉為 .xlsx 或 .csv 檔案提供給 Test Script 作為測資的來源。
實際的使用方式可以參考 Google Developers 的文件,主要就是在 Google Cloud Platform (GCP) 建立一個新的 Project,並且啟用 Google Sheets API 的服務、建立一組憑證金鑰 (credentials) 以提供後續撰寫的 Script 使用。
✔ ️Pytest
選擇 Pytest 的原因主要是基於我們 API 自動化測試的框架選型就是採用該方案,能夠復用舊有的 Code 降低成本是我們首要的目標。關於 API 自動化測試框架的這個題目,之後我們會專門寫一篇文章與大家分享,有興趣話的可以先參考之前發布的文章 — 街口支付軟體測試自動化方案選型。
先列出幾個在這次實作中的思路給大家參考,若有推薦更好的方式也請不吝賜教!
- conftest.py
有使用過 pytest 框架的夥伴應該都清楚 fixture 與 conftest.py 作為 setup/teardown 的使用方式。而在實作中,Test Script 執行之前會先做一件事情:
「透過 Client API 取得我們放在 Google Sheets 的監控列表,並且將其作為 Excel 檔案儲存在本機端」
會將檔案存在本機端再餵給 Test Script 進行測試的原因,是因為即使 Google 身為全球數一數二的服務供應商,也無法確保其提供的服務在每時每刻都是正常的。為了降低對外部服務的依賴,每次成功取得監控列表後,都將其儲存在本機端。若是某次執行時遇到錯誤而導致無法從 Google Sheets 取得最新的監控列表,至少還能夠使用前一次儲存下來的資料來執行測試,避免監控會因此而中斷。
另外,將 Slack 通知的 channels 放到 command option 則是因為目前所有的監控通知我們都統一推播到同個 channel。倘若未來有其他需求,可能也會選擇將其放到監控列表文件的欄位之中,如此一來各個 Service 的異常通知就可以分別推播到指定的 channel 中。
- base_api.py
透過 requests lib 封裝後,用來進行 API 請求的基礎類別物件。在我們的自動化測試框架中,各個 Service/Component的 API 會繼承該類別、有另外的衍伸,之後的文章會再分享給大家。
而當服務的響應時間過慢或是無回應時,對用戶體驗來說也會非常扣分,所以響應時間同樣也必須納入我們關注的指標之一,透過 timeout 的設置就能夠輔助我們來觀測這個指標。除了有預設好的閾值外,也可以在監控列表中針對各個監控目標設定不同的閾值。
- Test Script
實際測試執行的流程則是透過 pandas 讀取 Excel 檔案後,透過 pytest.mark.parametrize
的 decorator 將各個測資傳入 Test Function 中。其他資料型態處理或是測資轉換的內容就不放在範例裡面,可以依照各自的需求延伸出各種方案。
進入 Test Function 後會先確認該測試案例是否有被 skip (is_run),再來就會針對 url 進行 request。以最基礎的確認來說,會驗證 Response 的 HTTP Status Code 是否為 200、以及前述提過的 timeout、duration 時間,其餘非預期的 Exception 也都會包起來以便在測試 Fail 時執行我們想要的行為。若有其他 Assertion 需求也可以自行擴充。
️✔ Jenkins
建立完 Test Script 後,當然就是考慮如何定期執行來達到 Health Check 的效果。無論是透過 cornjobs、schedule task 或是 Jenkins 等工具都可以達到此目標。
而我們選擇了 Jenkins 的定期建置來執行,原因也很簡單:
- 目前內部自動化測試的流程也大多是採用 Jenkins 來串通鏈路
- 除了執行 Test Script 外,在測試結果出爐後我們仍有其他的動作要執行,例如:將結果記錄到資料庫留存、產生 Test Report 等
- 原先有使用 Jenkins Slack Notifications 的 Extension 來達到通知效果
為什麼會講原先呢?因為當時我們使用 Jenkins 來進行 Notify 時,它只會根據 Job 的 Build Status 來告知這次的執行結果是成功或是失敗,但沒有辦法立刻看出來實際發生問題的是哪一個 Service 的測試案例,導致我們需要進到 Jenkins 的 Console Output 中、或是另外透過 Allure Report 的內容才能知道是哪一個服務發生異常。
️ ✔ ️Slack Notification
為了解決前面提到 Jenkins Slack Notifications 的痛點,我們直接透過 Slack 提供的 API 建立了一隻通知用的 Slack Bot。實際建立的方式可以直接參考 Slack API: Applications 的文件,只要建立好 App 後透過 Python Slack SDK 撰寫 Script、使用 Bot User OAuth Token 串接,就可以輕易達成。
除了推播到指定的 Slack channel 外,也可以 mention 指定的用戶來達到更有效的通知手段。
另外也推薦可以使用官方提供的 Block Kit Builder 來組建符合自己需求的訊息樣式,以呈現更完整的資訊。
這些監控的機制仍有許多能被優化或是擴展的方向,像是以目前設計的 URL Health Check 除了針對 HTTP Status Code 進行確認以外,之後也能夠拓展至關注 Response 回應的內容是否正確、確認 Remote Config 是否符合預期、透過 Kibana 的日誌記錄以發覺更多面向的業務價值。累計下來的數據紀錄也可以用來進行統計與分析,嘗試找出系統瓶頸或是不易發現的異常狀態。
建立了以上的監控機制後,街口對於服務異常的感知能力又大幅提升了幾個檔次,不再像以往都必須等到用戶輿情反應或是客服回報後才進行處理。而透過每次的異常紀錄也可以發現服務異常的平均修復時間 (MTTR) 明顯縮短,也因此客服人員處理的案件數量大幅減少、同時有效的降低維運成本。在街口服務日漸壯大的情況下,系統的穩定性會變得更加難以掌握,所以一旦服務出錯,能夠及早發現、快速止血就變成了街口最重要的小事。
JKOPay SDET Team
2021.11.25