演進式架構的必要性#
幾乎每一個知名的 DevOps 典範組織都曾因架構問題經歷過瀕死體驗——LinkedIn、Google、eBay、Amazon、Etsy 都是如此。在每個案例中,他們都成功遷移到了更適合當前問題和組織需求的架構。
Jez Humble 觀察到:「任何成功的產品或組織的架構都必然會在其生命週期中演進。」Randy Shoup 曾在 eBay 擔任首席工程師和傑出架構師,他指出:「eBay 和 Google 各自都在進行第五次完全重寫架構。」
架構決策需要與組織當前的目標相匹配。正如 Shoup 所說:「如果我們一開始就嘗試實現 1995 年版本的微服務,我們很可能會失敗,甚至拖垮整個公司。」
挑戰在於如何持續從我們現有的架構遷移到我們需要的架構。eBay 的做法是先做一個小型試點專案來證明他們理解問題,例如 2006 年規劃將部分網站遷移到全端 Java 時,他們選擇了收入最高的頁面開始,當商業回報不足以支持投入時就停止。
Strangler Application 模式#
eBay 使用了 strangler application 模式——不是「拆除和替換」舊服務,而是:
- 將現有功能放在 API 後面,不再直接修改
- 所有新功能在使用新架構的新服務中實現
- 需要時透過 API 呼叫舊系統
這種模式特別適合將單體應用或緊密耦合的服務遷移到更鬆散耦合的架構。
緊密耦合架構的問題#
當架構變得過於緊密耦合和互相連結時,後果顯而易見:
- 每次提交程式碼或發布到生產環境都可能產生全域性故障
- 每個小變更都需要數天甚至數週的溝通和協調
- 部署批次越大,整合和測試越困難
- 即使部署小變更也可能需要與數百甚至數千名開發者協調
- 開發者可能只花 15% 的時間寫程式碼,其餘時間都在開會
這些都導致了一個極度不安全的工作系統,使小變更具有看似不可知且災難性的後果,並助長了對整合和部署程式碼的恐懼。
從企業架構的角度來看,這種向下螺旋是「架構熱力學第二定律」的後果——IT 專案負責人不需要為整體系統熵的貢獻負責,降低複雜度和提高所有開發團隊的生產力很少是個別專案的目標。
促進生產力、可測試性和安全性的架構#
相較於會阻礙所有人安全做出變更的緊密耦合架構,鬆散耦合架構具有明確定義的介面,能夠促進生產力和安全性:
- 讓小型、高效的 two-pizza teams 能夠安全地獨立做出小變更並部署
- 每個服務都有明確定義的 API,便於測試和建立團隊間的契約與 SLA
- 小團隊可以在更小、更簡單的開發單元上獨立工作
以 Google Cloud Datastore 為例——作為世界上最大的 NoSQL 服務之一,它僅由約八人的團隊支援,因為它建立在層層相依服務之上。
Randy Shoup 指出:「Google 和 Amazon 等擁有這類架構的組織,展示了它如何影響組織結構、創造靈活性和可擴展性。這些組織擁有數萬名開發者,但小團隊仍然可以保持極高的生產力。」
架構原型:單體 vs. 微服務#
在歷史上的某個時間點,大多數 DevOps 組織都曾被緊密耦合的單體架構所困擾。但單體架構本質上並不是壞的——在產品生命週期早期,它們通常是最佳選擇。
三種主要架構原型#
Monolithic v1(所有功能在一個應用程式中):
- 優點:初期簡單、低行程間延遲、單一程式碼庫和部署單元
- 缺點:團隊成長時協調成本增加、模組化難以執行、擴展性差、全有或全無的部署
Monolithic v2(分層式單體,如前端/應用伺服器/資料庫層):
- 優點:初期簡單、Join 查詢容易、單一 schema
- 缺點:耦合隨時間增加、擴展性和冗餘性差、調校困難、全有或全無的 schema 管理
Microservice(模組化、獨立、圖形關係、隔離持久化):
- 優點:每個單元簡單、獨立擴展和效能調校、獨立測試和部署
- 缺點:多個協作單元、多個小型程式碼庫、需要更複雜的工具和依賴管理、網路延遲
產品或服務的功能幾乎一定會隨時間演進,架構需求也會跟著改變。在 1 倍規模有效的架構,在 10 倍或 100 倍規模時很少能繼續有效運作。
案例:Amazon 的演進式架構(2002)#
Amazon 的架構轉型是最被廣泛研究的案例之一。Amazon CTO Werner Vogels 解釋,Amazon.com 於 1996 年以一個單體應用程式 Obidos 開始,它包含了所有的商業邏輯、顯示邏輯和功能。
隨著時間推移,Obidos 變得過於糾結,無法獨立擴展各部分。Vogels 說:「我們經歷了一段認真的反思期,得出結論:服務導向架構能給我們所需的隔離層級,讓我們能快速且獨立地建構許多軟體元件。」
從 2001 到 2005 年,Amazon 完成了從雙層單體到完全分散式、去中心化的服務平台的重大架構轉變。
從 Vogels 的經驗中學到的教訓:
- 教訓 1:嚴格的服務導向是實現隔離的優秀技術,達到前所未有的控制水準
- 教訓 2:禁止客戶端直接存取資料庫,使得服務狀態的擴展和可靠性改進可以不影響客戶端
- 教訓 3:服務模型是創建能快速創新且具有強烈客戶導向的團隊的關鍵推動力
成果令人驚嘆:2011 年 Amazon 每天執行約一萬五千次部署,到 2015 年已達到每天近 136,000 次部署。
使用 Strangler Application 模式安全演進架構#
Strangler application 這個術語由 Martin Fowler 在 2004 年創造,靈感來自澳洲的絞殺藤——它們在無花果樹的高處生根,逐漸向下生長,最終絞殺宿主樹。
如果我們確定目前的架構過於緊密耦合,可以開始安全地從現有架構中解耦功能:
- 將現有功能放在 API 後面,保持不變
- 新功能使用新的目標架構實現
- 需要時透過 API 呼叫舊系統
- 透過 versioned APIs(版本化 API)存取所有服務
如果服務沒有乾淨定義的 API,應該建構 API,或至少在具有乾淨 API 的客戶端函式庫中隱藏與這些系統通訊的複雜性。
透過反覆從緊密耦合系統中解耦功能,遺留應用程式會逐漸縮小,最終甚至可能完全消失。
同時要注意避免單純在新架構中複製現有功能——透過使用者研究,我們通常能設計出更簡潔、更精簡的方式來達成商業目標。
案例:Blackboard Learn 的 Strangler 模式(2011)#
Blackboard Inc. 是教育技術的先驅之一,2011 年營收約 6.5 億美元。其旗艦產品 Learn 的程式碼庫可追溯到 1997 年的 J2EE 遺留系統。
首席架構師 David Ashman 觀察到一個令人擔憂的趨勢:從原始碼庫的圖表中可以看出,程式碼行數持續增加,但 commit 數量卻開始減少,客觀地顯示了引入程式碼變更的難度越來越高。
2012 年,Ashman 使用 strangler 模式實施架構重構,建立了名為 Building Blocks 的模組,讓開發者在與單體程式碼庫解耦的獨立模組中工作,透過固定 API 存取。

Figure 16: Number of developers deploying per week at Facebook
結果引人注目:
- 當 Building Blocks 可用後,單體原始碼庫的大小開始減少
- Building Blocks 程式碼庫的行數和 commit 數量都呈指數增長
- 開發者更有生產力,工作更安全,因為錯誤只導致小型、局部的故障而非全域災難

Figure 17: Daily deployments and increasing release frequency resulted in decrease in outages

Figure 18: High performers had much faster deployment lead times and much faster recovery times

Figure 19: The Deployinator console at Etsy

Figure 20: Blue-green deployment patterns

Figure 21: The canary release pattern
結論#
在很大程度上,我們服務所運行的架構決定了我們如何測試和部署程式碼。Puppet Labs 的 2015 State of DevOps Report 驗證了這一點,顯示架構是工程師生產力和變更安全性的頂級預測因子之一。
由於我們經常受限於為不同組織目標或已過去時代所優化的架構,我們必須能夠安全地從一種架構遷移到另一種。本章介紹的案例研究——以及之前的 Amazon 案例——展示了像 strangler pattern 這樣的技術如何幫助我們漸進地在架構之間遷移,使我們能夠適應組織的需求。