閒談軟體設計:備援

更新 發佈閱讀 9 分鐘

2025 年的雲端十分不平靜,感覺綠色乖乖可能又會大賣了,六月 12 日 GCP + Cloudflare 大當機,十月 19 日 AWS 癱瘓,沒想到一個月後,十一月 18 日換成 Cloudflare 掛掉,前兩次我們的服務都沒受到影響,算是運氣不錯,但第三次運氣就沒那麼好了,只能說我們讓服務比 Cloudflare 修復前更早就恢復運作,值得慶幸。

事件經過

就用時間軸回顧一下當晚的處理經過吧 (以下時間都是台北時區)!

  • 21:27 Slack 客服群組回報官網跟商家用的管理頁面無法使用
  • 21:29 確認 Cloudflare 異常
  • 21:35 確認員工使用的 App 和主管使用的 App 仍然正常
  • 21:51 確認有經過 Cloudflare Proxy 的流量都斷了,其他都正常
當初 API 不想讓 Cloudflare 快取,導致奇怪的問題發生,決定不要 Proxy,這決定讓後來我們的服務沒有全部都掛掉的關鍵,員工和主管仍可以透過 App 使用部分功能,這也是一種運氣。
經過 Cloudflare Proxy 的流量異常,其他正常 (Icon 來自 Freepik、Cloudflare 和 GCP)

經過 Cloudflare Proxy 的流量異常,其他正常 (Icon 來自 Freepik、Cloudflare 和 GCP)

  • 21:56 不確定 Cloudflare 還要多久才能恢復,決定先切換 DNS 到 GCP Cloud DNS
  • 21:58 切換完畢
切換到 GCP Cloud DNS (Icon 來自 Freepik、Cloudflare 和 GCP)

切換到 GCP Cloud DNS (Icon 來自 Freepik、Cloudflare 和 GCP)

  • 22:03 官網恢復正常,但商家用的管理頁面能使用,但 SSL 憑證異常
  • 22:07 更新 SSL 憑證
  • 22:28 新憑證生效,所有服務恢復正常

大約半小時,讓服務可以開始使用,只是瀏覽器會抱怨連線不安全,再過半小時,Cloudflare 還完全恢復正常前,我們的服務就完全恢復正常了。

這次的轉換之所以能這麼快,有一部分是屬於運氣,因為在今年年初服務一上線時,DNS 用的就是 GCP 的 Cloud DNS,所以上面有所有服務完整的紀錄 (records),後來因為 Cloudflare 能提供一些功能 (來源 IP、IP 的地理位置等等),所以把 DNS 改成 Cloudflare,但 GCP 上的 Cloud DNS 的紀錄並沒有拆掉,包含原有的 SSL 憑證都保留,所以能夠直接切換過去。

備援設計

唯一比較沒那麼順利的是,代管的 SSL 憑證竟然沒自動更新而過期,這就怪了,只好手動更新,這裡使用了兩個備援,一個是 DNS 紀錄,另一個是 SSL 憑證,在準備切換到 Cloudflare 時,曾考慮把 CDN 前的 Load Balancer 的 SSL 憑證拆掉,反正 Cloudflare 會提供,但後來想想算了,就維持著吧。沒想到就變成我們能提前恢復正常的因素之一了。

在求學的過程中,常會提到系統要有備援,才能提高容錯能力,但說真的,備援卻不是容易做的事,至少需要考慮幾件事:

  • 成本。這是最容易察覺的,多一台機器,即便是使用雲端服務,不需要購買硬體的費用,但啟動一台 VM,或一個容器,都是以時間計費的,就算每單位成本低,累積久了也是不少錢,更何況正式環境用的機器等級通常也會比內部開發測試用的環境好很多,那加上備援的費用就會更高了。我們很珍惜使用投資者給予我們的經費,因此,我們的正式環境,只有面對客戶的服務,才會保底最少兩個 Pods,內部營運用的服務就一個 Pod,若故障,得自己去重開。
  • 同步。運算類的服務,如果能確保 Stateless (無狀態),起多個備援或是起多個服務來應付大流量都不是問題,但是像資料庫,就是個大麻煩,因為資料要同步到備援,當其中一個掛了,另一個才能順利接上,但同步本身就是一件難事,特別是要做到幾乎零時差的同步,最近一些雲端服務商開始提供 Active-Active 或 Active-Passive 的備援機制,省去自己架設和管理的麻煩,只要口袋夠深,開下去就行了。
  • 複雜度。當服務都有一個以上的備援時,整個系統架構會變得很複雜,光是讓流量可以導向正確的機器,當發生異常能正確地隔離與切換,都是不容易的事,只能說過去雲端服務的出現,讓很多麻煩的事都交給雲端服務商去處理,透過設定就能享受到許多原本要自己建構的備援機制,但帶來的缺點就是,受制於雲端服務商,當雲端服務商掛掉時,通常只能束手無策。
  • 可行性。這次,正好我們不是透過 Cloudflare 購買我們使用的 domain,而販售 domain 的服務商也沒使用 Cloudflare,這才讓我能夠切換 name server。雖然 name server 能有多個避免點點故障 (single point of failure),即便 name server 同時加上兩個 DNS,但 Cloudflare 這次 DNS 是正常的,也不會主動切換到第二個 DNS。事實上,在網路的世界中,不是所有的節點都可以加備援,或是加備援也不一定有用,更別說被框架綁架時,在備援的選擇上就更少了。
  • 演練。假設系統的備援都設計得很好,但莫非定律告訴我們,所有的設計都有可能沒照原訂的計畫執行,例如 GCP 代管的 SSL 憑證不知為何沒自動更新之類的,所以團隊除了開發和上線新功能,有時候也需要演練一下當什麼狀況發生時,要怎麼修復系統,越熟練時,才能在突發狀況發生讓備援越快上線,不然備援也只是擺在那邊好看的。

因為要考慮到很多因素,也可以理解為什麼明知備援是能提高容錯的好事,卻不見得是每個服務都會做到完整的備援了。

事後檢討

當晚雖然提早讓服務恢復正常,但事後檢討起來,還是有兩件事要處理

  • 根據 Cloudflare 的事件報告,Cloudflare 在 19:20 便開始出現異常,但我們是到了 21:27 透過客服回報才發現異常,中間有兩個多小時,其實不知道我們的服務究竟是否正常。雖然 Cloudflare 的報告中指出到 21:20 前,Cloudflare 呈現時好時壞的狀態,一直到 21:20 才開始全面異常,時間好像對上了。當晚我們決定加上 uptime detector,過去我們的 monitor 都專注在 GCP 內部,但這次 GCP 內部都正常,而是對外連線異常,如果沒有從外部監控,是無法發現的。還好現在也有現成的雲端服務,幾分鐘就架設好兩個,沒錯,兩個不同服務商提供的 uptime detector,並提供精美的 status page。
  • 追蹤 SSL 憑證更新的問題,雖然手動更新 SSL 憑證不難,但 provisioning 需要時間,完成 provisioning 到更新至瀏覽器這端也需要時間,如果是在服務異常時才手動更新,會延長異常狀態的時間,最理想就是在還沒過期前就更新完畢,這邊會發個 ticket 問一下 GCP 了解一下狀況。
raw-image

小結

備援會讓系統的容錯度變高,但同時也會增加複雜度和成本,因此,備援的採用與否,以及如何設計,要根據你提供的應用服務是屬於哪種類型,若是偏 infra 層級 (這應該都被 Google/AWS/Microsoft 做掉了),那勢必要仔細設計,如果是 critical 服務,像是金融等等,那也要好好設計,但如果是 non-critical 服務,掛掉幾小時雖然會造成麻煩但不會死,那也許就不需要那麼高等級的備援。

另外,就是看打算提供多高的 SLA 了,以上述的三次大事件,GCP、AWS 和 Cloudflare 今年都無法達到 99.5% 的 SLA (一年只能停機 3 小時 39 分 8 秒),所以,大家覺得,一般的應用服務,SLA 的目標應該訂在多少比較合理呢?

題外話

沒記錯的話,應該是 2018 AWS re:invent 研討會吧,講者在說明 SLA 的計算時提到,如果你的應用服務用到三個 SLA 99.5% 的雲端服務,那你的 SLA 最多只會到多少?答案是 98.5% (0.995 * 0.995 * 0.995),四個就更低了,只剩下 98%。所以,哪天聽到有人說達到 99.5% 的 SLA,可以檢視一下他們使用幾個雲端服務,以及個別的 SLA 是多少,就可以知道是不是在椪風了。

留言
avatar-img
Spirit的沙龍
58會員
116內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
Spirit的沙龍的其他內容
2025/06/29
本文探討後端 API 安全驗證機制,說明如何基於請求憑證進行身家調查,確保只有授權使用者才能存取資源。文中詳細闡述了資料採信原則、角色來源、組織層級的設計考量,並介紹簡單的實作範例,以確保系統安全。
Thumbnail
2025/06/29
本文探討後端 API 安全驗證機制,說明如何基於請求憑證進行身家調查,確保只有授權使用者才能存取資源。文中詳細闡述了資料採信原則、角色來源、組織層級的設計考量,並介紹簡單的實作範例,以確保系統安全。
Thumbnail
2025/06/22
本文探討如何使用函數式風格聲明 Javalin API endpoint 的安全性需求,並透過組合函數,例如 any 和 all,以及自定義函數,例如 anyManager 和 anyOwner,來簡化複雜的權限檢查。此方法避免了使用註解的繁瑣,並提高了程式碼的可讀性和可維護性。
Thumbnail
2025/06/22
本文探討如何使用函數式風格聲明 Javalin API endpoint 的安全性需求,並透過組合函數,例如 any 和 all,以及自定義函數,例如 anyManager 和 anyOwner,來簡化複雜的權限檢查。此方法避免了使用註解的繁瑣,並提高了程式碼的可讀性和可維護性。
Thumbnail
2025/06/15
從Spring Boot轉換到Javalin的過程與考量,以及如何保持核心業務邏輯與Web框架的距離以提升專案彈性。文中比較了Micronaut, Helidon和Javalin三個輕量級框架,並說明選擇Javalin的原因及優缺點。
Thumbnail
2025/06/15
從Spring Boot轉換到Javalin的過程與考量,以及如何保持核心業務邏輯與Web框架的距離以提升專案彈性。文中比較了Micronaut, Helidon和Javalin三個輕量級框架,並說明選擇Javalin的原因及優缺點。
Thumbnail
看更多