GitChat 做者:顧宇
原文:Serverless 風格微服務的持續交付(上):架構案例
關注微信公衆號:「GitChat技術雜談」 一本正經的講技術html
Serverless 架構最先能夠追溯到 Ken Fromm 發表的文章《Why The Future Of Software And Apps Is Serverless》。在這篇文章裏, Ken Fromm 描述了將來雲計算基礎設施成熟的條件下應用程序是不須要服務器端的。在無武器場景下構建應用程序的時候。開發人員和運維人員無需擔憂服務器如何安裝配置,如何設置網絡和負載均衡,無需監控狀態,甚至再也不會出現服務器相關的工做內容。這樣可讓本來建設機房的時間成本和貨幣成本從按年計算縮短至按秒計算。前端
在 Martin Fowler 的博客《Serverless Architectures》中,他將無服務器架構分爲兩種:java
第一種無服務器架構被稱爲被稱爲 __BaaS(Backend as a Service,後端應用即服務)__。即應用的架構是由一大堆第三方 API 來組織的。一切狀態和邏輯都由這些服務提供方來管理。隨着移動應用和單頁 Web 應用這樣的富客戶端(Rich Client)應用的普及,先後端的通訊漸漸以 API 調用爲主,而所需的服務再也不由 服務端應用開發工程師和運維工程師來維護,只須要調用提供服務的第三方 API 就能夠完成相應的功能。例如雲上的數據庫服務和用戶認證服務。node
另外一種無服務器架構被稱爲 __FaaS(Function as a Service,函數即服務)__。這一架構的興起源於 AWS Lambda 的發展。 AWS Lambda 是一種無狀態的代碼運行時服務,這項服務提供最小的代碼運行資源。你可使用 Java,Node.js,Python 和 C# 編寫程序處理 AWS 各類服務的事件。無需初始化一臺服務器,安裝操做系統並配置程序運行環境。因爲運行資源不多,完成的計算有限,使得這種應用沒法保存狀態,所以這類程序以函數的方式存在。nginx
本文所介紹的 Serverless 架構主要是以 AWS Lambda 以及 Amazon API Gateway 架構的應用,它同時也具有 BaaS 的特徵。git
AWS Lambda 運行在一個假想的虛擬容器裏,但你沒法經過 API 配置這個容器。此外,這個虛擬的容器有一些資源限制,主要限制以下:數據庫
AWS Lambda 的編程模型以下所示:編程
Lambda 的執行流程:json
當事件請求大批量發生的時候。Lambda 會爲每個事件單獨執行一次 。這意味着每個請求之間的執行期間,內容是不能共享的。(經本人親測,內存中存儲的是能夠共享的,但內容保留的有效時間和狀態沒法保證。)後端
根據 Martin Fowler 對微服務的描述性定義,咱們能夠認爲微服務從技術層面包含如下特徵:
在 AWS 現有的服務狀況下,AWS Lambda 知足了上面的第 一、三、5 點,這只是一個微服務的處理單元,非管理單元。而 2 和 4 則須要另外的服務做爲管理單元共同構成微服務,這個任務通常交由 API 網關實現。
Amazon API Gateway 是一種徹底託管的 API 網關服務,能夠幫助開發者輕鬆建立、發佈、維護、監控和保護任意規模的 API。它集成了不少 API 網關的功能,諸如緩存、用戶認證等功能。而且支持經過 HAML 和 Swagger 配置,這樣就能夠用代碼管理系統配置 API 了。
Amazon API Gateway 能夠根據不一樣的 Restful API 訪問點將請求的數據傳遞給不一樣的資源進行處理。通常的 AWS API 架構以下所示:
相較於傳統的微服務架構,經過 API Gateway 和 Lambda 的這種集成方式能夠獲得更輕量級的微服務。團隊只須要規劃好 API 訪問並完成函數的開發,就能夠快速的構建出一個最簡單的微服務,使得微服務基礎設施的搭建時間從幾周縮短爲幾個小時。此外,大大提高了微服務架構的開發效率和穩定性。
2016年12月初,當時我正在以一名 DevOps 諮詢師的身份參與悉尼某一移動電話運營商的 Digital (電子渠道)部門的 DevOps 轉型項目。這個項目是提高該部門在 AWS (Amazon Web Services)雲計算平臺上的 DevOps 能力。
Digital 部門負責該電信運營商全部的互聯網和移動設備應用開發。這些應用主要是用來爲用戶提供諸如 SIM 卡激活,話費查詢,話費充值,優惠套餐訂購等自助服務(Self service),從而下降營業廳和人工話務客服的成本。
自助服務的應用系統基於 Ruby on Rails 框架開發,前端部分採用 AngularJS 1.0,可是沒有采用先後端分離的設計,頁面代碼仍然是經過 ERB 組合而成。yi移動端則採用 Cordova 開發。爲了下降開發難度和工做量, 移動端的應用內容其實是把 AngularJS 所生成的 Web 頁面經過響應式樣式的方式嵌入到移動端。但由於常常超時,因此這款 APP 體驗並很差。
整套 Rails 應用部署在 AWS 上,而且經過網關和內部業務 BOSS (Business Operating Support System) 系統隔離。BOSS 系統採用 SOAP 對外暴露服務,並由另一個部門負責。所以,雲上的應用所作的業務是給用戶展示一個使用友好的界面,並經過數據的轉化和內部 BOSS 系統進行交互。系統架構以下圖所示:
這個應用經歷了多年的開發,先後已經更換過不少技術人員。可是沒有人對這個應用代碼庫有完整的的認識。所以,咱們對整個團隊和產品進行了一次痛點總結:
我參與過不少 Ruby 技術棧遺留系統的維護。在經歷了這些 Ruby 項目以後,我發現 Ruby 是一個開發起來很爽可是維護起來很痛苦的技術棧。大部分的維護更改是因爲 Ruby 的版本 和 Gem 的版本更新致使的。此外,因爲 Ruby 比較靈活,人們都有本身的想法和使用習慣,所以代碼庫很難維護。
雖然團隊已經有比較好的持續交付流程,可是 Ops 能力缺少和應用架構帶來的侷限阻礙了整個產品的前進。所以,當務之急是可以經過 DevOps 提高團隊的 Ops 能力,緩解 Ops 資源不足,削弱 DevOps 矛盾。
DevOps 組織轉型中通常有兩種方法:一種方法是提高 Dev 的 Ops 能力,另外一種方法是下降 Ops 工做門檻。在時間資源很緊張的狀況下,經過技術的改進,下降 Ops 的門檻是短時間內收益最大的方法。
在我加入這個項目的時候,客戶收購了一個本地的寬帶/固定電話運營商。所以原有的系統須要須要承載固話和寬帶的新業務。恰巧有個訂單查詢的業務須要讓當前的團隊完整這樣一個需求:經過現有的訂單查詢功能能夠同時查詢移動和固網寬帶訂單。
這要求在起因的訂單查詢功能上新增添一些選項和內容,能夠同時查到移動和固網寬帶的訂單。經過上述痛點可知,這在當時完成這樣一個任務的代價是十分昂貴的。
在開發的項目上進行 DevOps 轉型就像在行進的汽車上換車輪,一不留心就會讓全部團隊中止工做。所以我建議經過設立並行的新團隊來同時完成新功能的開發和 DevOps 轉型的試點。
這是一個功能拆分和新功能拆分需求,恰好訂單查詢是原系統中一個比較獨立和成熟的功能。爲了不影響原有各功能開發的進度。咱們決定採用微服務架構來完成這個功能。
咱們並不想重蹈以前應用架構的覆轍,咱們要作到先後端分離。使得比較小的開發團隊能夠並行開發,只要協商好了 接口之間的契約(Contract),將來開發完成以後會很好集成。
這讓我想起了 Chris Richardson 提出了三種微服務架構策略,分別是:中止挖坑,先後端分離和提取微服務。
中止挖坑的意思是說:若是發現本身掉坑裏,立刻中止。
原先的單體應用對咱們來講就是一個焦油坑,所以咱們要中止在原來的代碼庫上繼續工做。而且爲新應用單首創建一個代碼庫。因此,咱們拆分策略模式以下所示:
在咱們的架構裏,實現新的需求就要變更老的應用。咱們的想法是:
咱們本來要在原有的應用上增長一個 API 用來訪問之前應用的邏輯。但想一想這實際上也是一種挖坑。在評估了業務的複雜性以後。咱們發現這個功能若是全新開發只須要 2人2周(一我的月)的時間,這僅僅佔咱們預估工做量的20%不到。所以咱們放棄了對遺留代碼動工的念頭。最終經過微服務直接訪問後臺系統,而不須要經過原有的應用。
在咱們拆微服務的部分十分簡單。對於後端來講說只須要修改 CDN 覆蓋原先的訪問源(Origin)以及保存在 route.rb 裏的原功能訪問點,就能夠完成微服務的集成。
結合上面的應用痛點和思路,在構建微服務的技術選型時咱們肯定了如下方向:
所以咱們選擇了 React 做爲前端技術棧而且用 yarn 管理依賴和任務。另一個緣由是咱們可以經過 React-native 爲將來構建新的應用作好準備。此外,咱們引入了 AWS SDK 的 nodejs 版本。用編寫一些常見的諸如構建、部署、配置等 AWS 相關的操做。而且經過 swagger 描述後端 API 的行爲。這樣,後端只須要知足這個 API 規範,就很容易作先後端集成。
因爲 AWS S3 服務自帶 Static Web Hosting (靜態頁面服務) 功能,這就大大減小了咱們構建基礎環境所花費的時間。若是你還想着用 Nginx 和 Apache 做爲靜態內容的 Web 服務器,那麼你還不夠 CloudNative。
雖然 AWS S3 服務曾經發生過故障,但 SLA 也比咱們本身構建的 EC2 實例處理靜態內容要強得多。此外還有如下優勢:
在構建微服務的最初,咱們當時有兩個選擇:
然而,這兩個方案的都有一個共同的問題:須要經過 ruby 語言編寫的基礎設施工具構建一套運行微服務的基礎設施。而這個基礎設施的搭建,前先後後估計得須要至少 1個月,這仍是在運維團隊有人幫助的狀況下的樂觀估計。
因此,要找到一種下降環境構建和運維團隊阻塞的方式避開傳統的 EC2 搭建應用的方式。
基於上面的種種考量,咱們選擇了 Amazon API Gateway + Lambda 的組合。而 Amazon API Gateway + Lambda 還有額外好處:
雖然有這麼多優勢,但不能忽略了關鍵性的問題:AWS Lambda 不必定適合你的應用場景!
根據上文對 AWS Lambda 的介紹,支持 AWS Lambda 運行的資源和時間頗有限。所以不少對同步和強一致性的業務需求是沒法知足的。因此,AWS Lambda 更適合可以異步處理的業務場景。此外,AWS Lambda 對消耗存儲空間和 CPU 不少的場景支持不是很好,例如 AI 和 大數據。(PS: AWS 已經有專門的 AI 和大數據服務了,因此不須要和本身過不去)
對於咱們的應用場景而言,上文中的 Ruby On Rails 應用中的主要功能(至少60% 以上)實際上只是一個數據轉換適配器:把前端輸入的數據進行加工,轉換成對應的 SOAP 調用。
所以,對於這樣一個簡單的場景而言,Amazon API Gateway + Lambda 徹底知足需求!
選擇了Amazon API Gateway + Lambda 後,後端的微服務部署看起來很簡單:
可是,這卻不是很容易的一件事。咱們將在《Serverless 風格微服務的持續交付(中):持續交付的挑戰》中對這方面踩過的坑詳細介紹。
這時候在 CDN 上給新的微服務配置 API Gateway 做爲一個新的源(Origin),覆蓋原先寫在 route.rb 和 nginx.conf 裏的 API 訪問規則就能夠了。CDN 會攔截訪問請求,使得請求在 nginx 處理以前就會把對應的請求轉發到 API Gateway。
固然,若是你想作灰度發佈的話,就不能按上面這種方式搞了。CloudFront 和 ELB 負載均衡 並不具有帶權轉發功能。所以你須要經過 nginx 配置,按訪問權重把 API Gateway 做爲一個 upstream 裏的一個 Server 就能夠。
不要留着無用的遺留代碼!
不要留着無用的遺留代碼!
不要留着無用的遺留代碼!
重要且最容易被忽略的事情要說三遍。斬草要除根,雖然咱們能夠保持代碼不動。可是清理再也不使用的遺留代碼和自動化測試能夠爲其它團隊減小不少沒必要要的工做量。
通過6我的兩個月的開發(原計劃8我的3個月),咱們的 Serverless 微服務最終落地了。固然這中間有 60% 的時間是在探索全新的技術棧。若是熟練的話,估計 4 我的一個月就能夠完成工做。
最後的架構以下圖所示:
在上圖中,請求仍然是先到 CDN (CloudFront),而後:
經過 API Gateway 轉發的 API 請求分紅了三類,每一類均可以根據請求情況自擴展:
因爲沒有 EC2 設施初始化的時間,咱們減小了至少一個月的工做量,分別是:
若是要把 API Gateway 算做是基礎設施初始化的時間來看。第一次初始化 API Gateway 用了一天,之後 API Gateway 結合持續交付流程每次修改僅僅須要幾分鐘。
不管怎麼說,Serverless 大大下降了基礎設施配置和運維門檻。
此外,對於團隊來講,Amazon API Gateway + Lambda 的微服務還帶來其它好處:
此外,咱們作了 Java 和 NodeJs 比較。在開發一樣的功能下,NodeJS 的開發效率更高,緣由是 Java 要把請求的 json 轉化爲對象,也要把返回的 json 轉化爲對象,而不像 nodejs 直接處理 json。此外, Java 須要引入一些其它 JAR 包做爲依賴。在 AWS 場景下開發一樣一個函數式微服務,nodejs 有 4 倍於 java 的開發效率提高。
Serverless 風格的微服務雖然大大減小了開發工做量以及基礎設施的開發維護工做量。但也帶來了新的挑戰:
這讓咱們從新思考了 Serverless 架構的微服務如何更好的進行持續交付。
敬請期待下一篇《Serverless 風格微服務的持續交付(中):持續交付的挑戰 》
實錄:《顧宇:構建Serverless 風格微服務實戰解析(上)》
更多精彩內容請關注: