如何理解Heroku提出的12要素應(yīng)用?

簡(jiǎn)介

如今琅轧,軟件通常會(huì)作為一種服務(wù)來交付,它們被稱為網(wǎng)絡(luò)應(yīng)用程序踊挠,或軟件即服務(wù)(SaaS)乍桂。12-Factor 為構(gòu)建如下的 SaaS 應(yīng)用提供了方法論:

使用標(biāo)準(zhǔn)化流程自動(dòng)配置,從而使新的開發(fā)者花費(fèi)最少的學(xué)習(xí)成本加入這個(gè)項(xiàng)目效床。

和操作系統(tǒng)之間盡可能的劃清界限睹酌,在各個(gè)系統(tǒng)中提供最大的可移植性

適合部署在現(xiàn)代的云計(jì)算平臺(tái)剩檀,從而在服務(wù)器和系統(tǒng)管理方面節(jié)省資源憋沿。

將開發(fā)環(huán)境和生產(chǎn)環(huán)境的差異降至最低,并使用持續(xù)交付實(shí)施敏捷開發(fā)沪猴。

可以在工具辐啄、架構(gòu)和開發(fā)流程不發(fā)生明顯變化的前提下實(shí)現(xiàn)擴(kuò)展

這套理論適用于任意語言和后端服務(wù)(數(shù)據(jù)庫运嗜、消息隊(duì)列壶辜、緩存等)開發(fā)的應(yīng)用程序。

背景

本文的貢獻(xiàn)者參與過數(shù)以百計(jì)的應(yīng)用程序的開發(fā)和部署担租,并通過?Heroku?平臺(tái)間接見證了數(shù)十萬應(yīng)用程序的開發(fā)砸民,運(yùn)作以及擴(kuò)展的過程。

本文綜合了我們關(guān)于 SaaS 應(yīng)用幾乎所有的經(jīng)驗(yàn)和智慧翩活,是開發(fā)此類應(yīng)用的理想實(shí)踐標(biāo)準(zhǔn),并特別關(guān)注于應(yīng)用程序如何保持良性成長(zhǎng)便贵,開發(fā)者之間如何進(jìn)行有效的代碼協(xié)作菠镇,以及如何?避免軟件污染?。

我們的初衷是分享在現(xiàn)代軟件開發(fā)過程中發(fā)現(xiàn)的一些系統(tǒng)性問題承璃,并加深對(duì)這些問題的認(rèn)識(shí)利耍。我們提供了討論這些問題時(shí)所需的共享詞匯,同時(shí)使用相關(guān)術(shù)語給出一套針對(duì)這些問題的廣義解決方案盔粹。本文格式的靈感來自于 Martin Fowler 的書籍:?Patterns of Enterprise Application Architecture?隘梨,?Refactoring?。

讀者應(yīng)該是哪些人舷嗡?

任何 SaaS 應(yīng)用的開發(fā)人員轴猎。部署和管理此類應(yīng)用的運(yùn)維工程師。

I. 基準(zhǔn)代碼

一份基準(zhǔn)代碼(Codebase)进萄,多份部署(deploy

12-Factor應(yīng)用(譯者注:應(yīng)該是說一個(gè)使用本文概念來設(shè)計(jì)的應(yīng)用捻脖,下同)通常會(huì)使用版本控制系統(tǒng)加以管理锐峭,如Git,?Mercurial,?Subversion。一份用來跟蹤代碼所有修訂版本的數(shù)據(jù)庫被稱作?代碼庫(code repository, code repo, repo)可婶。

在類似 SVN 這樣的集中式版本控制系統(tǒng)中沿癞,基準(zhǔn)代碼?就是指控制系統(tǒng)中的這一份代碼庫;而在 Git 那樣的分布式版本控制系統(tǒng)中矛渴,基準(zhǔn)代碼?則是指最上游的那份代碼庫椎扬。

一份代碼庫對(duì)應(yīng)多份部署

基準(zhǔn)代碼和應(yīng)用之間總是保持一一對(duì)應(yīng)的關(guān)系:

一旦有多個(gè)基準(zhǔn)代碼,就不能稱為一個(gè)應(yīng)用具温,而是一個(gè)分布式系統(tǒng)蚕涤。分布式系統(tǒng)中的每一個(gè)組件都是一個(gè)應(yīng)用,每一個(gè)應(yīng)用可以分別使用 12-Factor 進(jìn)行開發(fā)桂躏。

多個(gè)應(yīng)用共享一份基準(zhǔn)代碼是有悖于 12-Factor 原則的钻趋。解決方案是將共享的代碼拆分為獨(dú)立的類庫,然后使用?依賴管理?策略去加載它們剂习。

盡管每個(gè)應(yīng)用只對(duì)應(yīng)一份基準(zhǔn)代碼蛮位,但可以同時(shí)存在多份部署。每份?部署?相當(dāng)于運(yùn)行了一個(gè)應(yīng)用的實(shí)例鳞绕。通常會(huì)有一個(gè)生產(chǎn)環(huán)境失仁,一個(gè)或多個(gè)預(yù)發(fā)布環(huán)境。此外们何,每個(gè)開發(fā)人員都會(huì)在自己本地環(huán)境運(yùn)行一個(gè)應(yīng)用實(shí)例萄焦,這些都相當(dāng)于一份部署。

所有部署的基準(zhǔn)代碼相同冤竹,但每份部署可以使用其不同的版本拂封。比如,開發(fā)人員可能有一些提交還沒有同步至預(yù)發(fā)布環(huán)境鹦蠕;預(yù)發(fā)布環(huán)境也有一些提交沒有同步至生產(chǎn)環(huán)境冒签。但它們都共享一份基準(zhǔn)代碼,我們就認(rèn)為它們只是相同應(yīng)用的不同部署而已钟病。

II. 依賴

顯式聲明依賴關(guān)系(?dependency?)

大多數(shù)編程語言都會(huì)提供一個(gè)打包系統(tǒng)萧恕,用來為各個(gè)類庫提供打包服務(wù),就像 Perl 的?CPAN?或是 Ruby 的?Rubygems?肠阱。通過打包系統(tǒng)安裝的類庫可以是系統(tǒng)級(jí)的(稱之為 “site packages”)票唆,或僅供某個(gè)應(yīng)用程序使用,部署在相應(yīng)的目錄中(稱之為 “vendoring” 或 “bunding”)屹徘。

12-Factor規(guī)則下的應(yīng)用程序不會(huì)隱式依賴系統(tǒng)級(jí)的類庫走趋。?它一定通過?依賴清單?,確切地聲明所有依賴項(xiàng)噪伊。此外吆视,在運(yùn)行過程中通過?依賴隔離?工具來確保程序不會(huì)調(diào)用系統(tǒng)中存在但清單中未聲明的依賴項(xiàng)典挑。這一做法會(huì)統(tǒng)一應(yīng)用到生產(chǎn)和開發(fā)環(huán)境。

例如啦吧, Ruby 的?Bundler?使用?Gemfile?作為依賴項(xiàng)聲明清單您觉,使用?bundle exec?來進(jìn)行依賴隔離。Python 中則可分別使用兩種工具 –?Pip?用作依賴聲明授滓,?Virtualenv?用作依賴隔離琳水。甚至 C 語言也有類似工具,?Autoconf?用作依賴聲明般堆,靜態(tài)鏈接庫用作依賴隔離在孝。無論用什么工具,依賴聲明和依賴隔離必須一起使用淮摔,否則無法滿足 12-Factor 規(guī)范私沮。

顯式聲明依賴的優(yōu)點(diǎn)之一是為新進(jìn)開發(fā)者簡(jiǎn)化了環(huán)境配置流程。新進(jìn)開發(fā)者可以檢出應(yīng)用程序的基準(zhǔn)代碼和橙,安裝編程語言環(huán)境和它對(duì)應(yīng)的依賴管理工具仔燕,只需通過一個(gè)?構(gòu)建命令?來安裝所有的依賴項(xiàng),即可開始工作魔招。例如晰搀,Ruby/Bundler 下使用?bundle install,而 Clojure/Leiningen?則是?lein deps办斑。

12-Factor 應(yīng)用同樣不會(huì)隱式依賴某些系統(tǒng)工具外恕,如 ImageMagick 或是curl雀监。即使這些工具存在于幾乎所有系統(tǒng)揣炕,但終究無法保證所有未來的系統(tǒng)都能支持應(yīng)用順利運(yùn)行唱较,或是能夠和應(yīng)用兼容锅风。如果應(yīng)用必須使用到某些系統(tǒng)工具,那么這些工具應(yīng)該被包含在應(yīng)用之中捻撑。

III. 配置

在環(huán)境中存儲(chǔ)配置

通常恕汇,應(yīng)用的?配置?在不同?部署?(預(yù)發(fā)布阀坏、生產(chǎn)環(huán)境波势、開發(fā)環(huán)境等等)間會(huì)有很大差異翎朱。這其中包括:

數(shù)據(jù)庫橄维,Memcached尺铣,以及其他?后端服務(wù)?的配置

第三方服務(wù)的證書,如 Amazon S3争舞、Twitter 等

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

有些應(yīng)用在代碼中使用常量保存配置,這與 12-Factor 所要求的代碼和配置嚴(yán)格分離顯然大相徑庭竞川。配置文件在各部署間存在大幅差異店溢,代碼卻完全一致叁熔。

判斷一個(gè)應(yīng)用是否正確地將配置排除在代碼之外,一個(gè)簡(jiǎn)單的方法是看該應(yīng)用的基準(zhǔn)代碼是否可以立刻開源床牧,而不用擔(dān)心會(huì)暴露任何敏感的信息荣回。

需要指出的是,這里定義的“配置”并包括應(yīng)用的內(nèi)部配置戈咳,比如 Rails 的?config/routes.rb心软,或是使用?Spring?時(shí)?代碼模塊間的依賴注入關(guān)系?。這類配置在不同部署間不存在差異著蛙,所以應(yīng)該寫入代碼删铃。

另外一個(gè)解決方法是使用配置文件,但不把它們納入版本控制系統(tǒng)踏堡,就像 Rails 的?config/database.yml?猎唁。這相對(duì)于在代碼中使用常量已經(jīng)是長(zhǎng)足進(jìn)步,但仍然有缺點(diǎn):總是會(huì)不小心將配置文件簽入了代碼庫顷蟆;配置文件的可能會(huì)分散在不同的目錄诫隅,并有著不同的格式,這讓找出一個(gè)地方來統(tǒng)一管理所有配置變的不太現(xiàn)實(shí)慕的。更糟的是阎肝,這些格式通常是語言或框架特定的。

12-Factor推薦將應(yīng)用的配置存儲(chǔ)于 環(huán)境變量 中(?env vars,?env?)肮街。環(huán)境變量可以非常方便地在不同的部署間做修改风题,卻不動(dòng)一行代碼;與配置文件不同嫉父,不小心把它們簽入代碼庫的概率微乎其微沛硅;與一些傳統(tǒng)的解決配置問題的機(jī)制(比如 Java 的屬性配置文件)相比,環(huán)境變量與語言和系統(tǒng)無關(guān)绕辖。

配置管理的另一個(gè)方面是分組摇肌。有時(shí)應(yīng)用會(huì)將配置按照特定部署進(jìn)行分組(或叫做“環(huán)境”),例如Rails中的?development,test, 和?production?環(huán)境仪际。這種方法無法輕易擴(kuò)展:更多部署意味著更多新的環(huán)境围小,例如?staging或?qa?。 隨著項(xiàng)目的不斷深入树碱,開發(fā)人員可能還會(huì)添加他們自己的環(huán)境肯适,比如?joes-staging?,這將導(dǎo)致各種配置組合的激增成榜,從而給管理部署增加了很多不確定因素框舔。

12-Factor 應(yīng)用中,環(huán)境變量的粒度要足夠小,且相對(duì)獨(dú)立刘绣。它們永遠(yuǎn)也不會(huì)組合成一個(gè)所謂的“環(huán)境”樱溉,而是獨(dú)立存在于每個(gè)部署之中。當(dāng)應(yīng)用程序不斷擴(kuò)展纬凤,需要更多種類的部署時(shí)福贞,這種配置管理方式能夠做到平滑過渡。

IV. 后端服務(wù)

把后端服務(wù)(backing services)當(dāng)作附加資源

后端服務(wù)是指程序運(yùn)行所需要的通過網(wǎng)絡(luò)調(diào)用的各種服務(wù)停士,如數(shù)據(jù)庫(MySQL肚医,CouchDB),消息/隊(duì)列系統(tǒng)(RabbitMQ向瓷,Beanstalkd)肠套,SMTP 郵件發(fā)送服務(wù)(Postfix),以及緩存系統(tǒng)(Memcached)猖任。

類似數(shù)據(jù)庫的后端服務(wù)你稚,通常由部署應(yīng)用程序的系統(tǒng)管理員一起管理。除了本地服務(wù)之外朱躺,應(yīng)用程序有可能使用了第三方發(fā)布和管理的服務(wù)刁赖。示例包括 SMTP(例如?Postmark),數(shù)據(jù)收集服務(wù)(例如?New Relic?或?Loggly)长搀,數(shù)據(jù)存儲(chǔ)服務(wù)(如?Amazon S3)宇弛,以及使用 API 訪問的服務(wù)(例如?Twitter,?Google Maps,?Last.fm)。

12-Factor 應(yīng)用不會(huì)區(qū)別對(duì)待本地或第三方服務(wù)源请。?對(duì)應(yīng)用程序而言枪芒,兩種都是附加資源,通過一個(gè) url 或是其他存儲(chǔ)在?配置?中的服務(wù)定位/服務(wù)證書來獲取數(shù)據(jù)谁尸。12-Factor 應(yīng)用的任意?部署?舅踪,都應(yīng)該可以在不進(jìn)行任何代碼改動(dòng)的情況下,將本地 MySQL 數(shù)據(jù)庫換成第三方服務(wù)(例如?Amazon RDS)良蛮。類似的抽碌,本地 SMTP 服務(wù)應(yīng)該也可以和第三方 SMTP 服務(wù)(例如 Postmark )互換。上述 2 個(gè)例子中决瞳,僅需修改配置中的資源地址货徙。

每個(gè)不同的后端服務(wù)是一份?資源?。例如皮胡,一個(gè) MySQL 數(shù)據(jù)庫是一個(gè)資源痴颊,兩個(gè) MySQL 數(shù)據(jù)庫(用來數(shù)據(jù)分區(qū))就被當(dāng)作是 2 個(gè)不同的資源。12-Factor 應(yīng)用將這些數(shù)據(jù)庫都視作?附加資源?胸囱,這些資源和它們附屬的部署保持松耦合祷舀。

一種部署附加4個(gè)后端服務(wù)

部署可以按需加載或卸載資源瀑梗。例如烹笔,如果應(yīng)用的數(shù)據(jù)庫服務(wù)由于硬件問題出現(xiàn)異常裳扯,管理員可以從最近的備份中恢復(fù)一個(gè)數(shù)據(jù)庫,卸載當(dāng)前的數(shù)據(jù)庫谤职,然后加載新的數(shù)據(jù)庫 – 整個(gè)過程都不需要修改代碼饰豺。

V. 構(gòu)建,發(fā)布允蜈,運(yùn)行

嚴(yán)格分離構(gòu)建和運(yùn)行

基準(zhǔn)代碼?轉(zhuǎn)化為一份部署(非開發(fā)環(huán)境)需要以下三個(gè)階段:

構(gòu)建階段?是指將代碼倉庫轉(zhuǎn)化為可執(zhí)行包的過程冤吨。構(gòu)建時(shí)會(huì)使用指定版本的代碼,獲取和打包?依賴項(xiàng)饶套,編譯成二進(jìn)制文件和資源文件漩蟆。

發(fā)布階段?會(huì)將構(gòu)建的結(jié)果和當(dāng)前部署所需?配置?相結(jié)合,并能夠立刻在運(yùn)行環(huán)境中投入使用妓蛮。

運(yùn)行階段?(或者說“運(yùn)行時(shí)”)是指針對(duì)選定的發(fā)布版本怠李,在執(zhí)行環(huán)境中啟動(dòng)一系列應(yīng)用程序?進(jìn)程

代碼被構(gòu)建蛤克,然后和配置結(jié)合成為發(fā)布版本

12-factor 應(yīng)用嚴(yán)格區(qū)分構(gòu)建捺癞,發(fā)布,運(yùn)行這三個(gè)步驟构挤。?舉例來說髓介,直接修改處于運(yùn)行狀態(tài)的代碼是非常不可取的做法,因?yàn)檫@些修改很難再同步回構(gòu)建步驟筋现。

部署工具通常都提供了發(fā)布管理工具唐础,最引人注目的功能是退回至較舊的發(fā)布版本。比如矾飞,?Capistrano?將所有發(fā)布版本都存儲(chǔ)在一個(gè)叫?releases?的子目錄中彻犁,當(dāng)前的在線版本只需映射至對(duì)應(yīng)的目錄即可。該工具的?rollback?命令可以很容易地實(shí)現(xiàn)回退版本的功能凰慈。

每一個(gè)發(fā)布版本必須對(duì)應(yīng)一個(gè)唯一的發(fā)布 ID汞幢,例如可以使用發(fā)布時(shí)的時(shí)間戳(2011-04-06-20:32:17),亦或是一個(gè)增長(zhǎng)的數(shù)字(v100)微谓。發(fā)布的版本就像一本只能追加的賬本森篷,一旦發(fā)布就不可修改,任何的變動(dòng)都應(yīng)該產(chǎn)生一個(gè)新的發(fā)布版本豺型。

新的代碼在部署之前仲智,需要開發(fā)人員觸發(fā)構(gòu)建操作。但是姻氨,運(yùn)行階段不一定需要人為觸發(fā)钓辆,而是可以自動(dòng)進(jìn)行。如服務(wù)器重啟,或是進(jìn)程管理器重啟了一個(gè)崩潰的進(jìn)程前联。因此功戚,運(yùn)行階段應(yīng)該保持盡可能少的模塊,這樣假設(shè)半夜發(fā)生系統(tǒng)故障而開發(fā)人員又捉襟見肘也不會(huì)引起太大問題似嗤。構(gòu)建階段是可以相對(duì)復(fù)雜一些的啸臀,因?yàn)殄e(cuò)誤信息能夠立刻展示在開發(fā)人員面前,從而得到妥善處理烁落。

VI. 進(jìn)程

以一個(gè)或多個(gè)無狀態(tài)進(jìn)程運(yùn)行應(yīng)用

運(yùn)行環(huán)境中乘粒,應(yīng)用程序通常是以一個(gè)和多個(gè)?進(jìn)程?運(yùn)行的。

最簡(jiǎn)單的場(chǎng)景中伤塌,代碼是一個(gè)獨(dú)立的腳本灯萍,運(yùn)行環(huán)境是開發(fā)人員自己的筆記本電腦,進(jìn)程由一條命令行(例如python my_script.py)每聪。另外一個(gè)極端情況是竟稳,復(fù)雜的應(yīng)用可能會(huì)使用很多?進(jìn)程類型?,也就是零個(gè)或多個(gè)進(jìn)程實(shí)例熊痴。

12-Factor 應(yīng)用的進(jìn)程必須無狀態(tài)且 無共享 他爸。?任何需要持久化的數(shù)據(jù)都要存儲(chǔ)在?后端服務(wù)?內(nèi),比如數(shù)據(jù)庫果善。

內(nèi)存區(qū)域或磁盤空間可以作為進(jìn)程在做某種事務(wù)型操作時(shí)的緩存诊笤,例如下載一個(gè)很大的文件,對(duì)其操作并將結(jié)果寫入數(shù)據(jù)庫的過程巾陕。12-Factor應(yīng)用根本不用考慮這些緩存的內(nèi)容是不是可以保留給之后的請(qǐng)求來使用讨跟,這是因?yàn)閼?yīng)用啟動(dòng)了多種類型的進(jìn)程,將來的請(qǐng)求多半會(huì)由其他進(jìn)程來服務(wù)鄙煤。即使在只有一個(gè)進(jìn)程的情形下晾匠,先前保存的數(shù)據(jù)(內(nèi)存或文件系統(tǒng)中)也會(huì)因?yàn)橹貑ⅲㄈ绱a部署、配置更改梯刚、或運(yùn)行環(huán)境將進(jìn)程調(diào)度至另一個(gè)物理區(qū)域執(zhí)行)而丟失凉馆。

源文件打包工具(Jammit,?django-compressor) 使用文件系統(tǒng)來緩存編譯過的源文件。12-Factor 應(yīng)用更傾向于在?構(gòu)建步驟做此動(dòng)作——正如?Rails資源管道?亡资,而不是在運(yùn)行階段澜共。

一些互聯(lián)網(wǎng)系統(tǒng)依賴于 “粘性 session”, 這是指將用戶 session 中的數(shù)據(jù)緩存至某進(jìn)程的內(nèi)存中锥腻,并將同一用戶的后續(xù)請(qǐng)求路由到同一個(gè)進(jìn)程嗦董。粘性 session 是 12-Factor 極力反對(duì)的。Session 中的數(shù)據(jù)應(yīng)該保存在諸如?Memcached?或?Redis?這樣的帶有過期時(shí)間的緩存中瘦黑。

VII. 端口綁定

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

互聯(lián)網(wǎng)應(yīng)用有時(shí)會(huì)運(yùn)行于服務(wù)器的容器之中京革。例如 PHP 經(jīng)常作為?Apache HTTPD?的一個(gè)模塊來運(yùn)行奇唤,正如 Java 運(yùn)行于?Tomcat?。

12-Factor 應(yīng)用完全自我加載?而不依賴于任何網(wǎng)絡(luò)服務(wù)器就可以創(chuàng)建一個(gè)面向網(wǎng)絡(luò)的服務(wù)匹摇∫龋互聯(lián)網(wǎng)應(yīng)用?通過端口綁定來提供服務(wù)?,并監(jiān)聽發(fā)送至該端口的請(qǐng)求来惧。

本地環(huán)境中,開發(fā)人員通過類似http://localhost:5000/的地址來訪問服務(wù)演顾。在線上環(huán)境中供搀,請(qǐng)求統(tǒng)一發(fā)送至公共域名而后路由至綁定了端口的網(wǎng)絡(luò)進(jìn)程。

通常的實(shí)現(xiàn)思路是钠至,將網(wǎng)絡(luò)服務(wù)器類庫通過?依賴聲明?載入應(yīng)用葛虐。例如,Python 的?Tornado, Ruby 的Thin?, Java 以及其他基于 JVM 語言的?Jetty棉钧。完全由?用戶端?屿脐,確切的說應(yīng)該是應(yīng)用的代碼,發(fā)起請(qǐng)求宪卿。和運(yùn)行環(huán)境約定好綁定的端口即可處理這些請(qǐng)求的诵。

HTTP 并不是唯一一個(gè)可以由端口綁定提供的服務(wù)。其實(shí)幾乎所有服務(wù)器軟件都可以通過進(jìn)程綁定端口來等待請(qǐng)求佑钾。例如西疤,使用?XMPP?的?ejabberd?, 以及使用?Redis 協(xié)議?的?Redis?休溶。

還要指出的是代赁,端口綁定這種方式也意味著一個(gè)應(yīng)用可以成為另外一個(gè)應(yīng)用的?后端服務(wù),調(diào)用方將服務(wù)方提供的相應(yīng) URL 當(dāng)作資源存入?配置?以備將來調(diào)用兽掰。

VIII. 并發(fā)

通過進(jìn)程模型進(jìn)行擴(kuò)展

任何計(jì)算機(jī)程序芭碍,一旦啟動(dòng),就會(huì)生成一個(gè)或多個(gè)進(jìn)程孽尽〗押荆互聯(lián)網(wǎng)應(yīng)用采用多種進(jìn)程運(yùn)行方式。例如杉女,PHP 進(jìn)程作為 Apache 的子進(jìn)程存在艇拍,隨請(qǐng)求按需啟動(dòng)。Java 進(jìn)程則采取了相反的方式宠纯,在程序啟動(dòng)之初 JVM 就提供了一個(gè)超級(jí)進(jìn)程儲(chǔ)備了大量的系統(tǒng)資源(CPU 和內(nèi)存)卸夕,并通過多線程實(shí)現(xiàn)內(nèi)部的并發(fā)管理。上述 2 個(gè)例子中婆瓜,進(jìn)程是開發(fā)人員可以操作的最小單位快集。

擴(kuò)展表現(xiàn)為運(yùn)行中的進(jìn)程贡羔,工作多樣性表現(xiàn)為進(jìn)程類型。

在 12-factor 應(yīng)用中个初,進(jìn)程是一等公民乖寒。12-Factor 應(yīng)用的進(jìn)程主要借鑒于?unix 守護(hù)進(jìn)程模型?。開發(fā)人員可以運(yùn)用這個(gè)模型去設(shè)計(jì)應(yīng)用架構(gòu)院溺,將不同的工作分配給不同的?進(jìn)程類型?楣嘁。例如,HTTP 請(qǐng)求可以交給 web 進(jìn)程來處理珍逸,而常駐的后臺(tái)工作則交由 worker 進(jìn)程負(fù)責(zé)逐虚。

這并不包括個(gè)別較為特殊的進(jìn)程,例如通過虛擬機(jī)的線程處理并發(fā)的內(nèi)部運(yùn)算谆膳,或是使用諸如?EventMachine,?Twisted,?Node.js?的異步/事件觸發(fā)模型叭爱。但一臺(tái)獨(dú)立的虛擬機(jī)的擴(kuò)展有瓶頸(垂直擴(kuò)展),所以應(yīng)用程序必須可以在多臺(tái)物理機(jī)器間跨進(jìn)程工作漱病。

上述進(jìn)程模型會(huì)在系統(tǒng)急需擴(kuò)展時(shí)大放異彩买雾。?12-Factor 應(yīng)用的進(jìn)程所具備的無共享,水平分區(qū)的特性?意味著添加并發(fā)會(huì)變得簡(jiǎn)單而穩(wěn)妥杨帽。這些進(jìn)程的類型以及每個(gè)類型中進(jìn)程的數(shù)量就被稱作?進(jìn)程構(gòu)成?漓穿。

12-Factor 應(yīng)用的進(jìn)程?不需要守護(hù)進(jìn)程?或是寫入 PID 文件。相反的注盈,應(yīng)該借助操作系統(tǒng)的進(jìn)程管理器(例如?systemd?器净,分布式的進(jìn)程管理云平臺(tái),或是類似?Foreman?的工具)当凡,來管理?輸出流?山害,響應(yīng)崩潰的進(jìn)程,以及處理用戶觸發(fā)的重啟和關(guān)閉超級(jí)進(jìn)程的請(qǐng)求沿量。

IX. 易處理

快速啟動(dòng)和優(yōu)雅終止可最大化健壯性

12-Factor 應(yīng)用的 進(jìn)程 是 易處理(disposable)的浪慌,意思是說它們可以瞬間開啟或停止。?這有利于快速朴则、彈性的伸縮應(yīng)用权纤,迅速部署變化的?代碼?或?配置?,穩(wěn)健的部署應(yīng)用乌妒。

進(jìn)程應(yīng)當(dāng)追求?最小啟動(dòng)時(shí)間?汹想。 理想狀態(tài)下,進(jìn)程從敲下命令到真正啟動(dòng)并等待請(qǐng)求的時(shí)間應(yīng)該只需很短的時(shí)間撤蚊。更少的啟動(dòng)時(shí)間提供了更敏捷的?發(fā)布?以及擴(kuò)展過程古掏,此外還增加了健壯性,因?yàn)檫M(jìn)程管理器可以在授權(quán)情形下容易的將進(jìn)程搬到新的物理機(jī)器上侦啸。

進(jìn)程?一旦接收 終止信號(hào)(SIGTERM) 就會(huì)優(yōu)雅的終止?槽唾。就網(wǎng)絡(luò)進(jìn)程而言丧枪,優(yōu)雅終止是指停止監(jiān)聽服務(wù)的端口,即拒絕所有新的請(qǐng)求庞萍,并繼續(xù)執(zhí)行當(dāng)前已接收的請(qǐng)求拧烦,然后退出。此類型的進(jìn)程所隱含的要求是HTTP請(qǐng)求大多都很短(不會(huì)超過幾秒鐘)钝计,而在長(zhǎng)時(shí)間輪詢中恋博,客戶端在丟失連接后應(yīng)該馬上嘗試重連。

對(duì)于 worker 進(jìn)程來說私恬,優(yōu)雅終止是指將當(dāng)前任務(wù)退回隊(duì)列债沮。例如,RabbitMQ?中践付,worker 可以發(fā)送一個(gè)NACK信號(hào)秦士。?Beanstalkd?中缺厉,任務(wù)終止并退回隊(duì)列會(huì)在worker斷開時(shí)自動(dòng)觸發(fā)永高。有鎖機(jī)制的系統(tǒng)諸如?Delayed Job?則需要確定釋放了系統(tǒng)資源。此類型的進(jìn)程所隱含的要求是提针,任務(wù)都應(yīng)該?可重復(fù)執(zhí)行?命爬, 這主要由將結(jié)果包裝進(jìn)事務(wù)或是使重復(fù)操作?冪等?來實(shí)現(xiàn)。

進(jìn)程還應(yīng)當(dāng)在面對(duì)突然死亡時(shí)保持健壯辐脖,例如底層硬件故障饲宛。雖然這種情況比起優(yōu)雅終止來說少之又少,但終究有可能發(fā)生嗜价。一種推薦的方式是使用一個(gè)健壯的后端隊(duì)列艇抠,例如?Beanstalkd?,它可以在客戶端斷開或超時(shí)后自動(dòng)退回任務(wù)久锥。無論如何家淤,12-Factor 應(yīng)用都應(yīng)該可以設(shè)計(jì)能夠應(yīng)對(duì)意外的、不優(yōu)雅的終結(jié)瑟由。Crash-only design?將這種概念轉(zhuǎn)化為?合乎邏輯的理論絮重。

X. 開發(fā)環(huán)境與線上環(huán)境等價(jià)

盡可能的保持開發(fā),預(yù)發(fā)布歹苦,線上環(huán)境相同

從以往經(jīng)驗(yàn)來看青伤,開發(fā)環(huán)境(即開發(fā)人員的本地?部署)和線上環(huán)境(外部用戶訪問的真實(shí)部署)之間存在著很多差異。這些差異表現(xiàn)在以下三個(gè)方面:

時(shí)間差異:?開發(fā)人員正在編寫的代碼可能需要幾天殴瘦,幾周狠角,甚至幾個(gè)月才會(huì)上線。

人員差異:?開發(fā)人員編寫代碼蚪腋,運(yùn)維人員部署代碼擎厢。

工具差異:?開發(fā)人員或許使用 Nginx究流,SQLite,OS X动遭,而線上環(huán)境使用 Apache芬探,MySQL 以及 Linux。

12-Factor 應(yīng)用想要做到 持續(xù)部署 就必須縮小本地與線上差異厘惦。?再回頭看上面所描述的三個(gè)差異:

縮小時(shí)間差異:開發(fā)人員可以幾小時(shí)偷仿,甚至幾分鐘就部署代碼。

縮小人員差異:開發(fā)人員不只要編寫代碼宵蕉,更應(yīng)該密切參與部署過程以及代碼在線上的表現(xiàn)酝静。

縮小工具差異:盡量保證開發(fā)環(huán)境以及線上環(huán)境的一致性。

將上述總結(jié)變?yōu)橐粋€(gè)表格如下:

傳統(tǒng)應(yīng)用12-Factor 應(yīng)用

每次部署間隔數(shù)周幾小時(shí)

開發(fā)人員 vs 運(yùn)維人員不同的人相同的人

開發(fā)環(huán)境 vs 線上環(huán)境不同盡量接近

后端服務(wù)?是保持開發(fā)與線上等價(jià)的重要部分羡玛,例如數(shù)據(jù)庫别智,隊(duì)列系統(tǒng),以及緩存稼稿。許多語言都提供了簡(jiǎn)化獲取后端服務(wù)的類庫薄榛,例如不同類型服務(wù)的?適配器?。下列表格提供了一些例子让歼。

類型語言類庫適配器

數(shù)據(jù)庫Ruby/RailsActiveRecordMySQL, PostgreSQL, SQLite

隊(duì)列Python/DjangoCeleryRabbitMQ, Beanstalkd, Redis

緩存Ruby/RailsActiveSupport::CacheMemory, filesystem, Memcached

開發(fā)人員有時(shí)會(huì)覺得在本地環(huán)境中使用輕量的后端服務(wù)具有很強(qiáng)的吸引力敞恋,而那些更重量級(jí)的健壯的后端服務(wù)應(yīng)該使用在生產(chǎn)環(huán)境。例如谋右,本地使用 SQLite 線上使用 PostgreSQL硬猫;又如本地緩存在進(jìn)程內(nèi)存中而線上存入 Memcached。

12-Factor 應(yīng)用的開發(fā)人員應(yīng)該反對(duì)在不同環(huán)境間使用不同的后端服務(wù)?改执,即使適配器已經(jīng)可以幾乎消除使用上的差異啸蜜。這是因?yàn)椋煌暮蠖朔?wù)意味著會(huì)突然出現(xiàn)的不兼容辈挂,從而導(dǎo)致測(cè)試衬横、預(yù)發(fā)布都正常的代碼在線上出現(xiàn)問題。這些錯(cuò)誤會(huì)給持續(xù)部署帶來阻力呢岗。從應(yīng)用程序的生命周期來看冕香,消除這種阻力需要花費(fèi)很大的代價(jià)。

與此同時(shí)后豫,輕量的本地服務(wù)也不像以前那樣引人注目悉尾。借助于Homebrewapt-get等現(xiàn)代的打包系統(tǒng)挫酿,諸如Memcached构眯、PostgreSQL、RabbitMQ 等后端服務(wù)的安裝與運(yùn)行也并不復(fù)雜早龟。此外惫霸,使用類似?Chef?和?Puppet?的聲明式配置工具猫缭,結(jié)合像?Vagrant?這樣輕量的虛擬環(huán)境就可以使得開發(fā)人員的本地環(huán)境與線上環(huán)境無限接近。與同步環(huán)境和持續(xù)部署所帶來的益處相比壹店,安裝這些系統(tǒng)顯然是值得的猜丹。

不同后端服務(wù)的適配器仍然是有用的,因?yàn)樗鼈兛梢允挂浦埠蠖朔?wù)變得簡(jiǎn)單硅卢。但應(yīng)用的所有部署射窒,這其中包括開發(fā)、預(yù)發(fā)布以及線上環(huán)境将塑,都應(yīng)該使用同一個(gè)后端服務(wù)的相同版本脉顿。

XI. 日志

把日志當(dāng)作事件流

日志?使得應(yīng)用程序運(yùn)行的動(dòng)作變得透明。在基于服務(wù)器的環(huán)境中点寥,日志通常被寫在硬盤的一個(gè)文件里艾疟,但這只是一種輸出格式。

日志應(yīng)該是?事件流?的匯總敢辩,將所有運(yùn)行中進(jìn)程和后端服務(wù)的輸出流按照時(shí)間順序收集起來蔽莱。盡管在回溯問題時(shí)可能需要看很多行,日志最原始的格式確實(shí)是一個(gè)事件一行责鳍。日志沒有確定開始和結(jié)束碾褂,但隨著應(yīng)用在運(yùn)行會(huì)持續(xù)的增加兽间。

12-factor應(yīng)用本身從不考慮存儲(chǔ)自己的輸出流历葛。?不應(yīng)該試圖去寫或者管理日志文件。相反嘀略,每一個(gè)運(yùn)行的進(jìn)程都會(huì)直接的標(biāo)準(zhǔn)輸出(stdout)事件流恤溶。開發(fā)環(huán)境中,開發(fā)人員可以通過這些數(shù)據(jù)流帜羊,實(shí)時(shí)在終端看到應(yīng)用的活動(dòng)咒程。

在預(yù)發(fā)布或線上部署中,每個(gè)進(jìn)程的輸出流由運(yùn)行環(huán)境截獲讼育,并將其他輸出流整理在一起帐姻,然后一并發(fā)送給一個(gè)或多個(gè)最終的處理程序,用于查看或是長(zhǎng)期存檔奶段。這些存檔路徑對(duì)于應(yīng)用來說不可見也不可配置饥瓷,而是完全交給程序的運(yùn)行環(huán)境管理。類似?Logplex和?Fluentd?的開源工具可以達(dá)到這個(gè)目的痹籍。

這些事件流可以輸出至文件呢铆,或者在終端實(shí)時(shí)觀察。最重要的蹲缠,輸出流可以發(fā)送到?Splunk?這樣的日志索引及分析系統(tǒng)棺克,或?Hadoop/Hive?這樣的通用數(shù)據(jù)存儲(chǔ)系統(tǒng)悠垛。這些系統(tǒng)為查看應(yīng)用的歷史活動(dòng)提供了強(qiáng)大而靈活的功能,包括:

找出過去一段時(shí)間特殊的事件娜谊。

圖形化一個(gè)大規(guī)模的趨勢(shì)确买,比如每分鐘的請(qǐng)求量。

根據(jù)用戶定義的條件實(shí)時(shí)觸發(fā)警報(bào)纱皆,比如每分鐘的報(bào)錯(cuò)超過某個(gè)警戒線拇惋。

文章參考:The Twelve-Factor App

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市抹剩,隨后出現(xiàn)的幾起案子撑帖,更是在濱河造成了極大的恐慌,老刑警劉巖澳眷,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胡嘿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钳踊,警方通過查閱死者的電腦和手機(jī)衷敌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拓瞪,“玉大人缴罗,你說我怎么就攤上這事〖拦。” “怎么了面氓?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蛆橡。 經(jīng)常有香客問我舌界,道長(zhǎng),這世上最難降的妖魔是什么泰演? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任呻拌,我火速辦了婚禮,結(jié)果婚禮上睦焕,老公的妹妹穿的比我還像新娘藐握。我一直安慰自己,他們只是感情好垃喊,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布猾普。 她就那樣靜靜地躺著,像睡著了一般缔御。 火紅的嫁衣襯著肌膚如雪抬闷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音笤成,去河邊找鬼评架。 笑死,一個(gè)胖子當(dāng)著我的面吹牛炕泳,可吹牛的內(nèi)容都是我干的纵诞。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼培遵,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼浙芙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起籽腕,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤嗡呼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后皇耗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體南窗,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年郎楼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了万伤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呜袁,死狀恐怖敌买,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阶界,我是刑警寧澤虹钮,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站荐操,受9級(jí)特大地震影響芜抒,放射性物質(zhì)發(fā)生泄漏珍策。R本人自食惡果不足惜托启,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望攘宙。 院中可真熱鬧屯耸,春花似錦、人聲如沸蹭劈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铺韧。三九已至多矮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背塔逃。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來泰國打工讯壶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人湾盗。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓伏蚊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親格粪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子躏吊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容