面試官:作爲架構師,請你談談Saas 應用如何搭建?


點擊上方藍字,關注我們



引言


如今,軟件通常會作爲一種服務來交付,它們被稱爲網絡應用程序,或軟件即服務(SaaS)。12-Factor 爲構建如下的 SaaS 應用提供了方法論:

  • 使用標準化流程自動配置,從而使新的開發者花費最少的學習成本加入這個項目。

  • 和操作系統之間儘可能的劃清界限,在各個系統中提供最大的可移植性。

  • 適合部署在現代的雲計算平臺,從而在服務器和系統管理方面節省資源。

  • 將開發環境和生產環境的差異降至最低,並使用持續交付實施敏捷開發。

  • 可以在工具、架構和開發流程不發生明顯變化的前提下實現擴展。


這套理論適用於任意語言和後端服務(數據庫、消息隊列、緩存等)開發的應用程序。



統一源代碼管理系統


一份基準代碼(Codebase),多份部署(deploy)


在類似 SVN 這樣的集中式版本控制系統中,基準代碼就是指控制系統中的這一份代碼庫;而在 Git 那樣的分佈式版本控制系統中,基準代碼則是指最上游的那份代碼庫。


基準代碼和應用之間總是保持一一對應的關係:

  • 一旦有多個基準代碼,就不能稱爲一個應用,而是一個分佈式系統。分佈式系統中的每一個組件都是一個應用,每一個應用可以分別使用 12-Factor 進行開發。

  • 多個應用共享一份基準代碼是有悖於 12-Factor 原則的。解決方案是將共享的代碼拆分爲獨立的類庫,然後使用依賴管理策略去加載它們。




依賴管理


顯式聲明依賴

 

大多數編程語言都會提供一個打包系統,用來爲各個類庫提供打包服務,就像 Perl 的 CPAN 或是 Ruby 的 Rubygems 。通過打包系統安裝的類庫可以是系統級的(稱之爲 “site packages”),或僅供某個應用程序使用,部署在相應的目錄中(稱之爲 “vendoring” 或 “bunding”)

 

12-Factor規則下的應用程序不會隱式依賴系統級的類庫。 它一定通過 依賴清單 ,確切地聲明所有依賴項。此外,在運行過程中通過依賴隔離工具來確保程序不會調用系統中存在但清單中未聲明的依賴項。這一做法會統一應用到生產和開發環境。

 

顯式聲明依賴的優點之一是爲新進開發者簡化了環境配置流程。新的開發者可以檢出應用程序的基準代碼,安裝編程語言環境和它對應的依賴管理工具,只需通過一個構建命令來安裝所有的依賴項,即可開始工作,如Maven,Pip,Npm等

 

12-Factor 應用同樣不會隱式依賴某些系統工具,如 ImageMagick 或是curl。即使這些工具存在於幾乎所有系統,但終究無法保證所有未來的系統都能支持應用順利運行,或是能夠和應用兼容。如果應用必須使用到某些系統工具,那麼這些工具應該被包含在應用之中。



配置管理


在環境中存儲配置

 

通常,應用的配置在不同部署(預發佈、生產環境、開發環境等等)間會有很大差異。這其中包括:

  • 數據庫,Memcached,以及其他後端服務的配置。

  • 第三方服務的證書,憑證,如 Amazon S3、Twitter 等。

  • 每份部署特有的配置,如域名等

 

應用程序不允許將配置存儲爲代碼中的常量,這需要嚴格地將配置與代碼分離。配置在部署之間差異很大,代碼則沒有。另外,"config" 的這個定義不包括內部應用程序配置,這種類型的配置在部署之間不會有所不同,因此最好在代碼中保存。

 

提示:對應用程序是否在代碼中正確分配了所有配置的試金石是,代碼庫是否可以隨時變爲開源,而不用擔心泄漏任何敏感憑據。

 

應用程序應將配置存儲在環境變量中(通常縮寫爲env vars 或 env)。在不更改任何代碼的情況下,可以在部署之間輕鬆更改 Env 變量; 與配置文件不同,它們幾乎沒有機會被意外地檢入代碼倉庫; 與自定義配置文件或其他配置機制(如 Java 系統屬性)不同,它們是與語言和操作系統無關的標準。




後端服務


把後端服務(backing services)當作附加資源

 

後端服務是指程序運行所需要的通過網絡調用的各種服務,如數據庫(MySQL,CouchDB),消息/隊列系統(RabbitMQ,Beanstalkd),SMTP 郵件發送服務(Postfix),以及緩存系統(Memcached)。

 

類似數據庫的後端服務,通常由部署應用程序的系統管理員一起管理。除了本地服務之外,應用程序有可能使用了第三方發佈和管理的服務。示例包括 SMTP(例如 Postmark),數據收集服務(例如 New Relic 或 Loggly),數據存儲服務(如 Amazon S3),以及使用 API 訪問的服務(例如 Twitter, Google Maps, Last.fm)。

 

12-Factor 應用不會區別對待本地或第三方服務。 對應用程序而言,兩種都是附加資源,通過一個 url 或是其他存儲在配置中的服務定位/服務證書來獲取數據。12-Factor 應用的任意 部署 ,都應該可以在不進行任何代碼改動的情況下,將本地 MySQL 數據庫換成第三方服務(例如 Amazon RDS)。類似的,本地 SMTP 服務應該也可以和第三方 SMTP 服務(例如 Postmark )互換。上述 2 個例子中,僅需修改配置中的資源地址。


12-Factor 應用將這些都視作附加資源 ,這些資源和它們附屬的部署保持松耦合。




構建,發佈,運行


嚴格分離構建和運行

 

基準代碼轉化爲一份部署(非開發環境)需要以下三個階段:

  • 構建階段是指將代碼倉庫轉化爲可執行包的過程。構建時會使用指定版本的代碼,獲取和打包依賴項,編譯成二進制文件和資源文件。

  • 發佈階段會將構建的結果和當前部署所需配置相結合,並能夠立刻在運行環境中投入使用。

  • 運行階段(或者說“運行時”)是指針對選定的發佈版本,在執行環境中啓動一系列應用程序進程。



12-factor 應用嚴格區分構建,發佈,運行這三個步驟。 舉例來說,直接修改處於運行狀態的代碼是非常不可取的做法,因爲這些修改很難再同步回構建步驟。


每一個發佈版本必須對應一個唯一的發佈 ID,例如可以使用發佈時的時間戳(2011-04-06-20:32:17),亦或是一個增長的數字(v100)。發佈的版本就像一本只能追加的賬本,一旦發佈就不可修改,任何的變動都應該產生一個新的發佈版本。這樣也便於回退到任意歷史版本,而不需要冒風險重新構建。

 

新的代碼在部署之前,需要開發人員觸發構建操作。但是,運行階段不一定需要人爲觸發,而是可以自動進行。如服務器重啓,或是進程管理器重啓了一個崩潰的進程。因此,運行階段應該保持儘可能少的模塊,這樣假設半夜發生系統故障而開發人員又捉襟見肘也不會引起太大問題。構建階段是可以相對複雜一些的,因爲錯誤信息能夠立刻展示在開發人員面前,從而得到妥善處理。




進程


以一個或多個無狀態進程運行應用

 

運行環境中,應用程序通常是以一個和多個進程運行的。

 

12-Factor 應用的進程必須無狀態且無共享。 任何需要持久化的數據都要存儲在後端服務內,比如數據庫。


內存區域或磁盤空間可以作爲進程在做某種事務型操作時的緩存,例如下載一個很大的文件,對其操作並將結果寫入數據庫的過程。12-Factor應用根本不用考慮這些緩存的內容是不是可以保留給之後的請求來使用,這是因爲應用啓動了多種類型的進程,將來的請求多半會由其他進程來服務。即使在只有一個進程的情形下,先前保存的數據(內存或文件系統中)也會因爲重啓(如代碼部署、配置更改、或運行環境將進程調度至另一個物理區域執行)而丟失。

 

一些系統依賴於 “粘性 session”, 這是指將用戶 session 中的數據緩存至某進程的內存中,並將同一用戶的後續請求路由到同一個進程。粘性 session 是 12-Factor 極力反對的。Session 中的數據應該保存在諸如 Memcached 或 Redis 這樣的帶有過期時間的緩存中。



端口綁定


通過端口綁定(Port binding)來提供服務

 

應用有時會運行於服務器的容器之中。例如 PHP 經常作爲 Apache HTTPD 的一個模塊來運行,正如 Java 運行於 Tomcat 。


12-Factor 應用完全自我加載而不依賴於任何網絡服務器就可以創建一個面向網絡的服務。互聯網應用通過端口綁定來提供服務 ,並監聽發送至該端口的請求。


還要指出的是,端口綁定這種方式也意味着一個應用可以成爲另外一個應用的後端服務 ,調用方將服務方提供的相應 URL 當作資源存入配置以備將來調用。



併發


通過進程模型進行擴展

 

在 12-factor 應用中,進程是一等公民。12-Factor 應用的進程主要借鑑於 unix 守護進程模型 。開發人員可以運用這個模型去設計應用架構,將不同的工作分配給不同的進程類型 。例如,HTTP 請求可以交給 web 進程來處理,而常駐的後臺工作則交由 worker 進程負責。

 

上述進程模型會在系統急需擴展時大放異彩。 12-Factor 應用的進程所具備的無共享,水平分區的特性意味着添加併發會變得簡單而穩妥。這些進程的類型以及每個類型中進程的數量就被稱作進程構成 。




易處理


快速啓動和優雅終止可最大化健壯性

 

12-Factor 應用的進程是易處理(disposable)的意思是說它們可以瞬間開啓或停止。這有利於快速、彈性的伸縮應用,迅速部署變化的代碼或配置 ,穩健的部署應用。

 

進程應當追求最小啓動時間 。理想狀態下,進程從敲下命令到真正啓動並等待請求的時間應該只需很短的時間。更少的啓動時間提供了更敏捷的發佈以及擴展過程,此外還增加了健壯性,因爲進程管理器可以在授權情形下容易的將進程搬到新的物理機器上。

 

另外進程一旦接收終止信號(SIGTERM) 就會優雅的終止 。就網絡進程而言,優雅終止是指停止監聽服務的端口,即拒絕所有新的請求,並繼續執行當前已接收的請求,然後退出。此類型的進程所隱含的要求是HTTP請求大多都很短(不會超過幾秒鐘),而在長時間輪詢中,客戶端在丟失連接後應該馬上嘗試重連;對於 worker 進程來說,優雅終止是指將當前任務退回隊列。




開發環境與線上環境一致


儘可能的保持開發,預發佈,線上環境相同

 

開發環境(即開發人員的本地部署)和線上環境(外部用戶訪問的真實部署)之間存在着很多差異。這些差異表現在以下三個方面:

  • 時間差異: 開發人員正在編寫的代碼可能需要幾天,幾周,甚至幾個月纔會上線。

  • 人員差異: 開發人員編寫代碼,運維人員部署代碼。

  • 工具差異: 開發人員或許使用 Nginx,SQLite,OS X,而線上環境使用 Apache,MySQL 以及 Linux。


12-Factor 應用想要做到持續部署就必須縮小本地與線上差異。 再回頭看上面所描述的三個差異:

  • 縮小時間差異:開發人員可以幾小時,甚至幾分鐘就部署代碼。

  • 縮小人員差異:開發人員不只要編寫代碼,更應該密切參與部署過程以及代碼在線上的表現。

  • 縮小工具差異:儘量保證開發環境以及線上環境的一致性。


將上述總結變爲一個表格如下:



12-Factor 應用的開發人員應該反對在不同環境間使用不同的後端服務 ,即使適配器已經可以幾乎消除使用上的差異。這是因爲,不同的後端服務意味着會突然出現的不兼容,從而導致測試、預發佈都正常的代碼在線上出現問題。這些錯誤會給持續部署帶來阻力。從應用程序的生命週期來看,消除這種阻力需要花費很大的代價。



日誌


把日誌當作事件流

 

日誌使得應用程序運行的動作變得透明。在基於服務器的環境中,日誌通常被寫在硬盤的一個文件裏,但這只是一種輸出格式。

 

12-factor應用本身從不考慮存儲自己的輸出。 不應該試圖去寫或者管理日誌文件。相反,每一個運行的進程都會直接的標準輸出(stdout)事件流。開發環境中,開發人員可以通過這些數據流,實時在終端看到應用的活動。


在預發佈或線上部署中,每個進程的輸出流由運行環境截獲,並將其他輸出流整理在一起,然後一併發送給一個或多個最終的處理程序,用於查看或是長期存檔。這些存檔路徑對於應用來說不可見也不可配置,而是完全交給程序的運行環境管理。類似 Logplex 和 Fluentd 的開源工具可以達到這個目的。

 

這些事件流可以輸出至文件,或者在終端實時觀察。最重要的,輸出流可以發送到 Splunk 這樣的日誌索引及分析系統,或 Hadoop/Hive 這樣的通用數據存儲系統。這些系統爲查看應用的歷史活動提供了強大而靈活的功能,包括:

  • 找出過去一段時間特殊的事件。

  • 圖形化一個大規模的趨勢,比如每分鐘的請求量。

  • 根據用戶定義的條件實時觸發警報,比如每分鐘的報錯超過某個警戒線。



管理進程


後臺管理任務當作一次性進程運行

 

進程構成(process formation)是指用來處理應用的常規業務(比如處理 web 請求)的一組進程。與此不同,開發人員經常希望執行一些管理或維護應用的一次性任務,例如:

  • 運行數據移植(Django 中的 manage.py migrate, Rails 中的 rake db:migrate)。

  • 運行一個控制檯(也被稱爲 REPL shell),來執行一些代碼或是針對線上數據庫做一些檢查。大多數語言都通過解釋器提供了一個 REPL 工具(python 或 perl),或是其他命令(Ruby 使用 irb, Rails 使用 rails console)。

  • 運行一些提交到代碼倉庫的一次性腳本。


一次性管理進程應該和正常的常駐進程使用同樣的環境。這些管理進程和任何其他的進程一樣使用相同的代碼和配置 ,基於某個發佈版本運行。後臺管理代碼應該隨其他應用程序代碼一起發佈,從而避免同步問題。


往期推薦



面試官:請你談談用Redis實現一個輕量級的搜索引擎!

接商城類私活就靠他了,前後端都有

大廠實踐|Apache Pulsar 消息隊列在拉卡拉的應用

面試官:請你從架構演進的角度講講redis、kafka和 pulsar消息隊列

Java類加載中除了ClassNotFoundException,你還遇到過那些異常?

(百億級)Redis 如何存儲海量的人口標籤數據

Linux 如何查看文件是被那個進程佔用寫數據?

10張圖帶你深入理解Docker容器和鏡像 - DockOne.io

BAT某大廠CodeReview規範

【5000+源碼】學會產品思維,普通人也能通過小遊戲賺錢


本文分享自微信公衆號 - 俠夢的開發筆記(xmdevnote)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章