公有云(AWS)上的生產(chǎn)環(huán)境架構(gòu)優(yōu)化案例和遷移套路

本文是我在 gitchat 上的文章云計(jì)算生產(chǎn)環(huán)境架構(gòu)性能調(diào)優(yōu)和遷移套路總結(jié)(以 AWS 為例)的后半部分,本文對原文有所修改和總結(jié)。交流實(shí)錄請點(diǎn)擊這里

AWS 上的生產(chǎn)環(huán)境性能分析案例一文中,記錄了我對客戶應(yīng)用生產(chǎn)環(huán)境的一次性能分析。接下來,我們要根據(jù)所發(fā)現(xiàn)的性能問題進(jìn)行架構(gòu)優(yōu)化,以提升可用性和性能。同時,這篇文章也總結(jié)了應(yīng)用遷移到云上的套路。

設(shè)計(jì)云計(jì)算平臺遷移計(jì)劃和方案

將應(yīng)用程序遷移到云計(jì)算平臺上主要的目的是把自行構(gòu)建的高風(fēng)險高成本應(yīng)用以及組件替換為云計(jì)算平臺上的高可靠性低成本組件/服務(wù)。

應(yīng)用架構(gòu)的遷移有兩種方案:

一種是整體一次性遷移,即重新實(shí)現(xiàn)一個架構(gòu)并完成部署,然后通過金絲雀發(fā)布或者藍(lán)綠發(fā)布切換。這種方式的好處?是簡單,直接,有效,一開始就能按照最佳實(shí)踐構(gòu)建應(yīng)用架構(gòu)。而且對于現(xiàn)有系統(tǒng)來說影響不大。但如果方案沒設(shè)計(jì)好,容易造成高級別的風(fēng)險,所以應(yīng)當(dāng)進(jìn)行大量的測試以確保可靠性。

另一種是持續(xù)部分遷移,每次引入一點(diǎn)風(fēng)險,保證風(fēng)險可控,但缺點(diǎn)就是優(yōu)化步驟較多。雖然持續(xù)部分遷移步驟多,但是總體時間并不一定會比整體遷移更高。

注意:由于自動化基礎(chǔ)設(shè)施和架構(gòu)設(shè)計(jì)會帶來一些副作用,特別是配置間的耦合。因此,對于生產(chǎn)環(huán)境的直接優(yōu)化要慎用自動化。如果一定要用,請務(wù)必在測試環(huán)境上做好測試。但如果你能做到自動化并且有完好的測試,不如直接做整體一次性遷移方案得了。

一般說來,一個完整的云平臺遷移方案會分為以下三大階段:

第一階段:構(gòu)建高可用架構(gòu)以實(shí)施水平擴(kuò)展,從而保證了應(yīng)用的穩(wěn)定運(yùn)行。

第二階段:引入 APM 并根據(jù) APM 數(shù)據(jù)進(jìn)行定向優(yōu)化,采用云計(jì)算的服務(wù)來優(yōu)化應(yīng)用的資源使用。

第三階段:構(gòu)建應(yīng)用端的持續(xù)部署,構(gòu)建 DevOps 的工作模式。

這三個階段是大的順序,而每個大的階段里又會相互摻雜一些其它階段的內(nèi)容。但無論什么樣的遷移方案,一定要通過度量進(jìn)行風(fēng)險/收益比排序,最先完成代價最小,收益最大的內(nèi)容。

第一階段:構(gòu)建高可用架構(gòu)

我們之前說過,一個應(yīng)用架構(gòu)的第一追求就是業(yè)務(wù)的連續(xù)性和抗風(fēng)險能力。一個高可用的架構(gòu)能夠在你的應(yīng)用面對壓力的時候從容不迫。因?yàn)槿绻Y源滿負(fù)荷運(yùn)轉(zhuǎn),新的請求會因?yàn)闆]有可用資源而導(dǎo)致排隊(duì)。這是常見的停機(jī)或者性能降低的原因。這就是 AFK 擴(kuò)展矩陣常說的 X 軸擴(kuò)展:通過復(fù)制自己擴(kuò)展資源從而達(dá)到降低排隊(duì)等待的時間。 此外,水平擴(kuò)展出來的機(jī)器同樣也是一個預(yù)留資源,能夠提高應(yīng)用的可用性。應(yīng)用架構(gòu)不僅僅是應(yīng)用程序的事情,也包含著資源的分配,二者是相輔相成的。

一般會經(jīng)歷如下幾步:

第一步,有狀態(tài)和無狀態(tài)分離

第二步,牲畜化(Cattlize)應(yīng)用實(shí)例

第三步,自動化水平擴(kuò)展(AutoScaling)

第一步:有狀態(tài)和無狀態(tài)分離

先回顧一下當(dāng)前應(yīng)用的架構(gòu) :


當(dāng)前應(yīng)用架構(gòu)

狀態(tài)分離的目標(biāo)是把有狀態(tài)的組件和無狀態(tài)的組件分開,以便在做復(fù)制的時候降低不一致性。最簡單的判定辦法是:如果復(fù)制當(dāng)前的虛擬資源,并通過負(fù)載均衡隨機(jī)分配請求訪問,哪些部分會造成不一致。

常見的有狀態(tài)內(nèi)容比如數(shù)據(jù)庫,上傳的文件。所以,我們要把它們獨(dú)立出來。在“薩瓦迪卡”的例子中,我們首先把數(shù)據(jù)庫獨(dú)立了出來。如下圖所示:

數(shù)據(jù)庫分離

在這個過程中,我們采用 RDS 而不是另外一個 EC2 上構(gòu)建一套 MySQL 來完成數(shù)據(jù)庫的分離。最主要的原因就是 RDS 提供了更好的可用性和數(shù)據(jù)庫維護(hù)支持,例如自動備份,更多的監(jiān)控指標(biāo),更自動的數(shù)據(jù)庫遷移和維護(hù)窗口等。我們采用 Aurora 引擎的 MySQL 模式,這可以將數(shù)據(jù)庫做成一個集群并讓另外一個只讀分片,降低數(shù)據(jù)庫的負(fù)擔(dān)。

在分離數(shù)據(jù)庫的時候,要注意以下幾點(diǎn):

  1. 數(shù)據(jù)庫分離的性能基線就是在同樣的負(fù)載測試下,不能夠比沒分離之前更差。
  2. 數(shù)據(jù)庫的網(wǎng)絡(luò)建立在一個私有的子網(wǎng)中,除了應(yīng)用子網(wǎng)內(nèi)的 IP 不能訪問數(shù)據(jù)庫,從而提高安全性。
  3. 構(gòu)建一個私有域名來訪問數(shù)據(jù)庫,這樣可以固定應(yīng)用的內(nèi)部配置,減少對配置的修改。同時也給外部切換數(shù)據(jù)庫主備等留下了更靈活的空間。
  4. 注意對原有數(shù)據(jù)庫 MySQL 配置信息的復(fù)制,這會導(dǎo)致很大程度上的性能差異。
  5. 對于數(shù)據(jù)較大的數(shù)據(jù)庫啟動而言,會有一個幾分鐘的熱身(Warm up)時間,這會導(dǎo)致性能下降。所以,做切換的時候提前啟動數(shù)據(jù)庫以做好準(zhǔn)備。
  6. 不要用默認(rèn)的 root 賬戶作為應(yīng)用的訪問賬戶。
  7. 由于 RDS 可以在不影響數(shù)據(jù)完整性和一致性的情況下降低使用配置,在最開始的時候采用較高的配置。隨著優(yōu)化的不斷進(jìn)行,可以采用維護(hù)時間窗口(Maintenance Time Window)在低流量時段對 RDS 實(shí)例的配置進(jìn)行降級,以節(jié)約成本。

完成了數(shù)據(jù)庫的隔離,我們就可以依法炮制文件的隔離了。最簡單有效的方案是把文件存儲在對象存儲服務(wù)中。AWS S3 就是這樣一種服務(wù)。避免自己構(gòu)建共享文件系統(tǒng)或者共享存儲設(shè)備。

文件相較于數(shù)據(jù)庫來說,占用的內(nèi)存資源和 CPU 資源較少,大部分的處理為 IO 處理,只要網(wǎng)絡(luò)和設(shè)備的 IOPS 足夠。一般不會出現(xiàn)大的問題。

為了降低文件隔離帶來的問題,在遷移文件的時候盡量保證文件目錄結(jié)構(gòu)不變,只改變文件訪問根(root)的位置。對于文件來說,可以通過多種方式:

  1. 如果有對應(yīng)的文件存儲位置修改功能,可以通過修改全局文件存儲位置實(shí)現(xiàn)。
  2. 如果有反向代理,可以通過修改反向代理的配置來通過重定向?實(shí)現(xiàn)。
  3. 對時間敏感的文件讀寫,可以根據(jù)日期和時間建立文件夾。
  4. 如果有七層的負(fù)載均衡或者 CDN 可以通過路徑匹配來實(shí)現(xiàn)、

在“薩瓦迪卡”的例子里,我們通過 CDN 來實(shí)現(xiàn)了文件的隔離。將文件存儲在 AWS S3 上,并且用 CloudFront 作為 CDN。用路徑匹配的形式讓請求通過訪問 S3 而不是虛擬機(jī)實(shí)例來降低虛擬機(jī)的 IO 請求,再加上 CDN 的緩存,這就可以大大減少虛擬機(jī)實(shí)例的負(fù)擔(dān),也提升了用戶的體驗(yàn)。最終的架構(gòu)如下圖所示:

有狀態(tài)部分的整體分離

在采用 CDN 的時候請注意以下幾點(diǎn):

  1. 最開始的時候取消默認(rèn)緩存設(shè)置。因?yàn)閷τ谖粗膽?yīng)用來說,各個訪問點(diǎn)的內(nèi)容更新頻率并不清楚。這個階段主要是為了收集應(yīng)用的訪問數(shù)據(jù)。
  2. 靈活利用緩存的過期時間(Expire time)強(qiáng)制過期(Invalidate)功能,來控制新舊內(nèi)容的讀寫。
  3. 注意 DNS 的 TTL 時間,并可以通過設(shè)置多級域名和 Failover 功能進(jìn)行 0 停機(jī)切換或者藍(lán)綠部署。例如當(dāng)前總?cè)肟谟蛎苯釉L問 ELB,可以增加一個 Failover 備份訪問點(diǎn)訪問 CDN,然后通過 CDN 訪問 ELB。如果沒有特別的 WAF 配置。
  4. 出問題了多檢查 HTTP 請求頭和響應(yīng)頭的信息,一般都內(nèi)藏玄機(jī)。

完成了應(yīng)用的狀態(tài)隔離,我們就可以開始進(jìn)行水平擴(kuò)展了。

第二步:牲畜化(Cattlize)應(yīng)用實(shí)例

在“薩瓦迪卡”的例子里,它的整個架構(gòu)就是一個寵物式(Pets)的架構(gòu):獨(dú)一無二不可復(fù)制。但是帶來的問題就是當(dāng)寵物式架構(gòu)出了問題之后,沒有相對應(yīng)的替代方案。而牲畜式(Cattles)的架構(gòu),就是可以進(jìn)行復(fù)制的容錯架構(gòu),其中任何一個出問題了,都有相應(yīng)的備胎(alternatives),正所謂“有備無患”。

所以,可以認(rèn)為,高可用架構(gòu)的本質(zhì)就是把寵物變成牲畜的問題。

如果你的應(yīng)用是以函數(shù)式的方式進(jìn)行編寫的,那么它本身就自帶水平擴(kuò)展功能。函數(shù)本身是沒有狀態(tài)的,它只是對數(shù)據(jù)的加工。這樣的應(yīng)用僅僅是把用戶手上的數(shù)據(jù),經(jīng)過一系列的轉(zhuǎn)化,存儲在了服務(wù)端。

如果你的應(yīng)用不是以函數(shù)式的方式進(jìn)行編寫的,你也不想修改應(yīng)用,除了做狀態(tài)隔離以外,你需要將應(yīng)用程序?qū)嵗D(zhuǎn)變?yōu)榭蓮?fù)制的模式:

首先,你最好有一個備份的網(wǎng)絡(luò)可用域。可以理解為兩個機(jī)房,當(dāng)一個機(jī)房出問題了,你還有另外一個機(jī)房。

其次,你要通過虛擬機(jī)鏡像來對應(yīng)用實(shí)例進(jìn)行復(fù)制。

最后,你要采取藍(lán)綠部署或者金絲雀發(fā)布的形式來控制用戶的訪問以達(dá)到0停機(jī)的目的。

所以,我們在“薩瓦迪卡”上做了如下的規(guī)劃:

  1. 構(gòu)建另外一個可用區(qū)的網(wǎng)絡(luò)。
  2. 通過虛擬機(jī)鏡像復(fù)制虛擬機(jī)實(shí)例。
  3. 通過負(fù)載均衡來分配應(yīng)用的訪問。
  4. 通過 RDS 集群節(jié)點(diǎn)做統(tǒng)一的訪問入口,負(fù)載均衡和高可用由 RDS 自己管理。

于是,我們就形成了如下圖所示的最終方案:

最終架構(gòu)

特別需要注意的是,在構(gòu)建虛擬機(jī)鏡像的時候,你需要對虛擬機(jī)的操作系統(tǒng)進(jìn)行升級并安裝 APM 代理程序(APM agent),在升級的時候,為了避免不必要的停機(jī)時間,我們采用了如下流程:

  1. 構(gòu)建一個當(dāng)前虛擬機(jī)的鏡像。
  2. 采用新鏡像啟動一個虛擬機(jī)實(shí)例,這個實(shí)例的配置需要和現(xiàn)有虛擬機(jī)實(shí)例一致。
  3. 新的虛擬機(jī)實(shí)例啟動后并不加入到負(fù)載均衡里。
  4. 在新啟動的虛擬機(jī)實(shí)例一臺上進(jìn)行更新和升級。
  5. 把升級后的虛擬機(jī)加到負(fù)載均衡里。
  6. 檢查新加入負(fù)載均衡的虛擬機(jī)正常工作后,在線升級老的虛擬機(jī)。
  7. 完成之后,構(gòu)建新的虛擬機(jī)鏡像,并作為可復(fù)制鏡像。

完成了以上的步驟之后,我們就不需要晚上在低訪問量的時候進(jìn)行操作了,白天可以通過創(chuàng)建新的虛擬機(jī)實(shí)例來完成相應(yīng)的測試,并通過移入移出負(fù)載均衡的方式進(jìn)行發(fā)布。

接下來,就要讓這個架構(gòu)可自動化伸縮了。

第三步,自動化水平擴(kuò)展(AutoScaling)

當(dāng)我們完成了前面兩步,可以基本認(rèn)為滿足了高可用的條件。但是,由于是靜態(tài)的人為操作,仍然需要人工值守來解決突發(fā)狀況,這顯然不是一個長久之計(jì),我們要使架構(gòu)可以處理突發(fā)狀況,這也是云計(jì)算平臺的優(yōu)勢所在。

首先,我們需要規(guī)劃冗余資源。

冗余值是為了應(yīng)對超過過去峰值的資源使用率,而設(shè)計(jì)的容量,它是最大值減去保留使用率(Reserve Utilization)所剩下的值。以我的經(jīng)驗(yàn),在沒有特別事件出現(xiàn)的情況下。保留使用率一般取均值的 3 倍,或者峰值的 2 倍 中較高的那個值。舉個例子:如果我的內(nèi)存使用平均值在 2 GiB,峰值在 4 GiB。那么我的保留使用率是 8 GiB。

冗余值的目的不是為了應(yīng)對突發(fā)狀況,而是給突發(fā)狀況保留一些相應(yīng)時間。當(dāng)應(yīng)用有趨于不正常的趨勢的時候,我們可以由足夠的時間來為下一個特殊階段進(jìn)行處理。

然后,構(gòu)造監(jiān)控告警。

我們也可以用以上幾個值來設(shè)置資源監(jiān)控告警來通知我們,告警方案一般為“兩線三區(qū)”:兩線分別為告警線和人工干預(yù)線,三區(qū)分別為:綠區(qū)(正常),黃線(警告),紅線(嚴(yán)重)。而每個線的設(shè)計(jì)方法也不同,一般有以下幾種:

  1. 按照最大資源的 60% 和 80% 設(shè)計(jì)告警線和人工干預(yù)線。60%以下為綠區(qū),60%-80% 為黃區(qū),80% 以上為紅區(qū)。剩下的為冗余。
  2. 按照邊際值的增幅的平方和立方設(shè)計(jì)告警線和人工干預(yù)線。邊際值平方以下增速為綠區(qū),邊際值立方以下,平方以上為黃區(qū),邊際值立方以上為紅區(qū)。
  3. 按照資源使用均值的 3 倍,或者峰值的 2 倍的較低值和較高值設(shè)計(jì)告警線和人工干預(yù)線。

在“薩瓦迪卡”里,我們采用了第一種方式進(jìn)行了設(shè)置。因?yàn)檫@個應(yīng)用并不會有突發(fā)的訪問狀況,所以我們采用了均值。

最后,制定和測試自動伸縮策略

有了以上的數(shù)據(jù)之后,我們就可以制定自動伸縮策略了。一般是在監(jiān)控處于“黃區(qū)”的時候開始出發(fā)自動伸縮策略。而由于自動伸縮會有一定的“熱身時間”(Warm-up Time),這個時間如果資源被用完,就會導(dǎo)致宕機(jī)。所以,我們需要更快的響應(yīng)。因此,制定自動伸縮策略的時候要采用“快增慢減”原則:即以兩倍到三倍的速度增加以滿足資源消耗,并以但倍速的方式進(jìn)行減少,不怕浪費(fèi),就怕影響用戶感知。

此外,請一定要通過前面所講的性能測試方案來測試自動伸縮策略,以確保策略是可用的。我以前碰到過一個例子:客戶想當(dāng)然的制定了自動伸縮策略,但從未測試過。導(dǎo)致了一次自動伸縮失效而引起的停機(jī)。

這個時候,應(yīng)用遷移到云上的第一步工作就完成了。我們通過使用云計(jì)算平臺的可靠性特性,首先先保證了應(yīng)用的穩(wěn)定運(yùn)行。接下來,我們要用云計(jì)算平臺的優(yōu)勢來逐步優(yōu)化云平臺上的應(yīng)用。

第二階段:引入 APM 并根據(jù) APM 數(shù)據(jù)進(jìn)行定向優(yōu)化

對于一個黑盒應(yīng)用,我們需要了解應(yīng)用的內(nèi)部性能狀況,除了自己編寫相應(yīng)的代碼以外,就是用 APM 平臺了。APM 平臺往往會提供一個低侵入的方案來獲取應(yīng)用和操作系統(tǒng)的性能數(shù)據(jù)。一般會采用代理(Agent)的形式運(yùn)行,并且通過網(wǎng)絡(luò)對外傳輸數(shù)據(jù),某種意義上說這是一種不安全的方式,但現(xiàn)代大多數(shù) APM 工具都提供了加密的方式來傳輸和壓縮數(shù)據(jù)。NewRelic 就是這個行業(yè)的佼佼者,它不光提供了低入侵的方案,還提供了更多的分析界面來幫助我們找到應(yīng)用的性能問題。效果如下所示:

NewRelic APM

在“薩瓦迪卡”里,我們就用了 NewRelic 來作為 APM 方案,它幫我們發(fā)現(xiàn)并度量了很多問題,例如緩慢的查詢,低性能的插件,不穩(wěn)定的資源使用等。無論是應(yīng)用本身還是架構(gòu)上的問題,以指導(dǎo)我們更好的進(jìn)行性能調(diào)優(yōu),并通過數(shù)據(jù)的對比來判斷效果。此外,我們可以結(jié)合 CDN 的統(tǒng)計(jì)數(shù)據(jù)來看哪些 URL 和資源最常被訪問,從而制定出更有效的性能優(yōu)化手段。

而一般的性能優(yōu)化,會采用以下四種基本的方式。但無論是哪一作用方式,一定要根據(jù)業(yè)務(wù)數(shù)據(jù)來設(shè)計(jì)緩存,要了解對應(yīng)訪問點(diǎn)的數(shù)據(jù)更新頻率和性能要求。

增加緩存

緩存往往是提升性能的第一選擇,主要原理是采用少量速度更高且較貴的資源替代部分速度較慢的資源,形成“短路訪問”:本來需要經(jīng)過四個環(huán)節(jié)才能獲取的數(shù)據(jù)通過兩個環(huán)節(jié)就可以獲取到了。而緩存一般是根據(jù) LRU 算法(Least Recently Used,即最近最久未使用)來實(shí)現(xiàn)的。

CDN 可以被看做是一種緩存,它通過網(wǎng)絡(luò)延遲和路由幫用戶找到訪問更加快速的邊緣服務(wù)器來加速。邊緣服務(wù)器上往往根據(jù) LRU 算法存儲了 源服務(wù)器(Origin Server)的一部分拷貝。

另外一種就是內(nèi)存數(shù)據(jù)庫或者 Key-Value 存儲,例如 Redis 或者 Memory Cache 這種方案是把數(shù)據(jù)通過一定的格式索引(最簡單的方式就是 HashMap)并存儲到內(nèi)存里來替代訪問。然而,這些服務(wù)的能力有限,并不能提供太多的復(fù)雜的操作。

動靜分離

由于 Web 應(yīng)用大多是讀寫,而不同的設(shè)備和內(nèi)容的訪問速度是不同的。對于低寫入,高讀取的資源。我們可以把它靜態(tài)化。例如 Hexo 和 Jekyll 這樣的工具,就可以把 Markdown 生成靜態(tài)的 HTML 文件。然后通過 S3 或者反向代理為用戶提供內(nèi)容。相較于 Wordpress 或者很多動態(tài)應(yīng)用,這樣的內(nèi)容不會占用 CPU 和內(nèi)存,僅僅占用文件系統(tǒng) IO。占用資源低,也較少會出錯。此外,靜態(tài)的內(nèi)容也更容易被緩存。

唯一的問題就是區(qū)分應(yīng)用架構(gòu)中的靜態(tài)部分和動態(tài)部分,并通過版本控制來對內(nèi)容進(jìn)行管理。必要的時候要采用 緩存的失效時間或者 HTTP 頭的緩存控制來進(jìn)行更新。

讀寫分離

如果把應(yīng)用程序看成一個大的 I/O 系統(tǒng)或者 讀/寫系統(tǒng)。我們要明白數(shù)據(jù)是如何寫入并如何讀出的,特別是針對于數(shù)據(jù)庫而言, 某些的操作都會帶來一定的鎖或者事務(wù)來解決臨界資源的互斥訪問問題,這種操作一定程度上也會影響性能。所以,我們?nèi)绻馨褦?shù)據(jù)庫的讀寫分開,會提升應(yīng)用的部分訪問性能。

例如在 AWS 中,Aurora 數(shù)據(jù)引擎可以為數(shù)據(jù)庫創(chuàng)建集群和只讀分片來做讀寫分離。

另外一種情況下就是用搜索引擎(例如 ElasticSearch)來替代數(shù)據(jù)庫查詢,性能會高很多。只是要注意數(shù)據(jù)的更新頻率和索引時間。

異步訪問

在大型企業(yè)級應(yīng)用中,通常會應(yīng)用事務(wù)(Transaction)保證業(yè)務(wù)的完整性和一致性,代價則是系統(tǒng)資源的事務(wù)內(nèi)占用。如果有很多的事務(wù)占用著資源,則會造成時間上的資源使用浪費(fèi)。更加現(xiàn)代的做法是把一個較長的事務(wù)拆分成多個較短的事務(wù),通過異步的方式進(jìn)行“兩步提交”來保證最終一致性。優(yōu)點(diǎn)是釋放了很多資源,缺點(diǎn)則是需要更多的確認(rèn)操作來保證最終一致性。

通過以上的四種方式,我們還可以為不同業(yè)務(wù)組合成不同的分離方案。并通過 AFK 擴(kuò)展三角的 Y 軸按能力將應(yīng)用的關(guān)注點(diǎn)進(jìn)行拆分,采用不同的技術(shù)棧和服務(wù)來實(shí)現(xiàn)并優(yōu)化性能。例如變成微服務(wù)的架構(gòu)的應(yīng)用。或者當(dāng)數(shù)據(jù)庫達(dá)到瓶頸之后通過 Z 軸進(jìn)行拆表拆庫在分?jǐn)倲?shù)據(jù)庫的負(fù)載的情況下保證一致性。

第三階段:構(gòu)建應(yīng)用端的持續(xù)部署

以上只是進(jìn)行了基礎(chǔ)設(shè)施方面的改造,應(yīng)用的性能和穩(wěn)定性得到了一定程度提升。然而,我們?nèi)匀徊捎貌糠秩斯さ牟僮鱽磉M(jìn)行應(yīng)用和基礎(chǔ)設(shè)施的變更。以“薩瓦迪卡”為例,如果我們需要更新應(yīng)用,為了減少停機(jī)時間,則需要執(zhí)行下面的步驟:

  1. 為當(dāng)前生產(chǎn)環(huán)境虛擬機(jī)創(chuàng)建鏡像。
  2. 采用新創(chuàng)建的新鏡像創(chuàng)建虛擬機(jī)。
  3. 在新創(chuàng)建的虛擬機(jī)上進(jìn)行更新。
  4. 更新后進(jìn)行測試,測試完畢后創(chuàng)建新的更新鏡像。
  5. 采用更新后的鏡像進(jìn)行自動水平擴(kuò)展。
  6. 替換負(fù)載均衡里的新老虛擬機(jī)。
  7. 終止已經(jīng)下線的虛擬機(jī)實(shí)例。

然而,這些操作會帶來人為因素的風(fēng)險,可能會帶來一些數(shù)據(jù)丟失的情況。而且,浪費(fèi)了人工去做云計(jì)算環(huán)境的部署,前后時間可長達(dá) 4 個小時。

通過之前的兩個階段,我們已經(jīng)把一個非分布式的應(yīng)用變成了簡單的分布式應(yīng)用。而面對分布式應(yīng)用的架構(gòu)復(fù)雜性,人力處理肯定是低效的。我們需要采用自動化的方式完成應(yīng)用生命周期和基礎(chǔ)設(shè)施生命周期的完整管理。持續(xù)部署流水線就是這樣一種實(shí)踐,通過把流程固化成自動化的腳本和操作避免了人工干預(yù)的風(fēng)險,從而構(gòu)建出了一個發(fā)布軟件的可靠流程。

在實(shí)踐中,我們往往采用持續(xù)集成服務(wù)器(例如 Jenkins),搭配云計(jì)算平臺的資源描述和編排服務(wù)(例如 CloudFormation)和一些腳本和模板管理工具來完成這一系列操作。在這之前,我們需要對應(yīng)用程序的不同部分進(jìn)行封裝。

通過“三段封裝”來規(guī)劃應(yīng)用結(jié)構(gòu)

第一段:基礎(chǔ)設(shè)施封裝

通過基礎(chǔ)設(shè)施即代碼技術(shù)構(gòu)建出一個應(yīng)用程序的平臺,這個平臺可以做到隔離應(yīng)用且對開發(fā)者透明。例如:Kubernetes 或者 AWS CloudFormation。前者可以為開發(fā)者提供一個簡單的應(yīng)用部署平臺,并很好的支持了很多高可用的特性。后者可以用來配置包括網(wǎng)絡(luò)在內(nèi)的所有 AWS 資源。

這里需要注意的是要根據(jù)基礎(chǔ)設(shè)施的變更頻率對基礎(chǔ)設(shè)施實(shí)施分層管理,將經(jīng)常變動的部分獨(dú)立成一個風(fēng)險最小的變更單元,避免和其它部分相互影響。

第二段:應(yīng)用封裝

通過構(gòu)建持續(xù)交付流水線構(gòu)建出應(yīng)用鏡像或者虛擬機(jī)鏡像,要做到快速復(fù)制以實(shí)現(xiàn)水平擴(kuò)展。例如 Docker 鏡像或者用 Packer 構(gòu)建出 AMI。

這里需要注意的是構(gòu)建鏡像的時候一定要考慮無狀態(tài)特性,每個鏡像被創(chuàng)建后所展現(xiàn)出來的最終效果和操作都是冪等的。

第三段:數(shù)據(jù)封裝

通過數(shù)據(jù)全量+增量的備份把數(shù)據(jù)庫或者文件存儲在更穩(wěn)妥的地方,并修改訪問方式。例如:采用 S3 或者 RDS 來存儲。

這里需要注意的是如果你沒有用 RDS 等高可靠的數(shù)據(jù)存儲服務(wù),就要要定時對數(shù)據(jù)進(jìn)行備份恢復(fù)測試,避免需要恢復(fù)數(shù)據(jù)的時候備份不起作用。備份策略可以按照全量 + 增量的方式進(jìn)行,具體的方式可以參考不同數(shù)據(jù)庫的方案。

完成了三段封裝后,我們需要為其構(gòu)造一個可靠的生命周期管理流程:構(gòu)建持續(xù)部署流水線。

構(gòu)建持續(xù)部署流水線

首先,要把上述的內(nèi)容納入版本控制。二進(jìn)制的內(nèi)容就進(jìn)行版本編號存儲,且不可修改和刪除,只能新增。

持續(xù)集成服務(wù)器本質(zhì)上是一個自動化的任務(wù)調(diào)度和執(zhí)行管理程序,它包含兩個部分:任務(wù)調(diào)度任務(wù)執(zhí)行

而任務(wù)調(diào)度包含主動和被動兩個模式:

主動模式:通過定時機(jī)制進(jìn)行掃描,當(dāng)發(fā)現(xiàn)有變動之后觸發(fā)任務(wù)的執(zhí)行。

被動模式:通過類似于 web-hook 被動任務(wù)觸發(fā),或者由上一個任務(wù)進(jìn)行觸發(fā)。

任務(wù)執(zhí)行則需要做到冪等性和線程隔離,確保每次的執(zhí)行環(huán)境和結(jié)果都一致。我們可以用一個任務(wù)的執(zhí)行結(jié)果成為下一個執(zhí)行的輸出。這樣,我們通過主動掃描代碼改動或者提交任務(wù)觸發(fā)的方式,把測試,構(gòu)建和打包的工作串聯(lián)起來,就構(gòu)成了一條持續(xù)交付流水線。大概會經(jīng)歷以下幾個步驟:

  1. 代碼提交
  2. 運(yùn)行自動化功能測試和靜態(tài)檢查
  3. 構(gòu)建
  4. 部署至測試環(huán)境
  5. 運(yùn)行自動化驗(yàn)收測試
  6. 發(fā)布

在以上的步驟里,除了第一步和最后一步,所有的中間步驟都是要自動化的。

這里需要注意的是:我們需要把部署和發(fā)布解耦。發(fā)布(Release)和 部署(Deploy)是兩個不同但又相關(guān)的動作,發(fā)布是一個業(yè)務(wù)操作,表示用戶可以接觸到最新版的應(yīng)用系統(tǒng)。 而部署是一個技術(shù)操作,表示應(yīng)用運(yùn)行在某一環(huán)境上。

Jenkins 里,我們可以采用 Job(任務(wù))的方式來執(zhí)行任務(wù),由代碼庫觸發(fā)測試,測試完成后觸發(fā)構(gòu)建、部署和發(fā)布。

為了減少系統(tǒng)的停機(jī)時間,我們也需要使用一些零停機(jī)部署策略,例如”藍(lán)綠部署“和”金絲雀發(fā)布“。

藍(lán)綠部署

藍(lán)綠部署的做法是同時生成兩個相同的生產(chǎn)環(huán)境版本,一個叫做”藍(lán)環(huán)境“,一個叫做”綠環(huán)境“。用戶當(dāng)前只能訪問其中一個環(huán)境,讓另外一個環(huán)境進(jìn)行部署。待到部署完成并通過檢查之后,再切換至部署好的環(huán)境。如果部署失敗,則把用戶流量切換到原先的環(huán)境就算是做到了快速回滾。某些云計(jì)算平臺內(nèi)置 了這樣的部署策略,可以幫助快速做到這一點(diǎn)。我們也可以通過更新內(nèi)部 DNS 記錄或者 Nginx 配置做到這一點(diǎn)。如果你的變更中包含數(shù)據(jù)庫變更,則需要額外的數(shù)據(jù)庫遷移策略和切換策略,也會花費(fèi)額外的時間。

金絲雀發(fā)布

金絲雀發(fā)布可以看做是 藍(lán)綠發(fā)布的一種演變形式,相比藍(lán)綠發(fā)布來說更加靈活。我們可以通過路由權(quán)重讓一小部分用戶嘗試新的版本。就像是在煤礦坑道里的金絲雀那樣,很快就能發(fā)現(xiàn)生產(chǎn)中的問題,并限制問題的影響范圍。

我們還可以基于此做 A/B 測試來度量更新的使用率。但這需要你的基礎(chǔ)設(shè)施支持“帶權(quán)流量分配”和“ Session 持久化”。前者是為了更靈活的切分流量,后者則是避免同一個用戶訪問不同版本應(yīng)用帶來的不一致性。

基礎(chǔ)設(shè)施變更流水線

上述描述的是應(yīng)用程序的生命周期,我們還需要構(gòu)造基礎(chǔ)設(shè)施的生命周期。我們也可以如法炮制同樣的流水線來進(jìn)行基礎(chǔ)設(shè)施變更。這樣我們就可以避免人工調(diào)整基礎(chǔ)設(shè)施帶來的各種隱患。我們需要把不同的備份方案和測試方案增加進(jìn)去以確保我們的基礎(chǔ)設(shè)施的穩(wěn)定性和反脆弱性,很多工具可能需要我們自己來編寫。

這里需要注意的是一定要盡可能避免人工干預(yù)生產(chǎn)環(huán)境帶來的風(fēng)險,盡可能通過流水線來對基礎(chǔ)設(shè)施進(jìn)行變更。

最后

完成了以上三個階段,我們才可以說基本上完成了一個應(yīng)用程序遷移到云計(jì)算平臺上的基礎(chǔ)步驟。如果你的應(yīng)用遷移到了云平臺上并做到了以上的三個階段,才算是及格。

云計(jì)算所帶來的改變并不僅僅是可靠的廉價租賃式資源,還會改變組織的工作方式。特別是 DevOps 運(yùn)動的興起又為 CloudNative 的產(chǎn)品研發(fā)運(yùn)營增加了新的助燃劑。關(guān)于構(gòu)建 DevOps 團(tuán)隊(duì)更多的內(nèi)容和套路,請參見我的 GitChat 達(dá)人課:“DevOps 轉(zhuǎn)型實(shí)戰(zhàn)”。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,978評論 2 374

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