數位五倍券開放預約登記,結果造成多家銀行業、電支業的預約登記系統停擺。對於此類的狀況,本文提供了12項可擴展的架構設計原則,並配合現況架構藍圖,將能夠逐層優化解決人數爆量所造成系統停擺問題。
數位五倍券在2021年9月22日早上九點開放預約登記,造成多家發卡量大的銀行業、電支業的預約登記系統停擺,甚至有銀行連官網和網路銀行都受到影響,當天還被民眾笑稱是國家級壓測日,為何已有多間銀行導入PaaS容器平台,甚至是雲原生微服務架構,卻還是發生服務壅塞,火燒連環船,想搶優惠回饋卻敗興而歸,導致民眾罵聲連連。
應用系統採用PaaS容器平台條件
在前期文章中,說明了使用PaaS容器平台的應用系統特點,具體評估方式如表1,評估表可迅速判斷應用系統容器化的條件。
但是,容器化不代表可橫向擴展,且不是只有容器可以橫向擴展,在AWS的EC2虛擬機服務就有VM Auto Scaling Group,但使用橫向擴展的應用程式都必須是無狀態,這樣才不會造成資料不一致,因VM Auto Scaling Group前面有Load Balancer 將負載分攤到每台VM,因此為了可擴展性,不是只有容器需要改寫程式,虛擬機也是一樣。
早期最成功的PaaS平台Heroku,其開發團隊整理出十二條開發SaaS應用程式時的方針The 12 factor App - https://12factor.net/。
其中第六條,執行序(Processes),談到應用程式必須以「無狀態」的方式執行(圖1),無狀態(Stateless)指的是應用程式起始前不會有任何的預設狀態,每個Request在執行完後,執行過程中所用到的狀態(State)都不會被儲存下來,早期HTTP就是最典型的例子,之後為了儲存使用者狀態則需要透過Cookie或HTTP Session。
為了可以橫向擴展服務,其容器內服務必須是「無狀態」,若有需要保留的資料一律由後端服務(Backing Service)來儲存,例如資料庫或是快取記憶體(Cache),當容器橫向擴展後,服務會由多個Pod(容器)一同運行,故無法預期下個Request會由哪一個Pod來負責執行,唯有透過共通的Backing Service,才可以確保資料的完整性。例如,一般常用的HTTP Session,就不直接儲存在容器記憶體內,而應該利用統一可自動消失(Time to Live,TTL)的快取服務,如Memcached或Redis來儲存。
由於Web系統已經從傳統3-tier變成N-tier分散式系統,若有使用應用層效能監控(Application Performance Monitoring,APM),當監控到特定業務邏輯成為瓶頸點時,就須考慮把這業務邏輯切出成為另一個單獨服務,讓此單獨服務數量增加,服務與服務間可採用REST API、gRPC同步式,或非同步方式溝通。
如圖2所示,當發現Challenge/Users成為整個系統壅塞的原因,若整個系統未拆分再啟動另一個實例,則Gamification服務其實只要一個實例就可讓系統順利運作,因此占用浪費了1 Core CPU和400MB RAM,再有拆分情況下,只要單例多啟用Challenge/Users服務,便可負載目前的使用量,整體用了5 Core CPU和2,000MB RAM,比單體式擴增,節省1 Core CPU和400MB RAM。
可擴展的微服務架構設計
微服務依然不是資訊系統的Silver Bullet,Gartner在2021/08/20發表一篇技術文章「Should Your Team Be Using Microservice Architectures?」(https://www.gartner.com/smarterwithgartner/should-your-team-be-using-microservice-architectures),表示開發人員對微服務架構越來越失望,困擾軟體開發團隊的兩大問題是複雜性和組織文化不相符,複雜性是因為微服務必須嚴格獨立才能獲得架構優勢,開發人員必須採用新模式並遵守眾多架構約束以確保微服務實際上是獨立的。至於組織文化不相符,成功取決於改變團隊結構、提高分散式運算技能以及使用成熟的敏捷和DevOps實踐。
微服務大多是Web應用系統,其設計步驟就是解耦(Decoupling),將原本Web伺服器->應用伺服器->資料庫,傳統三層式架構找出瓶頸處,再加入中間層服務,逐步拆解整個架構,達到可橫向擴展應付業務量成長。以下針對每一層來解說:
1. Web伺服器:從原本Server-side動態網頁改成前後端分離的SPA(Single Page Application)或是Mobile App,拆解成靜態網頁檔案和動態內容API,且靜態檔案內容還可透過CDN(Content Delivery Network)託管,讓公有雲服務來傳送這些靜態檔案傳輸,避免占用到本地端頻寬。
2. Session認證機制:為了降低Mobile App的行動網路傳輸成本,且配合後端RESTful API的無狀態原則,使用者登入後的認證資訊就不適合存放在伺服器端Session,需要改成Client端的JWT(JSON Web Token)認證方式,每次檢查其Token內容是否正確,是否逾時。
3. 應用伺服器:從XML/SOAP服務,改成輕量簡單的JSON/REST API,且API內容配合前端AJAX渲染網頁,調整成Entity操作,並且可以依照流程,將使用量大但異動少的Entity內容使用Redis快取儲存,減少應用伺服器和資料庫的負載,並達成資料外部化或無狀態化,這樣應用伺服器才能橫向擴展,專注在業務邏輯運算。
當特定業務邏輯成為瓶頸點時,就須考慮把這業務邏輯切出成為另一個單獨服務,讓此單獨服務數量增加,服務與服務間可用REST API或gRPC溝通;若這外部相依服務成為瓶頸時,可考慮使用非同步方式,如圖3所示,(1) Client使用結帳API,(2)結帳API寫入Queue,(3)透過topic訂閱的對外專用API收到通知,(4)對外專用API呼叫Partner信用卡API,(5)外部信用卡API回傳結果,(6)對外專用API收到結果寫入資料庫,(7)結帳API查詢交易結果。不只是外部API可採用非同步方式,像Email發送或App推播也可相同模式來設計,因發送結果與交易無關,採用Fire & Forget方式可避免同步API造成回應時間過久。
4. 資料庫:當Web伺服器和應用伺服器都可橫向擴展後,資料庫就會成為瓶頸,連線數量增加,CPU衝高,回應時間過久,但資料庫是無法輕易擴展,這涉及到資料一致性和分散交易等複雜問題,因此有三種非同步方案可解決資料庫瓶頸問題(圖4),A方案是先寫到Queue,以Queue來當緩衝,資料寫入API再逐步寫入資料庫,為加速甚至可以增加資料寫入API的數量;B方案是利用Cache I/O頻寬大的優勢,先將資料寫入到Cache,適合預約登記或搶紅包的業務情境,避免所有Requests都打到Queue(ESB)和DB,再以整批方式讀取Cache資料寫入資料庫,這可降低資料庫連線數,避免共用資料庫時,影響到其他系統;C方案也是利用Cache I/O頻寬優勢,但寫入資料庫是採用CDC(Change Data Capture)方案,監看Cache變化,自動依照對應規則寫入到資料庫表格內,因此無須實作資料庫寫入API。
資料庫的操作大多是讀取,約占八成,寫入占兩成,80:20法則,因此為了減緩資料庫的負載,可以設計讀寫分離架構(Command Query Responsibility Segregation,CQRS),如圖5所示,1~4步驟是圖4的寫入A方案,早期多採用API分別寫入DB和Cache的A方案,但這資料寫入API需要控制兩者一致性,隨著異質CDC方案出現(Debezium),B方案雖增加CDC元件,卻可減少資料寫入API的程式量,當須寫入Cache的資料量持續增加時,B方案維護成本會是比較低的,建議大型系統採用B方案。
可擴展的架構原則
透過上述「可擴展的微服務架構設計」一節可知微服務架構設計就是不斷優化傳統Web三層式的功能角色,逐步增加中間層,透過分工解耦,達到強韌反脆弱的高可用。接下來的架構原則是參考「Scalability Rules: Principles for Scaling Web Sites, 2nd Edition」書中所列準則,篩選重點,並以三維構面Scale Cube來分類拆分,其中XYZ軸分別表示(圖6):
X軸:水平擴展,即將服務和數據複製多份。
Y軸:根據功能或業務來對應用進行拆分,例如微服務。
Z軸:依據服務或是數據進行Sharding分區。
X軸擴展原則
擴展解決方案最常用的方法是在負載均衡器(也稱為X軸擴展)後面運行多個相同應用系統副本,這是提高系統容量和可用性的好方法。使用X軸縮放時,每個伺服器都運行相同的服務副本(如果已分解)或單體系統。
X軸的好處是,它通常易於實現,並且從交易角度來看很容易擴展,實現X軸的障礙包括大量與Session相關的訊息,這些訊息通常難以共用或需要持久儲存到外部伺服器,這兩者都會導致可用性和可擴展性問題。而X軸的缺點是,雖然易於實施,但必須完整複製數據資料,這會增加儲存成本。此外,每個副本都會存取到所有數據,可能導致大量重複的快取資料,增加記憶體成本。最後,X軸無法讓人員組織規模擴大(圖7)。
Y軸擴展原則
Y軸擴展專注在SOA架構、微服務或單體的功能分解,依據服務邊界或領域模型來分離服務和數據,這種拆分每個服務彼此是不同的,常見範例包括:將瀏覽網頁中拆分搜索功能、獨立購物車的結帳功能和使用者帳戶的登錄功能等,在實踐拆分時,Y軸縮放是將單體應用系統拆分為一組服務。此外,每個服務都應該有本身、不可直接存取的資料庫,以確保高可用性和故障隔離。Y軸縮放與多個資料庫增加了交易量可擴展性的好處。
另外,由於Y軸允許對程式碼和數據的所有權來進行團隊分工,因此提高了組織人員的可擴展性,而資料快取應該隨著數據和服務在適當分段點增加,但是缺點是服務拆分就需要重構系統,且更多的服務,複雜度也會隨數量呈指數增加,維運成本亦會增加(圖8)。
Z軸擴展原則
Y軸處理不同服務的拆分,沿著業務領域或服務邊界,Z軸則是單一服務的分割或Data Center異地多活(AA mode),例如可依照customer_id取mod將單一表格拆成多表,或是轉移年份久遠的舊資料到另一台資料庫,避免筆數造成資料存取瓶頸;也可利用IP地理位置將客戶導向同一地區的Data Center達到客戶分流;產品目錄可以按照SKU拆分,內容可按content_id拆分。Z軸縮放提高了交易可擴展性,並且提高可用性,因部署到伺服器的系統程式在Z軸上每個副本都是一樣的(但數據是不同的),所以無法增加人員組織的可擴展性,加速系統開發,加上資料快取可讓伺服器使用較小的IaaS規格,降低硬體成本支出。
圖9解釋了Z軸可擴展性的優缺點,並顯示了一個故障隔離的高可用架構,在美國有2個獨立的以客戶切分的機房Pod,在歐盟有2個。而Z軸的另一個好處是可對Pod進行分割,以便符合當地的個資法,例如歐盟的GDPR。
善用工具原則
「當你手上只有槌子,每樣東西看起來都像釘子」,正如小朋友剛拿到槌子玩具,就會想東敲敲西敲敲;遇到資訊系統效能問題,程式開發人員就會想寫程式來解決,系統管理人員就會想加硬體資源來解決,資料庫管理員就會想優化SQL或Index設定來解決,沒有一刀切的根本解決辦法,經常是優化了資料庫,就發生伺服器效能問題,升級了硬體才發現是程式用錯演算法,執行時間複雜度太久。面對後疫情時代,多數企業面臨轉型和創新的兩難問題,過度專注在既有業務需求,資訊單位缺乏試驗和採用新技術或新工具的時間和人力,結果導致傳統技術和工具過度使用,技能嚴重鎖定而沒有勇氣和能力去解決,就如圖10的謎因圖,忙於工作而錯失改善的機會。
結語
就像作業系統經典教科書使用恐龍作為封面,暗喻作業系統在電腦科學領域是歷史久遠且逐漸演化成複雜的大型人工產物;大型企業的資訊系統複雜程度也不亞於作業系統,因資訊專業分工細化,且隨著人員來去轉換,每個IT部門只瞭解各自負責的專業範疇,卻不知上層架構設計或異動會造成底層系統效能問題,且沒有一張目前企業內整體資訊系統的架構圖,沒有方向當然也無從下手去改善系統效能問題,雖然本文提供12項可擴展的架構設計原則,仍須配合現況架構藍圖,才能逐層優化解決五倍券預約人數爆量所造成系統停擺問題。
<本文作者:鄭淳尹,Docker.Taipei社群共同發起人,台北富邦銀行雲端系統部架構師,曾任微軟MVP、國泰金控技術架構師、momo購物網架構師、臺北榮民總醫院資訊工程師、玉山銀行資訊處專員、宏碁eDC維運工程師。開源技術愛好者,曾在多間大學資工系擔任Docker容器技術講師,並翻譯審閱多本容器技術書籍。>