本文是我在 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)化,以提升可用性和性能。同時(shí)俊性,這篇文章也總結(jié)了應(yīng)用遷移到云上的套路。
設(shè)計(jì)云計(jì)算平臺(tái)遷移計(jì)劃和方案
將應(yīng)用程序遷移到云計(jì)算平臺(tái)上主要的目的是把自行構(gòu)建的高風(fēng)險(xiǎn)高成本應(yīng)用以及組件替換為云計(jì)算平臺(tái)上的高可靠性低成本組件/服務(wù)描扯。
應(yīng)用架構(gòu)的遷移有兩種方案:
一種是整體一次性遷移定页,即重新實(shí)現(xiàn)一個(gè)架構(gòu)并完成部署,然后通過金絲雀發(fā)布或者藍(lán)綠發(fā)布切換绽诚。這種方式的好處?是簡單典徊,直接,有效恩够,一開始就能按照最佳實(shí)踐構(gòu)建應(yīng)用架構(gòu)卒落。而且對于現(xiàn)有系統(tǒng)來說影響不大。但如果方案沒設(shè)計(jì)好蜂桶,容易造成高級別的風(fēng)險(xiǎn)儡毕,所以應(yīng)當(dāng)進(jìn)行大量的測試以確保可靠性屎飘。
另一種是持續(xù)部分遷移妥曲,每次引入一點(diǎn)風(fēng)險(xiǎn),保證風(fēng)險(xiǎn)可控钦购,但缺點(diǎn)就是優(yōu)化步驟較多檐盟。雖然持續(xù)部分遷移步驟多,但是總體時(shí)間并不一定會(huì)比整體遷移更高押桃。
注意:由于自動(dòng)化基礎(chǔ)設(shè)施和架構(gòu)設(shè)計(jì)會(huì)帶來一些副作用葵萎,特別是配置間的耦合。因此唱凯,對于生產(chǎn)環(huán)境的直接優(yōu)化要慎用自動(dòng)化羡忘。如果一定要用,請務(wù)必在測試環(huán)境上做好測試磕昼。但如果你能做到自動(dòng)化并且有完好的測試卷雕,不如直接做整體一次性遷移方案得了。
一般說來票从,一個(gè)完整的云平臺(tái)遷移方案會(huì)分為以下三大階段:
第一階段:構(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 的工作模式。
這三個(gè)階段是大的順序魁蒜,而每個(gè)大的階段里又會(huì)相互摻雜一些其它階段的內(nèi)容囊扳。但無論什么樣的遷移方案,一定要通過度量進(jìn)行風(fēng)險(xiǎn)/收益比排序兜看,最先完成代價(jià)最小锥咸,收益最大的內(nèi)容。
第一階段:構(gòu)建高可用架構(gòu)
我們之前說過细移,一個(gè)應(yīng)用架構(gòu)的第一追求就是業(yè)務(wù)的連續(xù)性和抗風(fēng)險(xiǎn)能力她君。一個(gè)高可用的架構(gòu)能夠在你的應(yīng)用面對壓力的時(shí)候從容不迫。因?yàn)槿绻Y源滿負(fù)荷運(yùn)轉(zhuǎn)葫哗,新的請求會(huì)因?yàn)闆]有可用資源而導(dǎo)致排隊(duì)缔刹。這是常見的停機(jī)或者性能降低的原因。這就是 AFK 擴(kuò)展矩陣常說的 X 軸擴(kuò)展:通過復(fù)制自己擴(kuò)展資源從而達(dá)到降低排隊(duì)等待的時(shí)間劣针。 此外校镐,水平擴(kuò)展出來的機(jī)器同樣也是一個(gè)預(yù)留資源,能夠提高應(yīng)用的可用性捺典。應(yīng)用架構(gòu)不僅僅是應(yīng)用程序的事情鸟廓,也包含著資源的分配,二者是相輔相成的襟己。
一般會(huì)經(jīng)歷如下幾步:
第一步引谜,有狀態(tài)和無狀態(tài)分離
第二步,牲畜化(Cattlize)應(yīng)用實(shí)例
第三步擎浴,自動(dòng)化水平擴(kuò)展(AutoScaling)
第一步:有狀態(tài)和無狀態(tài)分離
先回顧一下當(dāng)前應(yīng)用的架構(gòu) :
狀態(tài)分離的目標(biāo)是把有狀態(tài)的組件和無狀態(tài)的組件分開员咽,以便在做復(fù)制的時(shí)候降低不一致性。最簡單的判定辦法是:如果復(fù)制當(dāng)前的虛擬資源贮预,并通過負(fù)載均衡隨機(jī)分配請求訪問贝室,哪些部分會(huì)造成不一致。
常見的有狀態(tài)內(nèi)容比如數(shù)據(jù)庫仿吞,上傳的文件滑频。所以,我們要把它們獨(dú)立出來唤冈。在“薩瓦迪卡”的例子中峡迷,我們首先把數(shù)據(jù)庫獨(dú)立了出來。如下圖所示:
在這個(gè)過程中你虹,我們采用 RDS 而不是另外一個(gè) EC2 上構(gòu)建一套 MySQL 來完成數(shù)據(jù)庫的分離绘搞。最主要的原因就是 RDS 提供了更好的可用性和數(shù)據(jù)庫維護(hù)支持枣申,例如自動(dòng)備份,更多的監(jiān)控指標(biāo)看杭,更自動(dòng)的數(shù)據(jù)庫遷移和維護(hù)窗口等。我們采用 Aurora 引擎的 MySQL 模式挟伙,這可以將數(shù)據(jù)庫做成一個(gè)集群并讓另外一個(gè)只讀分片楼雹,降低數(shù)據(jù)庫的負(fù)擔(dān)。
在分離數(shù)據(jù)庫的時(shí)候尖阔,要注意以下幾點(diǎn):
- 數(shù)據(jù)庫分離的性能基線就是在同樣的負(fù)載測試下贮缅,不能夠比沒分離之前更差。
- 數(shù)據(jù)庫的網(wǎng)絡(luò)建立在一個(gè)私有的子網(wǎng)中介却,除了應(yīng)用子網(wǎng)內(nèi)的 IP 不能訪問數(shù)據(jù)庫谴供,從而提高安全性。
- 構(gòu)建一個(gè)私有域名來訪問數(shù)據(jù)庫齿坷,這樣可以固定應(yīng)用的內(nèi)部配置桂肌,減少對配置的修改。同時(shí)也給外部切換數(shù)據(jù)庫主備等留下了更靈活的空間永淌。
- 注意對原有數(shù)據(jù)庫 MySQL 配置信息的復(fù)制崎场,這會(huì)導(dǎo)致很大程度上的性能差異。
- 對于數(shù)據(jù)較大的數(shù)據(jù)庫啟動(dòng)而言遂蛀,會(huì)有一個(gè)幾分鐘的熱身(Warm up)時(shí)間谭跨,這會(huì)導(dǎo)致性能下降。所以李滴,做切換的時(shí)候提前啟動(dòng)數(shù)據(jù)庫以做好準(zhǔn)備螃宙。
- 不要用默認(rèn)的 root 賬戶作為應(yīng)用的訪問賬戶。
- 由于 RDS 可以在不影響數(shù)據(jù)完整性和一致性的情況下降低使用配置所坯,在最開始的時(shí)候采用較高的配置谆扎。隨著優(yōu)化的不斷進(jìn)行,可以采用維護(hù)時(shí)間窗口(Maintenance Time Window)在低流量時(shí)段對 RDS 實(shí)例的配置進(jìn)行降級芹助,以節(jié)約成本燕酷。
完成了數(shù)據(jù)庫的隔離,我們就可以依法炮制文件的隔離了周瞎。最簡單有效的方案是把文件存儲(chǔ)在對象存儲(chǔ)服務(wù)中苗缩。AWS S3 就是這樣一種服務(wù)。避免自己構(gòu)建共享文件系統(tǒng)或者共享存儲(chǔ)設(shè)備声诸。
文件相較于數(shù)據(jù)庫來說酱讶,占用的內(nèi)存資源和 CPU 資源較少,大部分的處理為 IO 處理彼乌,只要網(wǎng)絡(luò)和設(shè)備的 IOPS 足夠泻肯。一般不會(huì)出現(xiàn)大的問題渊迁。
為了降低文件隔離帶來的問題,在遷移文件的時(shí)候盡量保證文件目錄結(jié)構(gòu)不變灶挟,只改變文件訪問根(root)的位置琉朽。對于文件來說,可以通過多種方式:
- 如果有對應(yīng)的文件存儲(chǔ)位置修改功能稚铣,可以通過修改全局文件存儲(chǔ)位置實(shí)現(xiàn)箱叁。
- 如果有反向代理,可以通過修改反向代理的配置來通過重定向?實(shí)現(xiàn)惕医。
- 對時(shí)間敏感的文件讀寫耕漱,可以根據(jù)日期和時(shí)間建立文件夾。
- 如果有七層的負(fù)載均衡或者 CDN 可以通過路徑匹配來實(shí)現(xiàn)抬伺、
在“薩瓦迪卡”的例子里螟够,我們通過 CDN 來實(shí)現(xiàn)了文件的隔離。將文件存儲(chǔ)在 AWS S3 上峡钓,并且用 CloudFront 作為 CDN妓笙。用路徑匹配的形式讓請求通過訪問 S3 而不是虛擬機(jī)實(shí)例來降低虛擬機(jī)的 IO 請求,再加上 CDN 的緩存能岩,這就可以大大減少虛擬機(jī)實(shí)例的負(fù)擔(dān)给郊,也提升了用戶的體驗(yàn)。最終的架構(gòu)如下圖所示:
在采用 CDN 的時(shí)候請注意以下幾點(diǎn):
- 最開始的時(shí)候取消默認(rèn)緩存設(shè)置捧灰。因?yàn)閷τ谖粗膽?yīng)用來說淆九,各個(gè)訪問點(diǎn)的內(nèi)容更新頻率并不清楚。這個(gè)階段主要是為了收集應(yīng)用的訪問數(shù)據(jù)毛俏。
- 靈活利用緩存的過期時(shí)間(Expire time)和強(qiáng)制過期(Invalidate)功能炭庙,來控制新舊內(nèi)容的讀寫。
- 注意 DNS 的 TTL 時(shí)間煌寇,并可以通過設(shè)置多級域名和 Failover 功能進(jìn)行 0 停機(jī)切換或者藍(lán)綠部署焕蹄。例如當(dāng)前總?cè)肟谟蛎苯釉L問 ELB,可以增加一個(gè) Failover 備份訪問點(diǎn)訪問 CDN阀溶,然后通過 CDN 訪問 ELB腻脏。如果沒有特別的 WAF 配置。
- 出問題了多檢查 HTTP 請求頭和響應(yīng)頭的信息银锻,一般都內(nèi)藏玄機(jī)永品。
完成了應(yīng)用的狀態(tài)隔離,我們就可以開始進(jìn)行水平擴(kuò)展了击纬。
第二步:牲畜化(Cattlize)應(yīng)用實(shí)例
在“薩瓦迪卡”的例子里鼎姐,它的整個(gè)架構(gòu)就是一個(gè)寵物式(Pets)的架構(gòu):獨(dú)一無二不可復(fù)制。但是帶來的問題就是當(dāng)寵物式架構(gòu)出了問題之后,沒有相對應(yīng)的替代方案炕桨。而牲畜式(Cattles)的架構(gòu)饭尝,就是可以進(jìn)行復(fù)制的容錯(cuò)架構(gòu),其中任何一個(gè)出問題了献宫,都有相應(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)化原在,存儲(chǔ)在了服務(wù)端友扰。
如果你的應(yīng)用不是以函數(shù)式的方式進(jìn)行編寫的,你也不想修改應(yīng)用庶柿,除了做狀態(tài)隔離以外村怪,你需要將應(yīng)用程序?qū)嵗D(zhuǎn)變?yōu)榭蓮?fù)制的模式:
首先,你最好有一個(gè)備份的網(wǎng)絡(luò)可用域浮庐∩醺海可以理解為兩個(gè)機(jī)房,當(dāng)一個(gè)機(jī)房出問題了审残,你還有另外一個(gè)機(jī)房梭域。
其次,你要通過虛擬機(jī)鏡像來對應(yīng)用實(shí)例進(jìn)行復(fù)制搅轿。
最后病涨,你要采取藍(lán)綠部署或者金絲雀發(fā)布的形式來控制用戶的訪問以達(dá)到0停機(jī)的目的。
所以璧坟,我們在“薩瓦迪卡”上做了如下的規(guī)劃:
- 構(gòu)建另外一個(gè)可用區(qū)的網(wǎng)絡(luò)既穆。
- 通過虛擬機(jī)鏡像復(fù)制虛擬機(jī)實(shí)例。
- 通過負(fù)載均衡來分配應(yīng)用的訪問雀鹃。
- 通過 RDS 集群節(jié)點(diǎn)做統(tǒng)一的訪問入口幻工,負(fù)載均衡和高可用由 RDS 自己管理。
于是黎茎,我們就形成了如下圖所示的最終方案:
特別需要注意的是会钝,在構(gòu)建虛擬機(jī)鏡像的時(shí)候,你需要對虛擬機(jī)的操作系統(tǒng)進(jìn)行升級并安裝 APM 代理程序(APM agent),在升級的時(shí)候迁酸,為了避免不必要的停機(jī)時(shí)間先鱼,我們采用了如下流程:
- 構(gòu)建一個(gè)當(dāng)前虛擬機(jī)的鏡像。
- 采用新鏡像啟動(dòng)一個(gè)虛擬機(jī)實(shí)例奸鬓,這個(gè)實(shí)例的配置需要和現(xiàn)有虛擬機(jī)實(shí)例一致焙畔。
- 新的虛擬機(jī)實(shí)例啟動(dòng)后并不加入到負(fù)載均衡里。
- 在新啟動(dòng)的虛擬機(jī)實(shí)例一臺(tái)上進(jìn)行更新和升級串远。
- 把升級后的虛擬機(jī)加到負(fù)載均衡里宏多。
- 檢查新加入負(fù)載均衡的虛擬機(jī)正常工作后,在線升級老的虛擬機(jī)澡罚。
- 完成之后伸但,構(gòu)建新的虛擬機(jī)鏡像,并作為可復(fù)制鏡像留搔。
完成了以上的步驟之后更胖,我們就不需要晚上在低訪問量的時(shí)候進(jìn)行操作了,白天可以通過創(chuàng)建新的虛擬機(jī)實(shí)例來完成相應(yīng)的測試隔显,并通過移入移出負(fù)載均衡的方式進(jìn)行發(fā)布却妨。
接下來,就要讓這個(gè)架構(gòu)可自動(dòng)化伸縮了括眠。
第三步彪标,自動(dòng)化水平擴(kuò)展(AutoScaling)
當(dāng)我們完成了前面兩步,可以基本認(rèn)為滿足了高可用的條件掷豺。但是捞烟,由于是靜態(tài)的人為操作,仍然需要人工值守來解決突發(fā)狀況当船,這顯然不是一個(gè)長久之計(jì)坷襟,我們要使架構(gòu)可以處理突發(fā)狀況,這也是云計(jì)算平臺(tái)的優(yōu)勢所在生年。
首先婴程,我們需要規(guī)劃冗余資源。
冗余值是為了應(yīng)對超過過去峰值的資源使用率抱婉,而設(shè)計(jì)的容量档叔,它是最大值減去保留使用率(Reserve Utilization)所剩下的值。以我的經(jīng)驗(yàn)蒸绩,在沒有特別事件出現(xiàn)的情況下衙四。保留使用率一般取均值的 3 倍,或者峰值的 2 倍 中較高的那個(gè)值患亿。舉個(gè)例子:如果我的內(nèi)存使用平均值在 2 GiB传蹈,峰值在 4 GiB押逼。那么我的保留使用率是 8 GiB。
冗余值的目的不是為了應(yīng)對突發(fā)狀況惦界,而是給突發(fā)狀況保留一些相應(yīng)時(shí)間挑格。當(dāng)應(yīng)用有趨于不正常的趨勢的時(shí)候,我們可以由足夠的時(shí)間來為下一個(gè)特殊階段進(jìn)行處理沾歪。
然后漂彤,構(gòu)造監(jiān)控告警。
我們也可以用以上幾個(gè)值來設(shè)置資源監(jiān)控告警來通知我們灾搏,告警方案一般為“兩線三區(qū)”:兩線分別為告警線和人工干預(yù)線挫望,三區(qū)分別為:綠區(qū)(正常),黃線(警告)狂窑,紅線(嚴(yán)重)媳板。而每個(gè)線的設(shè)計(jì)方法也不同,一般有以下幾種:
- 按照最大資源的 60% 和 80% 設(shè)計(jì)告警線和人工干預(yù)線泉哈。60%以下為綠區(qū)蛉幸,60%-80% 為黃區(qū),80% 以上為紅區(qū)旨巷。剩下的為冗余巨缘。
- 按照邊際值的增幅的平方和立方設(shè)計(jì)告警線和人工干預(yù)線添忘。邊際值平方以下增速為綠區(qū)采呐,邊際值立方以下,平方以上為黃區(qū)搁骑,邊際值立方以上為紅區(qū)斧吐。
- 按照資源使用均值的 3 倍,或者峰值的 2 倍的較低值和較高值設(shè)計(jì)告警線和人工干預(yù)線仲器。
在“薩瓦迪卡”里煤率,我們采用了第一種方式進(jìn)行了設(shè)置。因?yàn)檫@個(gè)應(yīng)用并不會(huì)有突發(fā)的訪問狀況乏冀,所以我們采用了均值蝶糯。
最后,制定和測試自動(dòng)伸縮策略
有了以上的數(shù)據(jù)之后辆沦,我們就可以制定自動(dòng)伸縮策略了昼捍。一般是在監(jiān)控處于“黃區(qū)”的時(shí)候開始出發(fā)自動(dòng)伸縮策略。而由于自動(dòng)伸縮會(huì)有一定的“熱身時(shí)間”(Warm-up Time)肢扯,這個(gè)時(shí)間如果資源被用完妒茬,就會(huì)導(dǎo)致宕機(jī)。所以蔚晨,我們需要更快的響應(yīng)乍钻。因此,制定自動(dòng)伸縮策略的時(shí)候要采用“快增慢減”原則:即以兩倍到三倍的速度增加以滿足資源消耗,并以但倍速的方式進(jìn)行減少银择,不怕浪費(fèi)多糠,就怕影響用戶感知。
此外欢摄,請一定要通過前面所講的性能測試方案來測試自動(dòng)伸縮策略熬丧,以確保策略是可用的。我以前碰到過一個(gè)例子:客戶想當(dāng)然的制定了自動(dòng)伸縮策略怀挠,但從未測試過析蝴。導(dǎo)致了一次自動(dòng)伸縮失效而引起的停機(jī)。
這個(gè)時(shí)候绿淋,應(yīng)用遷移到云上的第一步工作就完成了闷畸。我們通過使用云計(jì)算平臺(tái)的可靠性特性,首先先保證了應(yīng)用的穩(wěn)定運(yùn)行吞滞。接下來佑菩,我們要用云計(jì)算平臺(tái)的優(yōu)勢來逐步優(yōu)化云平臺(tái)上的應(yīng)用。
第二階段:引入 APM 并根據(jù) APM 數(shù)據(jù)進(jìn)行定向優(yōu)化
對于一個(gè)黑盒應(yīng)用裁赠,我們需要了解應(yīng)用的內(nèi)部性能狀況殿漠,除了自己編寫相應(yīng)的代碼以外,就是用 APM 平臺(tái)了佩捞。APM 平臺(tái)往往會(huì)提供一個(gè)低侵入的方案來獲取應(yīng)用和操作系統(tǒng)的性能數(shù)據(jù)绞幌。一般會(huì)采用代理(Agent)的形式運(yùn)行,并且通過網(wǎng)絡(luò)對外傳輸數(shù)據(jù)一忱,某種意義上說這是一種不安全的方式莲蜘,但現(xiàn)代大多數(shù) APM 工具都提供了加密的方式來傳輸和壓縮數(shù)據(jù)。NewRelic 就是這個(gè)行業(yè)的佼佼者帘营,它不光提供了低入侵的方案票渠,還提供了更多的分析界面來幫助我們找到應(yīng)用的性能問題。效果如下所示:
在“薩瓦迪卡”里芬迄,我們就用了 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)化,會(huì)采用以下四種基本的方式畏浆。但無論是哪一作用方式胆胰,一定要根據(jù)業(yè)務(wù)數(shù)據(jù)來設(shè)計(jì)緩存,要了解對應(yīng)訪問點(diǎn)的數(shù)據(jù)更新頻率和性能要求刻获。
增加緩存
緩存往往是提升性能的第一選擇蜀涨,主要原理是采用少量速度更高且較貴的資源替代部分速度較慢的資源,形成“短路訪問”:本來需要經(jīng)過四個(gè)環(huán)節(jié)才能獲取的數(shù)據(jù)通過兩個(gè)環(huán)節(jié)就可以獲取到了蝎毡。而緩存一般是根據(jù) LRU 算法(Least Recently Used厚柳,即最近最久未使用)來實(shí)現(xiàn)的。
CDN 可以被看做是一種緩存沐兵,它通過網(wǎng)絡(luò)延遲和路由幫用戶找到訪問更加快速的邊緣服務(wù)器來加速别垮。邊緣服務(wù)器上往往根據(jù) LRU 算法存儲(chǔ)了 源服務(wù)器(Origin Server)的一部分拷貝。
另外一種就是內(nèi)存數(shù)據(jù)庫或者 Key-Value 存儲(chǔ)扎谎,例如 Redis 或者 Memory Cache 這種方案是把數(shù)據(jù)通過一定的格式索引(最簡單的方式就是 HashMap)并存儲(chǔ)到內(nèi)存里來替代訪問碳想。然而,這些服務(wù)的能力有限毁靶,并不能提供太多的復(fù)雜的操作胧奔。
動(dòng)靜分離
由于 Web 應(yīng)用大多是讀寫,而不同的設(shè)備和內(nèi)容的訪問速度是不同的预吆。對于低寫入龙填,高讀取的資源。我們可以把它靜態(tài)化啡浊。例如 Hexo 和 Jekyll 這樣的工具觅够,就可以把 Markdown 生成靜態(tài)的 HTML 文件胶背。然后通過 S3 或者反向代理為用戶提供內(nèi)容巷嚣。相較于 Wordpress 或者很多動(dòng)態(tài)應(yīng)用,這樣的內(nèi)容不會(huì)占用 CPU 和內(nèi)存钳吟,僅僅占用文件系統(tǒng) IO廷粒。占用資源低,也較少會(huì)出錯(cuò)红且。此外坝茎,靜態(tài)的內(nèi)容也更容易被緩存。
唯一的問題就是區(qū)分應(yīng)用架構(gòu)中的靜態(tài)部分和動(dòng)態(tài)部分暇番,并通過版本控制來對內(nèi)容進(jìn)行管理嗤放。必要的時(shí)候要采用 緩存的失效時(shí)間或者 HTTP 頭的緩存控制來進(jìn)行更新。
讀寫分離
如果把應(yīng)用程序看成一個(gè)大的 I/O 系統(tǒng)或者 讀/寫系統(tǒng)壁酬。我們要明白數(shù)據(jù)是如何寫入并如何讀出的次酌,特別是針對于數(shù)據(jù)庫而言恨课, 某些的操作都會(huì)帶來一定的鎖或者事務(wù)來解決臨界資源的互斥訪問問題,這種操作一定程度上也會(huì)影響性能岳服。所以剂公,我們?nèi)绻馨褦?shù)據(jù)庫的讀寫分開,會(huì)提升應(yīng)用的部分訪問性能吊宋。
例如在 AWS 中纲辽,Aurora 數(shù)據(jù)引擎可以為數(shù)據(jù)庫創(chuàng)建集群和只讀分片來做讀寫分離。
另外一種情況下就是用搜索引擎(例如 ElasticSearch)來替代數(shù)據(jù)庫查詢璃搜,性能會(huì)高很多拖吼。只是要注意數(shù)據(jù)的更新頻率和索引時(shí)間。
異步訪問
在大型企業(yè)級應(yīng)用中这吻,通常會(huì)應(yīng)用事務(wù)(Transaction)保證業(yè)務(wù)的完整性和一致性绿贞,代價(jià)則是系統(tǒng)資源的事務(wù)內(nèi)占用。如果有很多的事務(wù)占用著資源橘原,則會(huì)造成時(shí)間上的資源使用浪費(fèi)籍铁。更加現(xiàn)代的做法是把一個(gè)較長的事務(wù)拆分成多個(gè)較短的事務(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ī)時(shí)間阀坏,則需要執(zhí)行下面的步驟:
- 為當(dāng)前生產(chǎn)環(huán)境虛擬機(jī)創(chuàng)建鏡像。
- 采用新創(chuàng)建的新鏡像創(chuàng)建虛擬機(jī)笆檀。
- 在新創(chuàng)建的虛擬機(jī)上進(jìn)行更新忌堂。
- 更新后進(jìn)行測試,測試完畢后創(chuàng)建新的更新鏡像酗洒。
- 采用更新后的鏡像進(jìn)行自動(dòng)水平擴(kuò)展士修。
- 替換負(fù)載均衡里的新老虛擬機(jī)妄迁。
- 終止已經(jīng)下線的虛擬機(jī)實(shí)例。
然而李命,這些操作會(huì)帶來人為因素的風(fēng)險(xiǎn)登淘,可能會(huì)帶來一些數(shù)據(jù)丟失的情況。而且封字,浪費(fèi)了人工去做云計(jì)算環(huán)境的部署黔州,前后時(shí)間可長達(dá) 4 個(gè)小時(shí)。
通過之前的兩個(gè)階段阔籽,我們已經(jīng)把一個(gè)非分布式的應(yīng)用變成了簡單的分布式應(yīng)用流妻。而面對分布式應(yīng)用的架構(gòu)復(fù)雜性,人力處理肯定是低效的笆制。我們需要采用自動(dòng)化的方式完成應(yīng)用生命周期和基礎(chǔ)設(shè)施生命周期的完整管理绅这。持續(xù)部署流水線就是這樣一種實(shí)踐,通過把流程固化成自動(dòng)化的腳本和操作避免了人工干預(yù)的風(fēng)險(xiǎn)在辆,從而構(gòu)建出了一個(gè)發(fā)布軟件的可靠流程证薇。
在實(shí)踐中,我們往往采用持續(xù)集成服務(wù)器(例如 Jenkins)匆篓,搭配云計(jì)算平臺(tái)的資源描述和編排服務(wù)(例如 CloudFormation)和一些腳本和模板管理工具來完成這一系列操作浑度。在這之前,我們需要對應(yīng)用程序的不同部分進(jìn)行封裝鸦概。
通過“三段封裝”來規(guī)劃應(yīng)用結(jié)構(gòu)
第一段:基礎(chǔ)設(shè)施封裝
通過基礎(chǔ)設(shè)施即代碼技術(shù)構(gòu)建出一個(gè)應(yīng)用程序的平臺(tái)箩张,這個(gè)平臺(tái)可以做到隔離應(yīng)用且對開發(fā)者透明。例如:Kubernetes 或者 AWS CloudFormation窗市。前者可以為開發(fā)者提供一個(gè)簡單的應(yīng)用部署平臺(tái)先慷,并很好的支持了很多高可用的特性。后者可以用來配置包括網(wǎng)絡(luò)在內(nèi)的所有 AWS 資源咨察。
這里需要注意的是要根據(jù)基礎(chǔ)設(shè)施的變更頻率對基礎(chǔ)設(shè)施實(shí)施分層管理论熙,將經(jīng)常變動(dòng)的部分獨(dú)立成一個(gè)風(fēng)險(xiǎn)最小的變更單元,避免和其它部分相互影響扎拣。
第二段:應(yīng)用封裝
通過構(gòu)建持續(xù)交付流水線構(gòu)建出應(yīng)用鏡像或者虛擬機(jī)鏡像赴肚,要做到快速復(fù)制以實(shí)現(xiàn)水平擴(kuò)展素跺。例如 Docker 鏡像或者用 Packer 構(gòu)建出 AMI二蓝。
這里需要注意的是構(gòu)建鏡像的時(shí)候一定要考慮無狀態(tài)特性,每個(gè)鏡像被創(chuàng)建后所展現(xiàn)出來的最終效果和操作都是冪等的指厌。
第三段:數(shù)據(jù)封裝
通過數(shù)據(jù)全量+增量的備份把數(shù)據(jù)庫或者文件存儲(chǔ)在更穩(wěn)妥的地方刊愚,并修改訪問方式。例如:采用 S3 或者 RDS 來存儲(chǔ)踩验。
這里需要注意的是如果你沒有用 RDS 等高可靠的數(shù)據(jù)存儲(chǔ)服務(wù)鸥诽,就要要定時(shí)對數(shù)據(jù)進(jìn)行備份恢復(fù)測試商玫,避免需要恢復(fù)數(shù)據(jù)的時(shí)候備份不起作用。備份策略可以按照全量 + 增量的方式進(jìn)行牡借,具體的方式可以參考不同數(shù)據(jù)庫的方案拳昌。
完成了三段封裝后,我們需要為其構(gòu)造一個(gè)可靠的生命周期管理流程:構(gòu)建持續(xù)部署流水線钠龙。
構(gòu)建持續(xù)部署流水線
首先炬藤,要把上述的內(nèi)容納入版本控制。二進(jìn)制的內(nèi)容就進(jìn)行版本編號存儲(chǔ)碴里,且不可修改和刪除沈矿,只能新增。
持續(xù)集成服務(wù)器本質(zhì)上是一個(gè)自動(dòng)化的任務(wù)調(diào)度和執(zhí)行管理程序咬腋,它包含兩個(gè)部分:任務(wù)調(diào)度和任務(wù)執(zhí)行羹膳。
而任務(wù)調(diào)度包含主動(dòng)和被動(dòng)兩個(gè)模式:
主動(dòng)模式:通過定時(shí)機(jī)制進(jìn)行掃描,當(dāng)發(fā)現(xiàn)有變動(dòng)之后觸發(fā)任務(wù)的執(zhí)行根竿。
被動(dòng)模式:通過類似于 web-hook 被動(dòng)任務(wù)觸發(fā)陵像,或者由上一個(gè)任務(wù)進(jìn)行觸發(fā)。
任務(wù)執(zhí)行則需要做到冪等性和線程隔離寇壳,確保每次的執(zhí)行環(huán)境和結(jié)果都一致蠢壹。我們可以用一個(gè)任務(wù)的執(zhí)行結(jié)果成為下一個(gè)執(zhí)行的輸出。這樣九巡,我們通過主動(dòng)掃描代碼改動(dòng)或者提交任務(wù)觸發(fā)的方式图贸,把測試,構(gòu)建和打包的工作串聯(lián)起來冕广,就構(gòu)成了一條持續(xù)交付流水線疏日。大概會(huì)經(jīng)歷以下幾個(gè)步驟:
- 代碼提交
- 運(yùn)行自動(dòng)化功能測試和靜態(tài)檢查
- 構(gòu)建
- 部署至測試環(huán)境
- 運(yùn)行自動(dòng)化驗(yàn)收測試
- 發(fā)布
在以上的步驟里,除了第一步和最后一步撒汉,所有的中間步驟都是要自動(dòng)化的沟优。
這里需要注意的是:我們需要把部署和發(fā)布解耦。發(fā)布(Release)和 部署(Deploy)是兩個(gè)不同但又相關(guān)的動(dòng)作睬辐,發(fā)布是一個(gè)業(yè)務(wù)操作挠阁,表示用戶可以接觸到最新版的應(yīng)用系統(tǒng)。 而部署是一個(gè)技術(shù)操作溯饵,表示應(yīng)用運(yùn)行在某一環(huán)境上侵俗。
Jenkins 里,我們可以采用 Job(任務(wù))的方式來執(zhí)行任務(wù)丰刊,由代碼庫觸發(fā)測試隘谣,測試完成后觸發(fā)構(gòu)建、部署和發(fā)布啄巧。
為了減少系統(tǒng)的停機(jī)時(shí)間寻歧,我們也需要使用一些零停機(jī)部署策略掌栅,例如”藍(lán)綠部署“和”金絲雀發(fā)布“。
藍(lán)綠部署
藍(lán)綠部署的做法是同時(shí)生成兩個(gè)相同的生產(chǎn)環(huán)境版本码泛,一個(gè)叫做”藍(lán)環(huán)境“猾封,一個(gè)叫做”綠環(huán)境“。用戶當(dāng)前只能訪問其中一個(gè)環(huán)境噪珊,讓另外一個(gè)環(huán)境進(jìn)行部署忘衍。待到部署完成并通過檢查之后,再切換至部署好的環(huán)境卿城。如果部署失敗枚钓,則把用戶流量切換到原先的環(huán)境就算是做到了快速回滾。某些云計(jì)算平臺(tái)內(nèi)置 了這樣的部署策略瑟押,可以幫助快速做到這一點(diǎn)搀捷。我們也可以通過更新內(nèi)部 DNS 記錄或者 Nginx 配置做到這一點(diǎn)。如果你的變更中包含數(shù)據(jù)庫變更多望,則需要額外的數(shù)據(jù)庫遷移策略和切換策略嫩舟,也會(huì)花費(fèi)額外的時(shí)間。
金絲雀發(fā)布
金絲雀發(fā)布可以看做是 藍(lán)綠發(fā)布的一種演變形式怀偷,相比藍(lán)綠發(fā)布來說更加靈活家厌。我們可以通過路由權(quán)重讓一小部分用戶嘗試新的版本。就像是在煤礦坑道里的金絲雀那樣椎工,很快就能發(fā)現(xiàn)生產(chǎn)中的問題饭于,并限制問題的影響范圍。
我們還可以基于此做 A/B 測試來度量更新的使用率维蒙。但這需要你的基礎(chǔ)設(shè)施支持“帶權(quán)流量分配”和“ Session 持久化”掰吕。前者是為了更靈活的切分流量,后者則是避免同一個(gè)用戶訪問不同版本應(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)險(xiǎn)纽门,盡可能通過流水線來對基礎(chǔ)設(shè)施進(jìn)行變更。
最后
完成了以上三個(gè)階段沸停,我們才可以說基本上完成了一個(gè)應(yīng)用程序遷移到云計(jì)算平臺(tái)上的基礎(chǔ)步驟膜毁。如果你的應(yīng)用遷移到了云平臺(tái)上并做到了以上的三個(gè)階段,才算是及格愤钾。
云計(jì)算所帶來的改變并不僅僅是可靠的廉價(jià)租賃式資源瘟滨,還會(huì)改變組織的工作方式。特別是 DevOps 運(yùn)動(dòng)的興起又為 CloudNative 的產(chǎn)品研發(fā)運(yùn)營增加了新的助燃劑能颁。關(guān)于構(gòu)建 DevOps 團(tuán)隊(duì)更多的內(nèi)容和套路杂瘸,請參見我的 GitChat 達(dá)人課:“DevOps 轉(zhuǎn)型實(shí)戰(zhàn)”。