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