單體服務到雲端的遷移實踐

作者|Luciano Mammino  譯者|明知山

策劃|丁曉昀  

來源公衆號 | InfoQ 架構頭條

在本文中,我將介紹一種簡單的雲架構,企業可以以增量的方式將單體應用程序遷移到雲端,而無需對架構進行重大修改。

作爲 fourTheorem 的一名雲架構師,我發現許多公司都在努力規模化他們的應用程序並充分利用雲計算的能力。

其中一些公司既是初創公司,也是合併重組過的公司,它們以單體的形式開發產品,並最終佔據了一定的市場份額。他們的業務在增長,但他們很難擴大應用程序的部署規模。

他們的服務通常部署在本地的私有服務器上或託管供應商的虛擬服務器上。隨着服務需求的增長,他們的生產環境開始變得越來越慢,服務可用性出現了間隙性下降,並最終影響到服務質量和進一步增長的潛力。

此時,將產品遷移到 AWS 等雲供應商可能是一個明智的解決方案。遷移到雲端後,他們可以按需使用資源,並按需支付費用。雲資源可以動態伸縮,以適應突發的流量,使用戶體驗始終保持良好的水平。

有趣的是,我接觸過的一些公司認爲,爲了過渡到雲計算,他們必須重新設計應用程序的整個架構,並轉向微服務甚至無服務器。

在大多數情況下,重新設計整個應用程序的時間和成本將會令人望而卻步,而這些成本原本應該用於構建有助於業務進一步增長的功能。這種想法讓企業對雲計算可能帶來的機會持懷疑態度,他們最終傾向於選擇短期的伸縮策略,也就是將應用服務器升級成更強大、更昂貴的機器。

當然,單臺服務器的規模是有限的,最終,企業不得不重新開始考慮替代方案。

在本文中,我將介紹一種簡單的雲架構,企業可以以增量的方式將單體應用程序遷移到雲端,而無需對架構進行重大修改。我們將討論雲計算可伸縮性所需的最低要求和基本組件。我們還將探討一些常見的問題,可能涉及修改應用程序代碼庫。最後,我們將分析在完成雲端遷移後可能出現的一些進一步改進的可能性。

我已經看到很多公司用這種方法成功地遷移到雲端。在雲端站穩了腳跟並讓應用程序穩定下來之後,他們就可以專注於爲客戶提供服務,並進一步發展業務。此外,因爲技術已不再是阻礙因素,他們可以開始試驗,並將應用程序的一部分轉成解耦的服務。這樣,公司就可以開始過渡到微服務架構甚至 Lambda 函數等新技術,這些技術可以幫助他們在開發過程中實現更大的靈活性,併爲業務帶來額外的增長機會。

01 引入一家虛構的公司

我們舉個具體的例子,引入一家虛構的公司,我們將它作爲一個虛構的案例研究,以此來探索如何進行雲遷移。

Eaglebox 是一家文件存儲公司,他們的 Eaglebox App 是一個 Web 和移動端應用程序,爲用戶提供保存和組織文件的功能,並能夠從多個設備上進行遠程訪問。

我們舉幾個 Eaglebox App 的具體用例:

Eaglebox App 是一個單體應用程序,使用 Django 框架開發,數據庫爲 PostgreSQL。

Eaglebox App 目前部署在 Eaglebox 公司本地的服務器上,用戶所有的文件都保存在機器的磁盤上(是的,它們會頻繁備份!)。類似地,PostgreSQL 也運行在同一臺機器上。數據庫數據也經常備份,但沒有副本。

Eaglebox 最近與一些大公司簽訂了一些合同,從那時起,他們就在努力擴大他們的基礎設施。他們的服務器變得越來越慢,磁盤很快就飽和了,需要大量的維護。用戶體驗已經變得很差,整個業務目前處於風險之中。

我們來看看如何通過重新設計可伸縮的架構來幫助 Eaglebox 遷移到雲端。

02 面臨的挑戰

根據 EagleBox 的工程師告訴我們的情況,我們已經確定了幾個需要解決的關鍵問題:

除了這些技術問題之外,我們還需要承認,EagleBox 團隊沒有云架構方面的經驗,遷移到雲端對他們來說將是一次學習的機會。所以,我們要控制遷移變更的數量,讓團隊有時間適應和吸收新知識,這一點很重要。

我們提出一種新的架構,既要解決所有現有的技術問題,又要提供通往雲端的最短路徑,並且不要求團隊做出重大的技術變更。

03 一種簡單且可伸縮的雲架構

爲了解決 EagleBox 面臨的挑戰,我們提出一種簡單、可伸縮、彈性的雲架構,並將 AWS 作爲首先的雲供應商。

這種架構將有以下幾個部分組成:

圖 1. 架構概覽

在圖 1 中,我們可以看到架構的高級視圖。接下來我們將介紹各個組件。

數據中心和網絡

在討論各種組件的細節之前,有必要簡要地探討一下 AWS 是如何公開數據中心的,以及如何爲我們的架構配置網絡。我們不會講得太詳細,但需要涉及一些基礎知識,知道會出現什麼樣的故障、在出現故障時如何保持應用程序運行,以及當流量增加時如何擴展它。

“雲” 並非萬無一失,它也會出問題。AWS、Azure 和谷歌雲等雲供應商爲我們提供了設計彈性架構的工具和最佳實踐,但這是一種共享責任模型,我們需要了解供應商可以提供哪些保證、哪裏可能會出問題以及是怎樣出問題的。

說到網絡,我們需要介紹一些高級概念。需要注意的是,這裏我只提到 AWS,但這些概念也同樣適用於 Azure 和谷歌雲。

在我們的架構中,我們將使用如圖 2 所示的 VPC 配置。

圖 2. 我們架構中的 VPC 配置

主要的想法就是選擇一個離客戶較近的區域,並在該區域內創建一個專用 VPC。然後,我們將使用 3 個不同的可用性區域,每個可用性區域都有一個公共子網和一個私有子網。

公共子網只用於負載均衡器,架構中的其他組件 (虛擬機、緩存服務器和數據庫) 全部使用私有子網。

操作點:從爲所選的區域內配置 VPC 開始,確保在不同的可用性區域中分別創建了公共子網和私有子網。

負載均衡器

負載均衡器是所有進入 Eaglebox 應用服務器的流量的 “入口”。這是一個彈性應用負載均衡器 (Layer 7),可以管理 HTTP、HTTPS、WebSocket 和 gRPC 流量。它負責將流入的流量分配給後端的虛擬機。它可以檢查目標虛擬機的運行狀況,確保只將流入的流量轉發給運行狀況良好且響應迅速的實例。

操作點:確保單體應用程序提供了一個可用於檢查實例健康狀況的端點。如果還沒有,請在應用程序中添加。

負載均衡器與 ACM (AWS 證書管理器) 集成,可以使用證書並提供 HTTPS 流量,確保所有進出的流量都是加密的。

從網絡方面來看,負載均衡器可以使用所有的公共子網,所以也就可以使用所有的可用性區域。負載均衡器也因此具備高可用性:如果一個可用性區域突然不可用,流量將自動通過其他可用性區域進行路由。

在 AWS 中,彈性負載均衡器能夠很好地處理不斷增長的流量,每個實例能夠分發每秒數百萬個請求。對於大多數現實中的應用程序來說,我們不需要爲負載均衡器的伸縮問題操心。最後,值得一提的是,這種負載均衡器完全由 AWS 負責託管,因此我們不需要操心繫統配置或軟件更新方面的事情。

虛擬機

Eaglebox App 是一個用 Python 開發的 Web 應用程序,使用了 Django 框架。我們希望能夠同時在不同的服務器上運行應用程序的多個實例,這樣就可以根據不斷增加的流量進行伸縮。理想情況下,我們希望將不同的實例分佈到不同的可用性區域。同樣,如果一個可用性區域變得不可用,其他區域還有實例可以處理流量並避免宕機。

要讓實例可動態伸縮,可以使用自動伸縮組。我們可以定義應用程序新實例自動啓動 (或銷燬) 的條件。例如,我們可以使用平均 CPU 負載水平或每個實例的平均請求數來確定是否需要啓動新實例,或者,如果已經有足夠的可用容量,可以降低實例數量以節省成本。爲了保證高可用性,我們需要確保每個可用性區域中至少有一個實例可用。

要使用虛擬機,就需要構建虛擬機鏡像。鏡像可以有效地把操作系統、所有必要的軟件 (例如 Python 運行時)、應用程序的源代碼和所有依賴項打成包。

爲了啓動虛擬機實例,必須定義鏡像,這裏涉及的細節可能並不重要,但它確實與在本地託管軟件的方式有很大不同。在本地,虛擬機通常會一直保持運行。在配置完畢後,IT 人員通常會登錄到機器上給軟件打補丁、重新啓動服務或部署新的應用程序版本。但如果存在多個實例,並且需要自動啓動和銷燬,這種方式就不可行了。

在雲端,虛擬機是 “不可變” 的:一旦啓動,就不應該被修改。如果需要發佈更新,就構建新的鏡像並啓動新實例,同時逐步銷燬舊實例。

但是,這種不變性不僅影響部署或軟件更新,它還影響數據 (或 “狀態”) 的管理方式。我們無法在虛擬機裏存儲持久狀態,因爲一旦機器被關閉,我們將丟失所有的數據,因此,我們不能在本地文件系統存儲文件或在應用程序內存中保存會話數據。

基於這樣的模型,“基礎設施”和 “數據” 變成了相互獨立的關注點,可以分開處理和管理。

在評審已有代碼和構建虛擬機鏡像時,我們要注意所有訪問數據 (文件、數據庫記錄、用戶會話數據,等等) 的代碼,並在必要時做出修改,以此來確保不會在實例本地保存數據。我們將在討論我們的架構所涉及的不同存儲類型時更深入地討論我們的可選項。

那麼我們如何構建虛擬機鏡像呢?

有幾種不同的工具和方法可以幫助我們完成這項任務。就我個人而言,我曾經使用過的並且讓我感到很滿意的是 AWS 的 EC2 Image Builder 和 Hashicorp 的 Packer。

數據庫

在 AWS 中,啓動關係數據庫 (如 PostgreSQL) 的最簡單的方法是使用 RDS:關係數據庫服務。RDS 是一種託管服務,用戶啓動數據庫實例,AWS 負責數據庫實例的更新和備份。

RDS PostgreSQL 可以有讀取副本。使用讀取副本是將查詢負載分攤到多個實例的一種很好的方法,即使在負載很重的情況下也能保持數據庫的響應速度。

RDS 的另一個有趣的特性是可以在多可用性區域模式下運行一個 PostgreSQL 實例。這意味着數據庫的主實例將運行在一個可用性區域中,然後在其他可用性區域至少會有兩個備用副本,以備在主可用性區域出現故障時使用。AWS 將負責在發生故障時執行自動切換,以確保數據庫在沒有任何人工干預的情況下儘快恢復。

需要注意的是,多可用性區域故障轉移不是瞬間完成的 (通常需要 60 到 120 秒),所以你需要讓應用程序能夠在無法建立數據庫連接時也能做一些事情 (或至少向用戶顯示描述性的消息)。

現在的問題是,我們如何將數據從本地數據庫遷移到 RDS 實例中?理想情況下,我們希望可以在不停機的情況下在兩個環境之間進行逐步過渡,那麼我們能做些什麼呢?

AWS 提供了另一種數據庫服務,叫作 AWS 數據庫遷移服務。你可以用這個服務將所有數據從舊數據庫複製到新數據庫。有趣的是,它還可以在切換期間保持兩個數據庫同步。通常情況下,由於 DNS 的關係,可能會有一些用戶可以登錄到新系統,其他用戶則可能仍然被路由到舊服務器。

操作點:在 RDS 上創建數據庫實例,並啓用多可用性區域模式。使用 AWS 數據庫遷移服務遷移所有數據,並在切換期間保持兩個數據庫同步。

文件存儲

在我們的新架構中,我們可以通過 S3 (Simple Storage Service) 實現分佈式文件存儲。S3 是 AWS 最早提供的服務之一,也可能是最著名的服務之一。

S3 是一種持久的對象存儲服務,你可以用它持久地存儲任意數量的數據。對象可以存儲在桶 (具有惟一名稱的邏輯容器) 中。S3 使用了鍵值存儲模型:桶中的每一個對象都有一個唯一 “鍵”,內容和元數據與鍵關聯起來。

要用 S3 來讀寫對象,我們需要使用 AWS SDK。它支持多種語言 (包括 Python),並且提供了一個與所有 AWS 服務 (包括 S3) 交互的編程接口。

我們還可以通過 AWS 命令行接口(CLI)與 S3 交互。對於我們的場景,CLI 有一個特別方便的命令——sync。我們可以使用這個命令將所有已有文件複製到指定的 S3 桶中。

要在這兩個環境之間順利過渡,最好是直接從現有環境開始使用 S3。這意味着我們需要將所有本地文件同步到一個存儲桶中,並確保用戶上傳的每個新文件也被複制到同一個存儲桶中。

操作點:文件遷移。創建一個新的 S3 桶,將所有現有文件同步到桶中。在 S3 中保存每個新文件。

會話存儲

在我們的新架構中,有多個後端服務器負責處理用戶的請求。如果流量負載是均衡的,那麼每個用戶請求最終都會到達給定的後端實例,但來自同一用戶的後續請求可能會由另一個後端實例提供服務。

因此,所有實例都需要訪問共享會話存儲。事實上,如果沒有共享會話存儲,當請求由不同的後端實例提供服務時,各個實例將無法正確地識別用戶會話。

實現分佈式會話存儲的常見方法是使用 Redis 實例。

在 AWS 上啓動 Redis 實例最簡單的方法是使用 Elasticache 的服務。Elasticache 是 Redis 和 Memcached 的託管服務,和 RDS 一樣,在使用這個服務時,你不需要操心操作系統或安裝安全補丁方面的事情。

ElastiCache 可以在多可用性區域模式下啓動一個 Redis 集羣,並提供自動故障轉移功能。與 RDS 一樣,這意味着如果集羣主實例所在的可用性區域不可達,Elasticache 將自動執行 DNS 故障轉移,並切換到其他可用性區域的備用副本上。但它的故障轉移不是瞬間完成的,所以應用程序需要考慮到在故障轉移期間可能無法與 Redis 建立連接。

操作點:使用 ElastiCache 提供一個 Redis 集羣,並確保所有的會話數據都保存在那裏。

DNS

遷移的最後一步與 DNS 有關,也就是我們如何將流量轉發到 AWS 的基礎設施上?

最好的方法是在 Route 53 中爲所有的應用程序配置 DNS。Route 53 是一個高可用、可伸縮的雲 DNS 服務。

它可以將應用程序域中的所有流量轉發到負載均衡器。在配置並啓用了之後 (並且 DNS 已傳播),新的基礎設施將開始接收流量。

如果你的域名已經在其他地方註冊過,可以將域名轉到 AWS,或者修改註冊配置,使用新的 Route 53 託管區域作爲域名服務器。

操作點:在 Route 53 中創建一個新的託管區域,並配置 DNS,將域指向應用程序負載均衡器。在準備切換時,將你的域名註冊指向 Route 53 或將域名轉到 AWS。

其他建議

正如我們所看到的,這個新的架構由大量的活動組件組成。我們該如何追蹤所有這些組件,並確保所有的環境 (如開發、QA 和生產) 儘可能保持一致?

解決這一問題的最佳方法是使用基礎設施即代碼 (Infrastructure as a Code,IaaC)。你可以將所有的基礎設施定義爲代碼,這些代碼可以保存在代碼庫中 (甚至與應用程序代碼庫放在一起),所有的基礎設施對所有開發人員都是可見的。他們可以對變更進行評審或直接修改。更重要的是,IaaC 爲跨環境發佈變更提供了一個可重複的過程,隨着架構的演化,這種可重複的過程有助於保持一致性。

如果是在 AWS 上實現 IaaC,可以選擇 CloudFormation,有了這個工具,你就可以使用 YAML 指定基礎設施模板。AWS 提供的另一個替代工具是雲開發工具包 (Cloud Development Kit,CDK),它提供了一個高級接口,可以使用編程語言 (如 TypeScript、Python 或 Java) 定義基礎設施。

另一個常見的替代方案是第三方工具 Terraform。

選擇哪種工具並不重要 (它們都有各自的優點和缺點),重要的是你要將所有基礎設施定義成代碼,確保你可以圍繞如何將基礎設施變更交付到雲端構建一個可靠的流程。

可觀測性是另一個很重要的方面。現在我們有了這麼多的活動組件,該如何調試問題或者如何確保系統是健康的?關於可觀測性的內容超出了本文的範圍,但如果你對分佈式日誌、跟蹤、指標、警報和儀表盤等主題感興趣,可以瞭解下 CloudWatch 和 X-Ray。

基礎設施即代碼和可觀測性是兩個非常重要的主題,它們將極大地幫助你將應用程序部署到雲端並保持它們順利運行。

04 接下來

現在,我們已經到了雲端,那麼我們的旅程結束了嗎?恰恰相反,我們的旅程纔剛剛開始,後面還有很多東西需要探索和學習。

既然我們身處雲端,就有很多機會探索新技術和新方法。

我們可以研究下容器,甚至是無服務器。如果我們要構建一個新特性,不一定要侷限於必須部署在一個單體服務器上。我們可以嘗試利用新工具以一種更解耦的方式構建新特性。

例如,假設我們需要構建一個功能,當一個用戶上傳了一個新文檔,通過電子郵件通知其他人。一種方法是使用隊列和處理進程。應用程序可以將與發送通知電子郵件相關的作業定義發佈到隊列中,一個處理進程池將處理隊列中的作業,並完成發送電子郵件的任務。

這種方法可以讓後端應用程序保持快速響應,並將耗時的後臺任務 (如發送電子郵件) 委託給異步執行的外部進程。

要在 AWS 上實現這個,可以使用 SQS(隊列) 和 Lambda(無服務器計算)。

這個例子向我們展示了雲計算如何爲我們打開新的可能性,讓公司可以快速迭代和不斷試驗,並利用一套全面的工具和技術來滿足需求。

遷移到雲端是一段旅程,不是目的地,而且這段旅程纔剛剛開始。享受這段旅程吧!

05 附錄:將單體系統遷移到雲端的檢查清單

作者簡介:

Luciano Mammino 在 12 歲時在他父親的老式 i386 機器上寫了第一行代碼。從那時起,他就沒有停止過編程。他是 fourTheorem 的高級架構師,他的工作是幫助公司充分利用雲計算。Luciano 是《Node.js 設計模式》的合著者。他還是 Fullstack Bulletin 的維護者,這是一個爲全棧開發人員提供的免費每週新聞通訊。Luciano 還是一名認證的 AWS 解決方案架構師和微軟 MVP。

原文鏈接:

https://www.infoq.com/articles/cloud-migrate-scale/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Uq1okzT-BgRBaljNMco97g