Instagram最近發(fā)布了一篇文章來介紹他們的持續(xù)部署(Continuous Deployment,簡(jiǎn)稱CD)流水線。借助這套CD流水線,工程師們能更快地將代碼推送至生產(chǎn)環(huán)境,更方便地找到有問題的改動(dòng),以及將代碼隨時(shí)保持在可發(fā)布的狀態(tài)。在一段時(shí)間頻繁的迭代之后,最終其背后的核心原則主要包括:
擁有高質(zhì)量的測(cè)試集 擁有快速定位問題改動(dòng)的能力 擁有可視化部署流程的能力 擁有可行的回滾(rollback)計(jì)劃在Instagram使用這套CD流水線之前,發(fā)布流程是由一堆腳本與手動(dòng)步驟混雜而成。通常有一臺(tái)機(jī)器會(huì)先被部署并用來進(jìn)行可用性測(cè)試(Sanity),然后剩下的機(jī)器才會(huì)被依次部署。Sauron是一款基本的發(fā)布跟蹤系統(tǒng),由一套UI和一個(gè)數(shù)據(jù)庫(kù)構(gòu)成,用于可視化之前發(fā)布的結(jié)果。整個(gè)發(fā)布流程使用Fabric來實(shí)現(xiàn)基于SSH的自動(dòng)化部署。
2013年,F(xiàn)acebook收購(gòu)了Instagram,并開始將其基礎(chǔ)設(shè)施從AWS上遷移至Facebook自己的數(shù)據(jù)中心。據(jù)Facebook產(chǎn)品工程師(Production Engineer)、前文作者M(jìn)ichael Gorven介紹,Instagram擁有幾千臺(tái)機(jī)器,卻能做到每天部署30至50次代碼。之所以規(guī)模龐大并持續(xù)增長(zhǎng)的基礎(chǔ)設(shè)施沒有妨礙代碼的發(fā)布,主要?dú)w功于Facebook用于發(fā)布的一套分布式SSH系統(tǒng)。
持續(xù)部署最初遇到的兩個(gè)阻礙,分別是脆弱的測(cè)試集,以及一大堆等待發(fā)布的改動(dòng)。其中,前者主要由低效及不穩(wěn)定的測(cè)試造成,而后者則由失敗的部署造成。基礎(chǔ)設(shè)施的規(guī)模越大,部署時(shí)出錯(cuò)的幾率越高,對(duì)應(yīng)的每次部署所攜帶的改動(dòng)就越多,從而使部署更容易出錯(cuò)。就解決方法來說,脆弱的測(cè)試集主要靠人為優(yōu)化,而部署的試錯(cuò)則可以用所謂的“金絲雀部署”(Canary Deployment)來解決。在金絲雀部署中,改動(dòng)只被推送到一部分離線服務(wù)器上并進(jìn)行自動(dòng)化測(cè)試,然后根據(jù)測(cè)試結(jié)果來決定這次部署是繼續(xù)還是回滾。
在此基礎(chǔ)上,Instagram在CD流水線中加入了更多智能,使其能夠判斷決定哪些改動(dòng)可以發(fā)布上線。通過與Jenkins的集成,CD流水線可以根據(jù)測(cè)試結(jié)果將改動(dòng)分為“好”與“壞”兩類,并直接將分類結(jié)果推送至Sauron,以供利益干系人(stakeholders)查閱。
Sauron會(huì)顯示每一個(gè)改動(dòng)及其發(fā)布狀態(tài),并會(huì)將包含作者信息的發(fā)布動(dòng)態(tài)自動(dòng)推送到一個(gè)聊天頻道;同時(shí),改動(dòng)的作者將會(huì)收到一封郵件與一條短信,來告知他們所做的改動(dòng)正在發(fā)布。稍后,所有發(fā)布動(dòng)態(tài)將被記錄在數(shù)據(jù)庫(kù)中,以便日后做各種可視化分析。
在越過最初的障礙后,CD流水線又面臨一個(gè)更大的挑戰(zhàn),即數(shù)據(jù)庫(kù)的遷移,包括數(shù)據(jù)的遷移與表結(jié)構(gòu)的遷移。一般來講,數(shù)據(jù)庫(kù)遷移的解決方案大多是根據(jù)各自的產(chǎn)品和基礎(chǔ)設(shè)施而制作的定制化方案,而Instagram也不例外。每次將數(shù)據(jù)遷移時(shí),Instagram的大致做法為:
將一個(gè)在線數(shù)據(jù)片(shard)慢慢復(fù)制至新的位置,直至兩者的區(qū)別足夠小 使用開關(guān)將老的數(shù)據(jù)片下線 將僅剩的區(qū)別搬到新的數(shù)據(jù)片上 使用開關(guān)將新的數(shù)據(jù)片上線Gorven還詳細(xì)介紹了如何使用開關(guān)(feature toggles)來做表結(jié)構(gòu)的遷移。
首先,我們更新本發(fā)布代碼,使其能夠同時(shí)讀寫新老版本的數(shù)據(jù)。然后,我們復(fù)制一份表結(jié)構(gòu)并對(duì)其進(jìn)行改動(dòng),比如添加幾列,并將數(shù)據(jù)寫操作同時(shí)作用于新老表結(jié)構(gòu)。如有必要,我們將通過批處理,將現(xiàn)有數(shù)據(jù)從老的表結(jié)構(gòu)更新到新的表結(jié)構(gòu)。最終,我們會(huì)將數(shù)據(jù)讀操作從老版本數(shù)據(jù)切換到新版本數(shù)據(jù),而保持寫操作不變。這個(gè)階段會(huì)持續(xù)一段時(shí)間,以防萬(wàn)一,直到我們將數(shù)據(jù)讀寫操作都切換到新版本數(shù)據(jù)上,完成整個(gè)遷移。
作為后續(xù),Instagram的發(fā)布工程團(tuán)隊(duì)將會(huì)把注意力放在提高甄別問題改動(dòng)的能力,同時(shí)盡量保持發(fā)布的速度不受影響。
查看英文原文:Continuous Deployment at Instagram