上週比較忙,所以只整理了一篇較短的舊工作筆記。

由於專案環境的緣故,在某個專案遇到無法按往常慣例為服務設置多重的監控措施。經過討論後,最終在此專案使用了一種不太平常的做法來實現「只設置一項監控 Rule,即同時確認 NGINX、PHP-FPM、MySQL 三者是否正常運作」。該做法是透過 Cron job 定期執行自動化腳本,該腳本會啟用 Selenium 來實際登入網站,並進入特定網頁,最後根據是否能回傳正確的網頁結果,來判斷該服務是否仍正常上線。

(本文同步發表於 Medium。)

前言

如果要確認一個網站是否仍正常運作,我們可以使用多種不同的工具來監控它,例如 Zabbix 的 Web Scenarios、pingdom.com 的 Website Monitoring 或其他各式各樣的 Monitoring tools。

除了直接監控網站之外,有些人也會選擇以監控 Server 上的 Web service(例如 NGINX)或其他 Service 的 Status 是否正常,來確認網站是否仍正常運作。不過有經驗的人都知道,導致網站故障的原因很多,Web service status 正常,但網站依然故障也是有可能發生的。因此更多的人通常是並行多種層面的監控措施,並且設置合宜的 Dashboard 一覽從使用者送出 Request 開始直到 Response 回覆使用者為止的整條路線;倘若網站發生故障,只要一覽 Dashboard 何處出現紅燈,就能迅速定位故障問題點。例如 SRE Taiwan 社群的 Earou Huang 就曾經利用 Serverless Framework 做了一個可以監控系統內部各節點健康狀況的 Project - draft-mesh

(艦長也曾經試用 Earou 的 draft-mesh 弄了一個 Dashboard。)

總之要確認網站是否仍正常運作的方法很多,可以搭建複雜的監控工具,反之也可以撰寫 shell script + curl command 來快速實現簡易的監控。

而這次專案遇到的狀況,簡單來說又是一個非我方負責維護的 Server,因此無法隨心所欲的在 Server 上安裝並設置各種監控措施。最終為了簡易達成「只設置一項監控 Rule,就能同時確認 NGINX、PHP-FPM、MySQL 三者是否正常運作」的目標,在這個專案上採用了一種不太平常的方式來確認其運作狀態。

即是使用 Selenium 模擬網頁瀏覽器,透過腳本來自動登入該網站後台之特定 User 帳號,接著進入事先規劃的網頁,最後回傳網頁的特定內容。如果回傳的內容與監控 Rule 設定的條件相同,即代表該服務仍是正常運作。

過程紀錄

Selenium 是自動化測試領域的老牌工具之一,因此想要透過自動化腳本來自動啟用 Selenium 並執行各種動作已經有非常多參考資料了,以下只記錄當時留在筆記內之土法煉鋼的作法。

## 讓 Selenium 自動開啟特定的網頁,登入並前往指定的網頁。
    
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
    
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
browser = webdriver.Chrome(chrome_options=chrome_options)
    
## https://your_website/backend/login 改成你想要監控的網址
browser.get("https://your_website/backend/login")
    
## 以下腳本是讓 Selenium 輸入 User 登入的帳密,自動登入網站
## 因此要根據網頁,設定正確的 find_element 及動作
browser.find_element(By.NAME, "user_account").send_keys("monitor")
browser.find_element(By.NAME, "password").send_keys("monitor_password")
browser.find_element(By.CSS_SELECTOR, ".btn").click()
    
## 登入可能需要一點時間,因此讓 Selenium 等待一下
wait = WebDriverWait( browser, 5 )
    
## https://your_website/backend/service/status 是成功登入後要去檢查的指定網頁
browser.get("https://your_website/backend/service/status")
wait = WebDriverWait( browser, 30 )

## 取出網頁的 Title,當然也可以取出網頁上其他特定區域的字串
page_title = browser.title
    
## 印出字串
print(page_title)
    
## ...略... 可以繼續執行其他想要執行的動作
  • 撰寫 shell script 來執行 Docker container,並檢查結果。
#!/bin/bash
    
## 強制刪除未中止的同名 Container
docker rm -f monitor-selenium
    
## 進入存放前述 Python 腳本的資料夾    
cd /automation-script/docker-python-chromedriver
    
## 運行 Container
docker run -i -d --name monitor-selenium -w /usr/workspace -v $(pwd):/usr/workspace joyzoursky/python-chromedriver:3.7-selenium bash
    
## 執行 Python 腳本
docker exec monitor-selenium python monitor.py

## ... 略 ... 後續可以判斷 Selenium 輸出的結果為何,
## 然後決定是否要送出 Alert 通知管理者
  • 設定 Cron job 定期執行前述之 shell script。

結語

如前面內容所述,要監控網站是否正常運作,我們可以(也可能需要)從多個層面設置不同的監控點,畢竟從 Request 直到 Response 中間會經過多個節點,每一個節點都有可能遇到故障。

因此,如果我們能透過自動化腳本,真實的模擬使用者操作網站,只要操作過程皆順暢,並且能得到預期的結果,即能代表該網站目前是運作正常的。當然這個推論是有漏洞的,因為不同網站的架構是不相同的,如果是複雜的大型網站或服務,就不一定吻合這項推論。但本文的專案,該網站背後的架構只有 NGINX + PHP-FPM + MySQL,也沒有使用前後端分離架構。所以如果能順利登入網站、進入特定頁面,必定是 NGINX + PHP-FPM + MySQL 三者都正常運作的情況。

同樣在前面的內容也有提到,Selenium 本來就是自動化測試的老牌工具,在自動化測試的領域已經有多種幫助測試人員運用 Selenium 進行自動化測試的方法;例如 Selenium IDE 即可讓人直接錄製自己是如何操控瀏覽器、按過哪些按鈕、輸入哪些表單欄位,並且將錄製完的動作輸出成可以放進 CI Pipeline 中執行的測試腳本,屆時在 CI Pipeline 就會以 Selenium 重現動作完成測試。因此嚴格說來本文的做法並非創新或不常見,說穿了就只是把既有的自動化測試手法拿來用在 Production 環境作為服務監控之用。

最後,若是能選擇,我還是比較想要使用一慣的監控方式,為網站設置多重的監控措施,從多個面向確認服務的運作狀態,本文的做法實在是受制於當時專案的特殊情境,而不得不為之的特例。附帶一提,後來該專案也改掉此種特例做法,所以說人果然還是要走回正道才行啊。(喂~

參考資料