12-Factor,構建原生軟件應用方法論

官方地址:https://12factor.net/zh_cn/前端

原則1:一份基準代碼,多份部署

這個原則無論對微服務模式仍是其餘軟件開發模式來講都很是基本,因此被列爲12原則的第一條,該原則包括以下四個子原則python

  1. 使用代碼庫管理代碼,通常是Git或者SVN,這個要求很是初級,相信本書的讀者都會遵照。web

  2. 一份基準代碼(即一個代碼庫)對應一個應用。若是經過一份基準代碼能夠編譯出多個應用,那麼應該考慮將該基準代碼按應用拆分爲多份;若是一個應用須要多份基準代碼,那麼要麼考慮將多份基準代碼合併,要麼考慮將該應用按基準代碼拆分爲多個。shell

  3. 不容許多個應用共享一份基準代碼,若是確實須要共享,那就把須要共享的基準代碼的穩定版本發佈爲類庫,而後經過依賴管理策略進行加載。數據庫

  4. 同一應用的多份部署可使用同一份基準代碼的不一樣版本,可是不可使用不一樣的基準代碼,相似原則2,使用不一樣基準代碼的應用不該被視爲同一應用。後端

違反子原則2和3,會給代碼管理和編譯工做帶來麻煩:緩存

  1. 若是一份基準代碼能夠編譯出多個應用,那麼這幾個應用之間必然會存在不清晰的依賴關係,隨着時間的推移,這種依賴關係會變得越發混亂,以致於修改一個應用的代碼,會給其餘應用帶來不可預知的影響。這樣的基準代碼顯然極難維護。安全

  2. 基準代碼的劃分和應用的劃分很是相似,也是系統邊界的一種體現,若是一個應用須要從多份基準代碼編譯,那麼多數狀況下這個應用的內外部邊界問題會存在問題。若是邊界不存在問題,那麼請將多份基準代碼合併爲一份,而不是維持這種古怪的設計。服務器

  3. 若是多個應用不是經過類庫,而是直接共享一份基準代碼,那麼這份被共享的基準代碼會很難維護,對這份基準代碼的修改必須謹慎考慮對多個應用可能形成的影響。正確的方式是將這份基準代碼發佈爲類庫,保持清晰的邊界和接口約定供其它應用調用。網絡

原則2:顯式聲明依賴關係

  • 打包系統
  • 包類型:site packages、 vendoring| bunding
  • 依賴清單: 12-Factor規則下的應用程序不會隱式依賴系統級的類庫
  • 依賴隔離:不管用什麼工具,依賴聲明和依賴隔離必須一塊兒使用,不然沒法知足 12-Factor 規範
  • 構建命令:顯式聲明依賴的優勢之一是爲新進開發者簡化了環境配置流程。

原則3:在環境中存儲配置

首先須要明確的是,這裏的配置指與部署環境有關的配置,例如:

  • 數據庫、消息代理、緩存系統等後端服務的鏈接配置和位置信息,如URL、用戶名、密碼等。

  • 第三方服務的證書。

  • 每份部署獨有的配置,例如:域名、鏈接數、與部署目標環境資源規模有關的JVM參數等。

全部部署中都相同的信息,例如原則2裏講到的依賴信息,不在本原則所討論的範圍內。一些雖然在不一樣的部署中有所差別、可是和業務相關的信息,例如資金結算的轉換比例,也不屬於本原則所討論的配置。

我想大多數的開發者都知道如何經過使用配置文件實現配置和代碼的分離,可是這種方式仍然存在一些缺點,例如:

  1. 配置文件容易被開發人員不當心提交到代碼庫中,形成密碼、證書等敏感信息泄露。提交到代碼庫中的配置文件還容易被和應用一塊兒部署到目標環境中,極可能會致使在目標環境中應用了錯誤的配置或者形成配置衝突。

  2. 配置文件會分散在不一樣的目錄中,而且有不一樣的格式(配置文件的格式每每與開發語言和框架相關),這會給配置的統一管理形成困難。

爲了不上述問題,本原則要求將在環境中存儲配置。一種典型的方式是把配置存儲在環境變量中,這會使配置和代碼完全的分離,格式上也與開發語言和框架再無瓜葛,而且也不會被誤提交到代碼庫中。還可使用Spring Cloud Config Server這類配置管理服務進行配置推送,並將配置的歷史版本和變動緣由也一塊兒管理起來。

原則4:把後端服務看成附加資源

這裏的後端服務指的是應用運行所依賴的各類服務,例如數據庫、消息代理、緩存系統等,對於雲原生應用來講,每每還會有日誌收集服務、對象存儲服務、以及各類經過API訪問的服務;看成附加資源指的是把這些服務做爲外部的、經過網絡調用的資源。

該原則有以下幾層含義:

  1. 不要將這些服務放在應用本地:雲原生應用要求應用自己無狀態化,那麼狀態信息就應該存儲在外部服務中(參見不可變服務器)。同時,微服務模式要求應用責權單一以實現可靠性和擴展性,若是在應用本地放置數據庫,那麼微服務平臺將沒法經過更換應用的故障實例實現應用的高可用性,也沒法經過自動化的橫向伸縮實現擴展性,由於應用實例內包含兩種性質徹底不一樣的軟件(應用和數據庫),沒法對二者使用同一種方式進行橫向擴展。另外,若是將這些服務放在應用本地,那麼也沒法經過充分利用雲平臺提供的能力簡化運維工做,例如,若是在應用本地放置數據庫,而不是使用雲平臺提供的數據庫服務,那麼顯然沒法利用數據庫服務提供的自動備份、安全、和高可用等特性。

  2. 經過URL或者服務註冊/認證中心訪問這些後端服務:應用應該可以在不進行任何代碼修改的狀況下,在不一樣的目標環境中進行部署,應用不該該和後端服務的任何一種具體實現存在緊耦合關係。

  3. 相似「顯式聲明依賴關係」原則,應用最好也可以對其使用的這些後端服務進行顯示聲明,以方便雲平臺對服務資源進行自動綁定,在後端服務出現故障的時候,雲平臺也可以對其進行自動恢復。

原則5:嚴格分離構建、發佈和運行

在本原則中,構建、發佈和運行這三個概念可能和從前有所不一樣,所以有必要首先對其進行明確:

  • 構建指的是將應用代碼轉化爲執行體的過程:構建時會拉取特定版本的代碼和依賴項,將其編譯爲二進制文件(針對編譯型語言),並和資源文件一塊兒打包。

  • 發佈指的是將構建的結果和部署所需的配置相結合,並將其放置於運行環境之中。

  • 運行指的是將發佈的結果啓動爲運行環境中的一個或多個進程。

本原則要求構建、發佈和運行這三個步驟嚴格區分:

  1. 禁止直接修改運行狀態的代碼或者對應用進行打補丁,由於這些修改很難再同步回構建步驟,這時運行狀態的代碼就成爲了「孤本」。同時,也不該該在運行期間修改應用的配置,配置的修改應該僅限於發佈階段(參見不可變服務器)。

  2. 運行這一步驟應該很是簡單,僅限於啓動進程,資源文件的關聯應僅限於構建階段,配置的結合應僅限於發佈階段。

同時,每一次發佈都應該對應一個惟一的發佈ID,發佈的版本應當像一個只能追加的帳本,一旦發佈就不能修改。這麼作的好處是:

  1. 每一份運行狀態的代碼均可以在對應的發佈和構建階段找到它的來源,這是實現從新發布、故障實例的自動替換、發佈出錯後的版本回退等機制的基礎。

  2. 運行步驟很是簡單,這樣在硬件重啓、實例故障和橫向擴展等狀況下,應用能夠簡單和快速的實現重啓。

原則6:以一個或多個無狀態的進程運行應用

本原則要求應用進程的內部不要保存狀態信息,任何狀態信息都應該被保存在數據庫、緩存系統等外部服務中。應用實例之間的數據共享也要經過數據庫和緩存系統等外部服務進行,直接的數據共享不但違反無狀態原則,還引入了串行化的單點,這會爲應用的橫向擴展帶來障礙。

在微服務模式下,應用不該該在自身進程內部緩存數據以供未來的請求使用,由於微服務模式以多實例方式運行應用,未來的請求多半會被路由到其餘實例,此時雖然可使用粘滯會話將請求保持在同一個實例上,可是不管是雲原生應用仍是微服務模式都極力反對使用粘滯會話,緣由以下:

  1. 很難對粘滯會話實現負載均衡,由於粘滯會話的均衡性不只決定於負載均衡策略,還和會話自己的行爲相關,例如,可能存在應用某些實例上的會話已經大量退出,而另外一些實例上的會話依然處於活動狀態,此時這兩部分實例的負載處於不均衡狀態,而負載均衡器沒法將活動會話轉移到空閒的應用實例。

  2. 啓動新的應用實例不會當即提升應用的總體處理能力,由於這些新實例只能承接新會話,舊的會話依舊粘滯在舊的應用實例上。

  3. 應用實例退出會致使會話丟失,因此在實例發生故障時,即便雲平臺能夠對故障實例進行自動替換,也會致使用戶數據丟失。即便是對應用實例進行人工維護,也須要在維護以前對該實例上的會話進行轉移,這每每意味着須要編寫複雜的業務代碼。

    在傳統模式下,能夠經過在雙機之間進行會話複製來實現對用戶無感知的單機下線維護(雖然會付出處理能力減半的代價),可是在微服務模式下,應用的實例數量每每遠不止兩個,在大量的實例之間進行會話複製會使實例之間本來很是簡單的邏輯關係複雜化,此時將沒法經過雲平臺對其進行無差異的自動化維護。另外,在實例之間進行會話複製也意味着實例之間存在着直接的數據共享,這會爲應用的橫向擴展帶來障礙。

因此,粘滯會話是應用實現可用性和擴展性的重要障礙,使用粘滯會話顯然是種得不償失的選擇。更好的實現方式是將會話信息存儲在緩存服務中。

原則7:經過端口綁定提供服務

服務端應用經過網絡端口提供服務,這點毋庸置疑,可是本原則還有以下兩個深層次的含義:

  1. 不管是雲原生應用仍是微服務模式都要求應用應該徹底自我包含,而不是依賴於外部的應用服務器,端口綁定指的是應用直接與端口綁定,而不是經過應用服務器進行端口綁定。

    若是必定要使用應用服務器,那就使用嵌入式應用服務器,不管是雲原生應用仍是微服務模式都極力反對將多個應用放置於同一個應用服務器上運行,由於在這種模式下,一個應用出錯會對同一個應用服務器上的其餘應用形成影響,也沒法針對單一應用作橫向擴展。

  2. 端口綁定工做應該由雲平臺自動進行,雲平臺在實現應用到端口的綁定以外,還須要實現內部端口到外部端口的映射和外部端口到域名的映射。在應用的整個生命週期內,應用實例會經歷屢次的從新部署、重啓或者橫向擴展,端口會發生變化,但URL會保持不變。

原則8:經過進程模型進行擴展

與經過進程模型進行擴展相反的方式是經過線程模型進行擴展,這是一種相對較爲傳統的方式,典型的例子是Java應用。當咱們啓動一個Java進程的時候,一般會經過JVM參數爲其設置各個內存區域的容量上下限,同時還可能會在應用層面爲其設置一個或者多個線程池的容量上下限,當外部負載變化時,進程所佔用的內存容量和進程內部的線程數量能夠在這些預先設置好的上下限之間進行擴展,這種方式也被稱爲縱向擴展或者垂直擴展。

可是這種方式存在一些問題,首先,在進程的內存容量和線程數量提升時,應用的某些性能指標可能不會獲得同步提升,甚至可能會降低(這每每是由於程序對某些沒法擴展的資源進行爭用所形成的),這種良莠不齊的性能擴展對外部負載提升的承接能力會很不理想,有時甚至會拔苗助長;

其次,爲了使進程自己能夠完成縱向擴展,還須要在虛擬機層面或者容器層面爲其預留內存資源和對應的CPU資源,這會形成大量的資源浪費(固然,也可使虛擬機或者容器跟隨進程一塊兒進行縱向擴展,這在技術上是可行的,可是會爲虛擬機或者容器管理平臺的資源調度形成一些沒必要要的困難,例如頻繁的虛擬機遷移或者容器重啓)。

因此,如今更爲推崇使用「固定的」進程(對前面Java應用的例子來講,就是固定的內存容量和線程池容量),在外部負載提升時,啓動更多的進程,在外部負載下降時,中止一部分進程,這種方式就是本原則所說的經過進程模型進行擴展,有時候也被稱爲橫向擴展或者水平擴展。

這種擴展方式的好處是,在進程數量增長的時候,應用的各類性能指標會獲得同步的提升,這種提升即便不是線性的,也會按照一種平滑和可預期的曲線展開,能夠更爲穩定的應對外部負載的變化。

雲原生應用和微服務模式極力推崇將經過進程模型進行擴展做爲惟一的擴展方式,除了前文所述,還有一個緣由是進程是雲平臺能夠操做的最小運行單元(固然,能夠經過其餘技術手段去操做線程,可是那不會成爲雲平臺的通用技術特性),雲平臺能夠根據各個層面的監控數據,經過預設規則決定是否爲應用增長或者減小進程,例如,當前端的負載均衡器檢測到訪問某個後端應用的併發用戶數超過某個閾值時,能夠當即爲這個後端應用啓動更多的進程,以承接更大的負載,同時還能夠選擇是否對該應用後端的數據庫進行擴展。

若是此時選擇對應用進行縱向擴展,則雲平臺既不知道應用處理能力的變化,也沒法對這種變化進行預期管理,更沒法使應用的先後端對這種變化進行聯動,即該應用的擴展行爲脫離了雲平臺的管理。在微服務模式下,若是大量的進程都採用縱向擴展方式,則會爲平臺的資源調度帶來極大的混亂。

注3:該原則彷佛更適合被稱爲橫向擴展原則,可是爲了和12原則的原文保持一直,這裏咱們仍然將其稱爲「經過進程模型進行擴展」。

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

該原則要求應用能夠瞬間(理想狀況下是數秒或者更短)啓動和中止,由於這將有利於應用快速進行橫向擴展和變動或者故障後的從新部署,而這二者都是程序健壯性的體現。

前文不止一次提到過應用的快速啓動,在理念章節的開頭,咱們提到過平價的進程生成對多道程序設計相當重要,而微服務模式在某種程度上能夠認爲是多道程序設計在Web領域和分佈式系統下的進一步擴展,這裏所說的平價進程生成指的是操做系統的一種特性,是應用快速啓動的基礎,除此以外爲了保證應用能夠在數秒內完成啓動,還須要大量的優化工做,須要開發人員掌握複雜的調優技術與工具,有些工做必須在應用的初始設計階段完成,例如:若是應用體積過大或者是引用了太多的庫文件,那麼再多的後期優化也沒法將啓動時間下降到數秒之內。

「原則5:嚴格分離構建、發佈和運行」中咱們還提到,應用的運行步驟應該很是簡單,這裏的「簡單」也隱含着快速的意思,目的是爲了在硬件重啓、實例故障和橫向擴展等狀況下,應用能夠快速的實現重啓。除此以外,「原則6:以一個或多個無狀態的進程運行應用」也與應用的快速啓動有關,遵照無狀態原則,使用雲平臺提供的緩存服務,而不是在應用內部加載緩存,能夠避免在應用啓動期間進行耗時的緩存預熱。

比起應用的快速啓動,優雅終止(Graceful Shutdown)須要考慮的問題會更爲普遍一些。優雅終止須要儘量下降應用終止對用戶形成的不良影響(對於微服務應用,用戶多是人,也多是其餘微服務)。

對於短任務來講,這通常意味着拒絕全部新的請求,並將已經接收的請求處理完畢後再終止;對於長任務來講,這通常意味着應用重啓後的客戶端重連和爲任務設置斷點並在重啓後繼續執行。除此以外,優雅終止還須要釋放全部被進程鎖定的資源,並對事務的完整性和操做的冪等性作出完備的考慮。

最後,應用還必須應對突如其來的退出,在硬件出現故障時或者進程崩潰時,應用須要保證不會對其使用的數據形成損壞,遵照無狀態原則、將數據交由後端服務處理的應用能夠很容易的將應對忽然退出的複雜度外部化。

  • 12-Factor應用的進程是易處理(disposable)的,意思是說它們能夠瞬間開啓或中止。這有利於快速、彈性的伸縮應用,迅速部署變化的 代碼 或 配置 ,穩健的部署應用。
  • 進程應當追求最小啓動時間
  • 進程一旦接收終止信號(SIGTERM)就會優雅的終止
  • 進程還應當在面對忽然死亡時保持健壯,例如底層硬件故障
  • 12-Factor應用都應該能夠設計可以應對意外的、不優雅的終結。

原則10:開發環境與線上環境等價

本原則的淺層次含義是要求在開發環境和線上環境中使用相同的軟件棧,並儘量爲這些軟件棧使用相同的配置,以免「It works on my machine.」這類問題。本原則反對在不一樣的環境中使用不一樣的後端服務,雖然可使用適配器或者在代碼中作出兼容性考慮以消除後端服務的差別,可是這將牽扯開發人員和測試人員大量的精力以保證這些適配器和代碼確實能夠按預期工做,在應用的整個開發週期中,這將積累極大的額外工做量,是一種很是沒必要要的資源浪費。

近年來我的電腦的性能大幅提升,開發人員一度得以在本地開發環境中運行與生產環境中一致的軟件棧,而不是像曾經那樣採用輕量的替代方案。可是隨着雲原生應用和微服務模式的流行,狀況又發生了微妙的變化:開發微服務時須要依賴雲平臺提供的基礎服務和其餘微服務,愈來愈難以把這些服務完整的運行在本地,與此同時,徹底的在線開發愈發成爲一種趨勢,那樣的話至少在軟件棧上開發環境和線上環境就真的沒有任何區別了。

在我編寫這段文字的時候,Red Hat公司恰好在洽購在線開發環境創業公司Codenvy用以充實他們的雲平臺產品OpenShift,而另外一家與Codenvy相似的創業公司Cloud9在差很少一年前被Amazon公司收購。

本原則的深層次含義是儘可能縮小開發環境和線上環境中時間和人員的差別。開發環境中的代碼天天都在更新,而這些更新每每會累積數週甚至數月纔會被髮布到線上環境,這是開發環境和線上環境在時間上的巨大差別;開發人員只關心開發環境,運維人員只關心線上環境,開發人員和運維人員在工做上鮮有交集,這是開發環境和線上環境在人員上的巨大差別。

對於前一個差別,本原則要求更爲密集和頻繁的向線上環境發佈更新,要求創建機制以保障開發人員能夠在數小時甚至數分鐘內既可將更新發布到線上,這也正是本章理念部分中持續交付所提倡的;對於後一個差別,本原則要求開發人員不能只關心開發環境中本身的代碼,更要密切關注代碼的部署過程和代碼在線上的運行狀況,這也正是DevOps所提倡的。

  • 環境之間的差別:部署時間差別、人員分工差別、工具差別
  • 12-Factor應用想要作到持續部署就必須縮小本地與線上差別
  • 12-Factor 應用的開發人員應該反對在不一樣環境間使用不一樣的後端服務
  • 使用相似 Chef 和 Puppet 的聲明式配置工具,結合像 Vagrant 這樣輕量的虛擬環境就可使得開發人員的本地環境與線上環境無限接近。

原則11:把日誌看成事件流

  • 日誌使得應用程序運行的動做變得透明。在基於服務器的環境中,日誌一般被寫在硬盤的一個文件裏,但這只是一種輸出格式。
  • 日誌應該是 事件流 的彙總,將全部運行中進程和後端服務的輸出流按照時間順序收集起來。儘管在回溯問題時可能須要看不少行,日誌最原始的格式確實是一個事件一行。日誌沒有肯定開始和結束,但隨着應用在運行會持續的增長。
  • 12-factor應用自己從不考慮存儲本身的輸出流,不該該試圖去寫或者管理日誌文件。
  • 在預發佈或線上部署中,每一個進程的輸出流由運行環境截獲,並將其餘輸出流整理在一塊兒,而後一併發送給一個或多個最終的處理程序,用於查看或是長期存檔。這些存檔路徑對於應用來講不可見也不可配置,而是徹底交給程序的運行環境管理。相似 Logplex 和 Fluentd 的開源工具能夠達到這個目的。
  • 這些事件流能夠輸出至文件,或者在終端實時觀察。最重要的,輸出流能夠發送到 Splunk 這樣的日誌索引及分析系統,或 Hadoop/Hive 這樣的通用數據存儲系統。這些系統爲查看應用的歷史活動提供了強大而靈活的功能,包括:
    • 找出過去一段時間特殊的事件。
    • 圖形化一個大規模的趨勢,好比每分鐘的請求量。
    • 根據用戶定義的條件實時觸發警報,好比每分鐘的報錯超過某個警惕線。

原則12:後臺管理任務看成一次性進程運行

  • 進程構成(process formation)是指用來處理應用的常規業務(好比處理 web 請求)的一組進程。 與此不一樣,開發人員常常但願執行一些管理或維護應用的一次性任務,例如:
    • 運行數據移植(Django 中的 manage.py migrate, Rails 中的 rake db:migrate)。
    • 運行一個控制檯(也被稱爲 REPL shell),來執行一些代碼或是針對線上數據庫作一些檢查。大多數語言都經過解釋器提供了一個 REPL 工具(python 或 perl) ,或是其餘命令(Ruby 使用 irb, Rails 使用 rails console)。
    • 運行一些提交到代碼倉庫的一次性腳本。
  • 12-factor 尤爲青睞那些提供了 REPL shell 的語言,由於那會讓運行一次性腳本變得簡單。

總結

官方地址:https://12factor.net/zh_cn/

往上看到一個pdf講的還行吧,去下載收費25元,去他媽的,下載下來給你:連接:https://pan.baidu.com/s/1EZJJrgkvlpU1_d1xbVaPWg  提取碼:5gfp 

輔助理解:https://www.jianshu.com/p/bbdccd020a1d

相關文章
相關標籤/搜索