近期,炫一下(北京)科技有限公司(簡稱“一下科技”)短視頻產(chǎn)品“秒拍”完成了一個“大動作”——將原來部署在虛擬機上的主體業(yè)務(wù)遷移到華為云,同時將公司的技術(shù)體系承載在下一代虛擬技術(shù)容器(Docker)上。而這一系列動作是在業(yè)務(wù)不下線,用戶無感知的前提下完成的,秒拍是如何做到的?
近期,炫一下(北京)科技有限公司(簡稱“一下科技”)短視頻產(chǎn)品“秒拍”完成了一個“大動作”——將原來部署在虛擬機上的主體業(yè)務(wù)遷移到華為云,同時將公司的技術(shù)體系承載在下一代虛擬技術(shù)容器(Docker)上。而這一系列動作是在業(yè)務(wù)不下線,用戶無感知的前提下完成的,秒拍是如何做到的?
秒拍是一個媒體屬性很強的業(yè)務(wù),在用戶規(guī)模達(dá)到一定體量后,由明星熱點事件引發(fā)的流量突發(fā)狀況非常嚴(yán)重,而傳統(tǒng)虛機業(yè)務(wù)存在擴容響應(yīng)速度慢,成本高等一系列問題,于是秒拍想到了容器。容器服務(wù)擁有啟動快速、占用資源少、運行效率高等技術(shù)特點,在處理海量數(shù)據(jù)方面擁有天然的優(yōu)勢。但是如何保證業(yè)務(wù)能夠快速無縫地進(jìn)行切換,讓最終用戶毫無感知的完成從虛機到容器的遷移,真正要做到這一點非常困難。
盡管困難重重,但秒拍在評估了未來業(yè)務(wù)需求和技術(shù)團隊規(guī)模之后,還是選擇將已部署在云上的主體業(yè)務(wù)遷移到華為云CCE上。而華為云強大的技術(shù)支持能力和服務(wù)團隊,為這次遷移解決了后顧之憂。
以下是秒拍架構(gòu)師李東輝對本次業(yè)務(wù)遷移的記錄,如果你也希望從虛機向更靈活的容器升級,又不希望影響業(yè)務(wù),不妨一看:
背景
我們現(xiàn)在主體業(yè)務(wù)已經(jīng)是部署在某云上了,但整個技術(shù)體系,還是基于傳統(tǒng)的虛擬機去承載的,由于我們產(chǎn)品本身的媒體屬性,導(dǎo)致了不可避免的會經(jīng)常遇到突發(fā)流量,相比于一直比較平穩(wěn)的流量,這種對服務(wù)端的考驗更高,核心關(guān)注點還是在怎么保障在這種時刻用戶都能得到良好的體驗。
另一方面,由于云平臺本身的一些不可抗力因素,不能保證百分百的高可用,怎么降低單點依賴的風(fēng)險,也是我們需要重點考慮的。
經(jīng)過綜合性的權(quán)衡,我們決定把主體業(yè)務(wù)遷移到華為云,并且優(yōu)化升級原來的架構(gòu),以便更好的支撐海量用戶訪問。前端機也從VM過渡到docker,遷移后的整體架構(gòu)圖如下
各個資源的遷移過程
1. mc遷移
現(xiàn)在業(yè)務(wù)上使用mc只是做臨時緩存,cache miss會從存儲(DB、ES等)拉一份寫進(jìn)去,并且業(yè)內(nèi)也沒有比較成熟的mc存量與增量遷移方案,所以這部分?jǐn)?shù)據(jù)可以忽略,等上線前,預(yù)熱一部分熱點數(shù)據(jù)進(jìn)去。不過使用上有一些區(qū)別,原平臺使用的是服務(wù)端集群,現(xiàn)在是客戶端集群,需要建立MC連接的時候,添加所有的服務(wù)器列表以及權(quán)重。
2. mq遷移
mq主要用來解耦業(yè)務(wù)模塊,生產(chǎn)端生產(chǎn)一份數(shù)據(jù),消費端可能有多個,遷移的話,需要先配置好資源的vhost,exechange還有queue,服務(wù)端先更新配置上線,數(shù)據(jù)寫入到新資源,消費端在舊資源消費完成后,切換到新資源的消費上。
3. redis遷移
redis的遷移需要區(qū)分兩個場景的數(shù)據(jù),一個是緩存數(shù)據(jù),可以按照mc的遷移策略忽略,另一部分是持久化數(shù)據(jù),主要是業(yè)務(wù)上的各種計數(shù),這部分?jǐn)?shù)據(jù)要求盡量精確快速的遷移到新資源,當(dāng)時考慮了兩種方案
· 一種呢,是基于快照文件遷移 存量數(shù)據(jù)可以通過RDB快照,只需要原平臺有備份RDB的權(quán)限,在新資源通過快照回放完成全量數(shù)據(jù)的遷移。 這種方案優(yōu)點比較突出,操作簡單快速,但缺點是不支持增量同步。
· 另一種呢,基于業(yè)務(wù)遷移 首先,讀 優(yōu)先從新資源讀,沒有命中的從老資源讀取一份,寫入到新資源并返回這個值。 其次,寫 (incr decr操作) 優(yōu)先更新老資源,并且按照更新后的返回值寫入到新資源。
這種方案能兼顧存量與增量數(shù)據(jù),但存在的問題是,讀寫新老資源的過程非原子性,理論上高并發(fā)情況下會存在一定誤差,并且業(yè)務(wù)上的這種改造增加了后期的維護成本,另外,從性能方面考慮,原來一次連接(短連接)、一次redis操作就能搞定的事,現(xiàn)在都需要兩到三次。
綜合現(xiàn)在的業(yè)務(wù)量考慮,我們采取了第一種方案,但是時間點選在凌晨四點低峰時段,將影響范圍盡可能降到最低,后續(xù)通過落到DB的數(shù)據(jù)做統(tǒng)計恢復(fù)。
4. db遷移
db遷移相對比較容易,全量數(shù)據(jù)預(yù)先復(fù)制一份過去,增量數(shù)據(jù)因為都是基于binlog訂閱,只需要獲取原平臺DB的權(quán)限,就可以通過binlog同步到新數(shù)據(jù)庫。
這里需要注意的是一個主從同步的問題,新資源主從是半同步復(fù)制,主庫只需要等待一個從庫節(jié)點收到并且 Flush Binlog 到 Relay Log 文件即可,同時,這里只是一個收到的反饋,而不是已經(jīng)完全完成并且提交的反饋,這時候,主庫就會進(jìn)行其他操作,相比與之前的全同步的事務(wù)復(fù)制,節(jié)省了很多時間,但是也造成了新的問題,即:主從有理論上1ms的延遲,實際測試延遲時間是0.5-0.8ms,這在“更新DB后又立馬讀取一次DB數(shù)據(jù)”的場景下會有問題,并且根據(jù)Cache Aside Pattern的緩存更新策略,DB更新成功會直接刪除緩存,由于主從延遲,這時候讀進(jìn)程讀取到老數(shù)據(jù)并且寫入到緩存,從而導(dǎo)致了一段時間內(nèi)的臟數(shù)據(jù)。
有一個比較快速的方式能夠解決這個問題,那就是在DB更新成功后直接更新緩存,但是這樣處理后會產(chǎn)生新的問題,并發(fā)的寫操作,又會導(dǎo)致同一資源key的臟數(shù)據(jù),不過是概率大小的問題。這就涉及到了取舍,就像為了保證DB、cache的強一致性,采用2PC(prepare, commit/rollback),大大降低性能一樣,軟件設(shè)計從來都是取舍。
5. ES遷移
ES主要服務(wù)的是APP以及Admin后臺,用戶、媒資等數(shù)據(jù)的搜索,數(shù)據(jù)源在DB,所以存量數(shù)據(jù)直接從DB拉一份進(jìn)去,增量數(shù)據(jù)通過監(jiān)聽DB更新,同步到ES里。
6. 版本庫遷移
版本庫的遷移主要是方便接入鏡像構(gòu)建與k8s部署,同時呢,項目使用到的資源鏈接地址、用戶名、密碼等也需要更新,這部分都是統(tǒng)一配置,直接修改就行。
7. 服務(wù)發(fā)現(xiàn)
原來業(yè)務(wù)上都是基于服務(wù)端發(fā)現(xiàn)的模式,一個微服務(wù)對應(yīng)著一個LB,通過DNS解析到對應(yīng)的LB IP,LB實現(xiàn)VM的負(fù)載均衡策略與?;顧C制。LB下層現(xiàn)在多了一層k8s的調(diào)度,k8s調(diào)度的單元也不再是VM,而是Pod(邏輯主機),在這里VM的存在也僅僅是提供不同規(guī)格的物理資源。
其次使用DNS解析也可以方便不同VPC子網(wǎng)指向不同的資源IP,例如測試環(huán)境與生產(chǎn)環(huán)境,項目使用到的資源地址是相同的,只不過解析到了不同的資源。
8. Dokerfile
需要預(yù)先制作好基礎(chǔ)鏡像,包含基本的php、nginx環(huán)境跟用戶權(quán)限這些,Dokerfile主要實現(xiàn)將項目代碼復(fù)制到容器。
9. 切流量
后端資源遷移完成,準(zhǔn)備就緒以后,就可以開始切公網(wǎng)流量了,非核心業(yè)務(wù)直接修改公網(wǎng)DNS,解析到新LB IP,核心業(yè)務(wù)LB上層還有一層高防,在高防不變的情況下,只需要修改高防源站IP到新LB就行。
流量遷移完畢后,全線驗證,觀察錯誤日志,當(dāng)然這個過程并不是只有等流量切完才會開始,而是從資源遷移開始就一直持續(xù)進(jìn)行的。
10. 部署上線
原來的部署是通過中控機,將代碼分發(fā)到各個線上服務(wù)器,現(xiàn)在呢,需要使用上一步創(chuàng)建的Dockerfile,構(gòu)建鏡像,將構(gòu)建好的鏡像通過k8s滾動升級(先kill老鏡像,再派生出新鏡像)。升級的步驟如下:
push后鏡像已經(jīng)推送到私有倉庫,現(xiàn)在需要創(chuàng)建k8s的配置文件用于管理和升級容器。
創(chuàng)建pod kubectl create -f miaopai.yaml 后邊再升級容器,先把容器更新push到倉庫后,修改image地址,通過apply進(jìn)行升級就可以。
架構(gòu)優(yōu)化
架構(gòu)優(yōu)化的目標(biāo)是分析現(xiàn)在業(yè)務(wù)上存在的問題,并針對性的優(yōu)化解決,結(jié)合壓測結(jié)果,主要確定了幾個優(yōu)化點。
1. mc、redis的優(yōu)化
mc使用上存在的問題是,只有在存儲查詢到的情況下才會緩存數(shù)據(jù),這樣就會導(dǎo)致很多空查詢落到存儲,解決這個問題只需要將沒有查詢到數(shù)據(jù)的情況,也寫一份空數(shù)據(jù)到緩存就能解決。
除此之外,mc的批量查詢,存在太多的偽批量(redis也存在),例如:foreach每次循環(huán)里都使用get查詢,需要將這樣的處理都改成multiget的形式,不過multiget在集群的情況下會存在hole現(xiàn)象,這個問題最早是由 facebook 的工作人員提出的
facebook 在 2010 年左右,memcached 節(jié)點就已經(jīng)達(dá)3000 個.緩存數(shù)千 G 內(nèi)容.他們發(fā)現(xiàn)了一個問題-memcached 連接頻率,效率下降了,于是加 memcached 節(jié)點, 添加了后, 發(fā)現(xiàn)因為連接頻率導(dǎo)致的問題, 仍然存在, 并沒有好轉(zhuǎn),稱之為”multiget hole現(xiàn)象”。請求多臺服務(wù)器并不是問題的癥結(jié),真正的原因在于客戶端在請求多臺服務(wù)器時是并行的還是串行的!問題是很多客戶端,包括Libmemcached在內(nèi),在處理Multiget多服務(wù)器請求時,使用的是串行的方式!也就是說,先請求一臺服務(wù)器,然后等待響應(yīng)結(jié)果,接著請求另一臺,結(jié)果導(dǎo)致客戶端操作時間累加,請求堆積,性能下降。
有推薦根據(jù)key做hash的,這樣就可以使得相同key前綴的數(shù)據(jù)分布在一臺機器上,但是這樣又會導(dǎo)致新的問題,例如:增加業(yè)務(wù)復(fù)雜度,每個節(jié)點的數(shù)據(jù)分布不均等等,不過相信大部分公司業(yè)務(wù)的體量都沒辦法對標(biāo)facebook的,如果真的到了需要考慮這個問題的時候,其實是推薦使用redis的pipeline并行機制來解決的。
2. 核心業(yè)務(wù)的降級策略
作為APP內(nèi)首屏的幾個tab,數(shù)據(jù)都是從推薦系統(tǒng)中獲取,一旦推薦系統(tǒng)掛掉,基本沒有了用戶體驗,所以必要時還是需要采用熔斷降級策略,降級策略相對來說,只需要保證用戶能獲取到部分列表數(shù)據(jù),即使所有用戶獲取到的數(shù)據(jù)都一樣。實現(xiàn)上呢,先把部分列表數(shù)據(jù)存儲到cache,一旦發(fā)生熔斷,那么數(shù)據(jù)從推薦系統(tǒng)讀取的渠道會直接切斷,轉(zhuǎn)而從cache里讀取返回給用戶。但是有一個問題,讀取的這個流程應(yīng)該是由業(yè)務(wù)完成,還是由前端web服務(wù)器(nginx)直接完成呢。我們目前采用的后者,一方面使用ngx_lua能保證系統(tǒng)的吞吐,另一方面不僅僅是推薦系統(tǒng),即使在服務(wù)端整體掛掉的情況下,也可以繼續(xù)提供服務(wù)。
觸發(fā)熔斷的條件可以有很多,例如:每20個請求中,50%失敗,當(dāng)然了,失敗包括響應(yīng)失敗跟超時,也可以根據(jù)當(dāng)次請求結(jié)果來判斷。熔斷的條件其實并沒有什么標(biāo)準(zhǔn),更多的是依照以往系統(tǒng)的經(jīng)驗來一步步調(diào)整。在以前并沒有很多熔斷經(jīng)驗的情況下,盡量擴大這個閾值,隨著經(jīng)驗的一步步積累,再確定各個模塊比較合理的熔斷條件和降級策略。
3. 負(fù)載均衡策略
傳統(tǒng)負(fù)載均衡LB,實現(xiàn)的是請求到VM和端口的負(fù)載均衡,容器化之后,LB下層掛載了k8s集群,實際上這里的負(fù)載均衡除了LB的,還有k8s的,即請求到達(dá)節(jié)點(VM)后,再負(fù)載均衡到不同的容器。
上邊提到的負(fù)載均衡只是四層(IP加端口),如果要根據(jù)應(yīng)用層的信息,比如:URI,cookie等等,做負(fù)載均衡,就需要使用七層LB,我們使用到的場景,主要是還沒有切割成微服務(wù)的大單體,根據(jù)URI,將不同模塊的請求打到不同的四層LB。
4. Mysql HA
Mysql作為我們底層的核心存儲,必須要保障它的高可用,現(xiàn)在架構(gòu)是采用主從+主備的形式,不過這兩種方式都有個共性的問題,主機故障后,無法進(jìn)行寫操作,如果主機一直無法恢復(fù),需要人工指定新主機角色。優(yōu)化的目標(biāo)也顯而易見,就是設(shè)計雙機切換,在主機故障之后,能夠自動切換到其他主機。
PHP本身實現(xiàn)了mysql的負(fù)載均衡和failover策略,需要依賴插件mysqlnd_ms,詳見https://php.net/mysqlnd_ms,不過僅限于PHP5.x版本,倒是有支持PHP7.0以上的非官方版本,https://github.com/sergiotabanelli/mysqlnd_ms,但如果直接用在生產(chǎn)環(huán)境,并不十分明智,并且mysqlnd_ms需要特殊格式的資源配置,在一個項目里維護兩份資源配置,也會帶來新的復(fù)雜度問題。
要實現(xiàn)雙擊切換的核心點在于,對主機狀態(tài)的判斷,和狀態(tài)決策,可以通過引入一個中介角色,主機和備機把狀態(tài)傳遞給中介,由中介完成決策功能,但引入中介的角色并不是沒有代價的,那就是要考慮中介角色的高可用。這就陷入了一個遞歸的陷阱:為了實現(xiàn)高可用,我們引入中介,但中介本身又要求高可用,于是又要設(shè)計中介的高可用方案……如此遞歸下去就無窮無盡了。
MongoDB的Replica Set采取的就是這種方式,基本架構(gòu)如下:
幸運的是,開源方案已經(jīng)有比較成熟的中介式解決方案,例如:Zookeeper和Keepalived。ZP本身已經(jīng)實現(xiàn)了高可用集群架構(gòu),因此已經(jīng)解決了中介本身的可靠性問題,在實踐中也推薦這種架構(gòu)。
5. 日志與監(jiān)控
線上日志的定時收集反饋也是必不可少的,日志包括服務(wù)器的access_log,error_log,當(dāng)然還有業(yè)務(wù)的自定義log。收集的目的主要是用來統(tǒng)計一段時間內(nèi)的http code 分布、響應(yīng)時間和錯誤信息。
通過在實例跟資源上部署agent,定時收集CPU和內(nèi)存信息也是必要的。統(tǒng)計型的數(shù)據(jù)需要收集匯總成表格,方便觀察,各種指標(biāo)的閾值也需要提前設(shè)置好,超過閾值后能夠及時報警,通知到責(zé)任人。當(dāng)然了,監(jiān)控不是最終目的,及時巡檢線上資源、接口,排除系統(tǒng)隱患,防范于未然才是終極之道。
不得不說,互聯(lián)網(wǎng)企業(yè)把大多數(shù)業(yè)務(wù)部署在云服務(wù)器上,現(xiàn)在已漸成趨勢,但由于歷史原因,技術(shù)往往是架設(shè)在傳統(tǒng)的虛擬機(VM)上。如果企業(yè)要過渡到下一代虛擬技術(shù)容器,會涉及到各類資源遷移和技術(shù)架構(gòu)優(yōu)化,整個過程是必須短暫而痛苦的。但如果沒有相應(yīng)規(guī)模的技術(shù)團隊來操作,再加上云廠商沒有完善的技術(shù)支持團隊,這個過程會更加痛苦。如何減少企業(yè)業(yè)務(wù)升級的痛苦,這就非??简炂髽I(yè)技術(shù)決策者的選擇智慧!華為云,目前已經(jīng)展現(xiàn)出了在技術(shù)服務(wù)的獨特優(yōu)勢,未來肯定是擺在企業(yè)面前最具競爭力的選項之一。
免責(zé)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn)自行上傳,本網(wǎng)站不擁有所有權(quán),也不承認(rèn)相關(guān)法律責(zé)任。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,請發(fā)送郵件至:operations@xinnet.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,本站將立刻刪除涉嫌侵權(quán)內(nèi)容。