「什麼!服務受到DoS攻擊,資料庫無法負荷交易,服務癱瘓了!?」
一天早晨,聽說別部門服務受到DoS阻斷式攻擊,更蝦趴的是來源是內部IP,實際是因爲客戶端設置錯誤,造成頻繁請求,最終導致資料庫無法負荷交易,間接造成服務處理節點癱瘓失效。
這也造成了我開始接觸Apache APISIX的原因。
那幾天開始與不同部門的IT人員交流,討論該如何避免相似問題再度發生。恰恰那段時間之前,在學習了解微服務的概念,聽過朋友提到過斷路器/熔斷器。
不知道大家有沒有這樣的經驗:再使用高功率電器產品的時候,像是使用吹風機吹頭髮、開個烤箱烤土司、用微波爐加熱便當......然後就跳電了。
這時候需要先把高功率電子產品關閉,再去找到總電閘把電閘打開。
跳電,其實是一種保護機制。電線的電流壓力達到臨界,再增加可能引起電線走火、電器產品毀壞,更糟糕可能引起大型火災,人財兩失。跳電,是為了預防更大的危險。而電閘跳開的機構,就叫做「斷路器」,跳開形成斷路/電流無法形成迴路,電器產品便無法使用。
更早以前家庭使用的斷路器,同時也是熔斷器。熔斷器我在一些產品上還有看過,像是旅用萬國轉接頭。其原理是透過一根低熔點的金屬細絲形成整體迴路,也因此叫做「保險絲」。當電流增加,金屬絲溫度也上升,最後達到金屬絲融化的極限溫度,金屬融化也就無法形成迴路。「保險絲」犧牲自己,用斷路保護後面的家庭與電器。
(圖片來源: https://i.sstatic.net/NrcnJ.jpg 、 https://electronics.stackexchange.com/questions/227432/glass-fuse-selection-rule)
微服務,軟體的「斷路器」也是同樣目的。目的是為了保護上游服務節點,特別是軟體服務經常有自我恢復性,有可能在一段時間休息後就自我恢復。因此「斷路器」的作用,就是為了提供這一段空白的休息時間。
具有斷路器能力的反向代理工具,並不是只有Apache APISIX,Kong Gateway、Tyk也都有相關功能。會採用Apache APISIX的原因其實很單純:選擇上是第一個嘗試的,並且符合需求,也就沒有在嘗試其他工具。
難怪商業課上會討論「先行者優勢」🤔
其實像學習OAuth 2.0時,會使用「Keycloak」一樣,Apache APISIX最初也是個人私下學習為目的的。因此更多的不算是考量其商業價值,而是其原始碼授權和免費資源豐富性為考慮。Apache APISIX已經是Apache基金會下的頂級項目(Top-Level Project),使用友善Apache授權。貢獻至Apache前,項目有華人發起,當時相對豐富的中文資源。並且部落格還有一篇「有了NGINX和Kong,爲什麼還需要Apache APISIX - 韓飛」和BilBil研討會影片。

api-breaker斷路器pluginAPISIX的api-breaker Plugin大致邏輯如下:
「當上游端點回覆狀態碼是不健康的,並達到斷路觸發次數,則引發斷路時間。斷路時間由2秒、4秒、8秒方式倍增,直至斷路時間允許最大值(如300秒),提供上游恢復的休息時間。而當上游標記為不健康狀態,但回覆狀態碼是健康的,並達到回復成迴路的次數,則恢復轉發請求。」
因此需要考慮幾個部分的設定:
接下來的DEMO會更為清楚以上結果。
1. 建立httpbin服務
為了收到「不健康的回覆狀態碼」,並不能像是「新竹百貨公司」的例子,簡單使用http-server。因此我們需要換一個上游,換成httpbin這個服務。你可以直接透過 https://httpbin.org/ 玩玩看,也可在本地透過docker建立起本地服務:
docker run -p 8085:80 kennethreitz/httpbin
或是同樣建立docker-compose.yaml檔案,並用docker compose up啓動服務:
services:
httpbin01:
image: kennethreitz/httpbin
ports:
- 8085:80
2. 設定APISIX路由
建立一個名稱為「httpbin」的路由。這次直接在路由設定上游節點內容:https://httpbin.org:443。你也可以添加/換成自架的httpbin服務http://localhost:8085

另外特別注意超時設定,可以維持預設的6秒即可。這次LAB會透過超時機制實現。

3. 設定api-breakder plugin
| 參數 | 值 | 備註 |
|---|---|---|
| 什麼是不健康的回覆狀態碼? | 504 |
Gateway回復上游超時 |
| 不健康臨界次數 | 3次 | |
| 最大斷路時間 | 300秒 | |
| 什麼是健康的回覆狀態碼? | 200 |
|
| 視作健康臨界次數 | 3次 | |
| 當斷路形成時,回覆給客戶端的狀態碼與內容是什麼? | 回覆503狀態 |
內容「斷路器」 |

要在Dashboard設定斷路器,需要在
apisix_dashboard_config/config.yaml中plugins區段添加api-breaker。
像是:plugins: # plugin list (sorted in alphabetical order) - api-breaker
4. Delay API

這次LAB會利用httpbin的/delay/{delay} API來完成實驗。簡單來說瀏覽 http://localhost:9080/delay/2後,會等待2秒才收到回覆。

Apache APISIX同樣有一個plugin可以模擬超時行爲--fault-injection可以設定
delay.duration
另外注意2秒的延遲情況,狀態碼是200。相對來說,如果延遲超過6秒,也就會觸發APISIX的Gateway超時,並且恢復狀態碼是504。

5. 觸發「斷路器api-breaker」plugin
現在來試試延遲10秒4次看看: http://localhost:9080/delay/10 。會發現回覆狀態碼變成了503,並且內容顯示「斷路器」。再看使用時間,幾乎是秒回。如果回去看APISIX的記錄,會發現它直接回覆,而不將請求轉發給上游處理。

可能要再過個5分鐘(300秒),才會恢復到504的超時狀態。
「斷路器」做法在上游節點已經發生問題時,才會發揮作用。如果對於平常流量使用有了解,知道正常使用情況如何,DoS攻擊又如何,那麼可以設定限流。

在「高乘載管制」已經見過以請求數量限制流量的方式,APISIX還可以依照「請求數量」、「連線數量」進行限制。這節僅針對「高乘載管制」中「請求數量限制」進一步說明。設定的參數如下:

count: 1
time_window: 60
key_type: var
key: remote_addr
rejected_code: 429
rejected_msg: 高乘載管制
show_limit_quota_header: true
首先看到key_type和key的設定,表示針對remote_addr,也就是客戶端請求IP個別限制。如果192.168.56.9被限制流量了,192.168.56.10並不會因此受到影響。
接著看看rejected_code和rejected_msg,這兩個參數顯示當請求數量超過後,APISIX回覆給客戶端的狀態碼和內容。
show_limit_quota_header則允許限制狀態恢復給客戶端了解。
最後是計算的時間窗格time_window和在時間窗格內允許的數量。方便實驗,設置成了60秒的時間窗格和僅1次的數量。
可以同樣對「httpbin」路由套用「高乘載管制」的服務,然後請求 http://localhost:9080/get 一次看看
curl -s -v http://127.0.0.1:9080/get

第一次收到的狀態碼應該是200,並且在回覆的HEADER內可以看到限速狀態訊息。如果在60秒內再發送一次請求,則會收到429的狀態碼,而回覆內容是{"error_msg":"高乘載管制"}。

本章節介紹了兩種上游節點的保護方式:「斷路」和「限流」。不過其實更挑戰的是,應該怎麼設定兩者才是適合的?這可能需要知道平常的使用情況,服務水平或垂直擴展的能力等等。也歡迎有人和我分享適合的做法~