自動(dòng)化我們的基礎(chǔ)設(shè)施來(lái)武裝工程師
segment是一家客戶(hù)數(shù)據(jù)中心,致力于幫助數(shù)千家公司收集、處理數(shù)據(jù),本文主要講了segment在自動(dòng)化基礎(chǔ)設(shè)施方面的實(shí)踐,主要包括同步開(kāi)發(fā)環(huán)境、映射開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境、本地開(kāi)發(fā)和部署到生產(chǎn)環(huán)境。
發(fā)展企業(yè)是艱難的,發(fā)展一個(gè)支撐企業(yè)前行的工程團(tuán)隊(duì)可以說(shuō)是更加困難的。但是沒(méi)有一套穩(wěn)定的基礎(chǔ)設(shè)施,做這兩件事基本是不可能的。特別是對(duì)于高速發(fā)展的企業(yè),必須授權(quán)每一個(gè)工程師在編碼、測(cè)試和交付代碼方面具有高度的自主權(quán)。
過(guò)去的一年中,我們?cè)黾恿诉_(dá)60個(gè)新的集成環(huán)境(總數(shù)超過(guò)160個(gè)),為合作伙伴建立了一個(gè)平臺(tái),用以編寫(xiě)他們自己的集成,發(fā)布了Redshift(紅移)集成,同時(shí)發(fā)表了幾個(gè)重大的產(chǎn)品公告。那段時(shí)間,圍繞多環(huán)境管理、部署代碼和通常的開(kāi)發(fā)工作流程,我們經(jīng)歷了許多成長(zhǎng)的煩惱。之后,我們的工程師是最幸福、最具生產(chǎn)力的,因?yàn)樗麄兊臅r(shí)間都花在發(fā)布商品,打造工具和擴(kuò)展服務(wù)上。開(kāi)發(fā)流程和它所支持的基礎(chǔ)設(shè)施簡(jiǎn)單易用、擴(kuò)展靈活是至關(guān)重要的。
這就是為什么我們自動(dòng)化了自己的基礎(chǔ)設(shè)施的眾多方面。下面我將分享關(guān)于我們當(dāng)下的一些更加詳細(xì)的設(shè)置,主要包括這幾個(gè)領(lǐng)域:
- 同步開(kāi)發(fā)環(huán)境
- 映射開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境
- 本地開(kāi)發(fā)
- 部署到生產(chǎn)環(huán)境
一起來(lái)深入的探討一下吧!
同步開(kāi)發(fā)環(huán)境
由于代碼的復(fù)雜性的增長(zhǎng)和工程團(tuán)隊(duì)的擴(kuò)大,在所有的工程師中保持開(kāi)發(fā)環(huán)境的一致會(huì)變得更加困難。
在我們現(xiàn)有解決方案出現(xiàn)之前,我們的工程師所面臨的一個(gè)大問(wèn)題是同步開(kāi)發(fā)環(huán)境。我們有一個(gè)Github庫(kù),里面有一組shell腳本,所有新來(lái)的工程師執(zhí)行這些腳本,將必要的工具和身份驗(yàn)證令牌裝到他們本地的機(jī)器上,這些腳本也會(huì)建立Vagrant和一個(gè)虛擬機(jī)。
但是這個(gè)虛擬機(jī)是在每臺(tái)電腦上進(jìn)行本地構(gòu)建,如果你修改了虛擬機(jī)的狀態(tài),那么為了使得讓它恢復(fù)到與其他工程師的虛擬機(jī)同樣的狀態(tài),你必須從頭構(gòu)建一切。而當(dāng)有工程師更新了虛擬機(jī),你必須在Slack上告訴每個(gè)人,讓他們從Github VM庫(kù)中拉取代碼并重新構(gòu)建。這是一個(gè)痛苦的過(guò)程,因?yàn)?Vagrant很慢。
對(duì)于一個(gè)竭盡全力快速前行的發(fā)展中團(tuán)隊(duì)來(lái)說(shuō),上面的方法并不是一個(gè)好的解決方案。
當(dāng)我們初次嘗試使用Docker時(shí),我們很喜歡其在一個(gè)可復(fù)用和隔離的環(huán)境中運(yùn)行代碼的能力。我們想要復(fù)用Docker的這些原則和經(jīng)驗(yàn),從而在不斷擴(kuò)大的工程團(tuán)隊(duì)中保持開(kāi)發(fā)環(huán)境的一致。
我們寫(xiě)了一堆工具為新來(lái)的工程師配置虛擬機(jī),從基礎(chǔ)鏡像狀態(tài)升級(jí)或是重置。當(dāng)我們的工程師初次配置虛擬機(jī)的時(shí)候,需要Github憑證和AWS令牌,然后從Docker Hub中拉取最新的鏡像并構(gòu)建。
每次運(yùn)行時(shí),我們會(huì)通過(guò)查詢(xún)Docker Hub API來(lái)確保虛擬機(jī)是最新的。這個(gè)過(guò)程會(huì)更新工程師每天所需的包、工具等。這將耗費(fèi)5秒鐘,為了確保一切運(yùn)行正常,這也是必要的。
此外,由于我們的工程師使用Mac電腦,我們從boot2dockerVirtualBox虛擬機(jī)切換到了托管于boot2docker實(shí)例的Vagrant,以便我們可以充分利用NFS的優(yōu)勢(shì)來(lái)共享主機(jī)和客戶(hù)機(jī)的volumn。在本地部署的時(shí)候,使用NFS,性能得到大大提升。最后一點(diǎn),NFS允許工程師在虛擬機(jī)外部所做的改變可以即時(shí)地在虛擬機(jī)內(nèi)部反映出來(lái)。
通過(guò)這個(gè)解決方案,我們大大減少了需要在宿主機(jī)上安裝依賴(lài)的數(shù)目?,F(xiàn)在唯一需要的是Docker、Docker Compose、Go和GOPATH配置。
映射開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境
理想的情況是在開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境中運(yùn)行相同的代碼,然而這樣分離,開(kāi)發(fā)環(huán)境中的代碼可能永遠(yuǎn)不會(huì)對(duì)生產(chǎn)環(huán)境中的代碼產(chǎn)生影響。
之前我們將AWS狀態(tài)(由Terraform生成)存儲(chǔ)在Terraform的文件中,但它并不是一個(gè)完美的系統(tǒng)。例如,如果兩個(gè)人異步操作并應(yīng)用了不同的改變,狀態(tài)將會(huì)改變,最后推送代碼的人將很難搞定合并沖突。
我們盡可能以最簡(jiǎn)單的方式實(shí)現(xiàn)了staging和production環(huán)境的映射:
從一個(gè)文件夾復(fù)制文件到另一個(gè)文件夾。Terraform使我們?cè)谛抻喕A(chǔ)設(shè)施、部署新服務(wù)和做出改進(jìn)方面節(jié)省了大把時(shí)間。
在應(yīng)用之前,我們通過(guò)編寫(xiě)定制的構(gòu)建過(guò)程以及確保恰當(dāng)?shù)陌踩蛩匾芽紤]在內(nèi)來(lái)集成Terraform和CircleC。
目前,我們?cè)贕ithub上有一個(gè)名為基礎(chǔ)設(shè)施的單一庫(kù),其中包含了Terraform的腳本集合,用以為每一個(gè)容器配置環(huán)境變量和設(shè)置容器。
當(dāng)我們想要改變基礎(chǔ)設(shè)施中的某些東西時(shí),將必要的修改寫(xiě)進(jìn)Terraform腳本,并在新的pull請(qǐng)求之前運(yùn)行它們以便基礎(chǔ)設(shè)施團(tuán)隊(duì)中的其他成員來(lái)review它。一旦pull請(qǐng)求合并到主分支,CircleCI就會(huì)啟動(dòng)部署進(jìn)程:狀態(tài)變?yōu)閜ulled,本地被修改,并再次存入到S3。
#p#
本地部署
種子庫(kù)
本地開(kāi)發(fā)的時(shí)候,使用虛擬數(shù)據(jù)填充本地?cái)?shù)據(jù)庫(kù)是很重要的,這樣會(huì)讓我們的應(yīng)用看起來(lái)更真實(shí)。所以,種子庫(kù)是配置開(kāi)發(fā)環(huán)境的共同組成部分。
我們依賴(lài)CircleCI、Docker和volumn容器來(lái)提供獲取虛擬數(shù)據(jù)的便捷途徑。volume容器是靜態(tài)數(shù)據(jù)的便攜鏡像。我們決定使用volume容器,因?yàn)閿?shù)據(jù)模型和邏輯越來(lái)越松耦合而且容易維護(hù)。這樣做也是以防萬(wàn)一,在我們的基礎(chǔ)設(shè)施的其他地方可以用到這些數(shù)據(jù)(比如測(cè)試等,誰(shuí)知道呢)。
當(dāng)我們?cè)陂_(kāi)發(fā)過(guò)程中啟動(dòng)app服務(wù)器時(shí),就會(huì)自動(dòng)加載種子數(shù)據(jù)到我們的本地開(kāi)發(fā)環(huán)境中。例如,當(dāng)app(我們的主應(yīng)用)容器在開(kāi)發(fā)環(huán)境中啟動(dòng),app的docker-compose.yml腳本就會(huì)從Docker Hub中拉取最新的種子鏡像,并在虛擬機(jī)中掛載原生數(shù)據(jù)。
Docker Hub中的種子鏡像產(chǎn)生自Github倉(cāng)庫(kù)中的種子,作為我們導(dǎo)入到數(shù)據(jù)庫(kù)中的原生對(duì)象,它就是一組JSON文件。為了更新種子數(shù)據(jù),我們將 CircleCI配置到倉(cāng)庫(kù)上,以便任何到主分支的推送都會(huì)構(gòu)建(從Docker Hun中賺錢(qián)我們的mongodb容器和redis容器)并向Docker Hub中推送新的種子鏡像,這樣我們就能在app中使用這個(gè)容器了。
生成微服務(wù)
由于Segment數(shù)據(jù)密集型的特性,我們的app已經(jīng)依賴(lài)幾個(gè)微服務(wù)(db,redis,nsq等)。為了使我們的工程師可以開(kāi)發(fā)app,我們需要一個(gè)簡(jiǎn)單的方法在本地構(gòu)建這些服務(wù)。
Docker再一次使得這種工作流變的非常容易。
類(lèi)似于我們使用種子volume容器掛載數(shù)據(jù)到本地虛擬機(jī)那樣,我們以同樣方式來(lái)使用微服務(wù)。我們使用doker compose文件從Docker Hub抓取鏡像來(lái)進(jìn)行本地構(gòu)建、設(shè)置地址并最終將復(fù)雜性降低到一條終端命令就可以讓一切啟動(dòng)并運(yùn)行。
部署到生產(chǎn)環(huán)境
你編寫(xiě)代碼,但從不將代碼部署都生產(chǎn)環(huán)境,這種情況真的發(fā)生過(guò)嗎?
部署代碼到生產(chǎn)環(huán)境是開(kāi)發(fā)工作流程中的一個(gè)組成部分。在Segment,圍繞部署代碼到生產(chǎn)環(huán)境,我們優(yōu)先考慮難易程度和靈活性,因?yàn)檫@使得工程師可以快速的行動(dòng)而富有成效。我們還創(chuàng)造了足夠多的工具來(lái)為處理錯(cuò)誤,回滾和監(jiān)視構(gòu)建的狀態(tài)保駕護(hù)航。
我們使用Docker、ECS、CircleCI和Terraform來(lái)盡可能地自動(dòng)化持續(xù)部署。
無(wú)論何時(shí)代碼被推送或者被合并到主分支,CircleCI腳本就會(huì)構(gòu)建容器并將容器推送到Docker Hub上。
然后,我們有一個(gè)單獨(dú)的構(gòu)建服務(wù)來(lái)更新ECS中的任務(wù)定義,這是對(duì)于由request請(qǐng)求所觸發(fā)服務(wù)的預(yù)設(shè)(這樣可以讓我們通過(guò)Slack slash命令進(jìn)行部署)。
使用這種設(shè)置,我們可以為任何服務(wù)定義配置,使得我們的工程師創(chuàng)建和部署新的微服務(wù)變得非常容易。正如Calvin在以前的文章中所提到的,“使用Docker、ESC和Terraform重構(gòu)基礎(chǔ)設(shè)施”:
我們不再需要一組復(fù)雜的供應(yīng)腳本或是AMI--我們只需將生產(chǎn)集群交給鏡像。也沒(méi)有更多的狀態(tài)實(shí)例,我們可以保證在staging和prod環(huán)境中運(yùn)行完全相同的代碼。 |
部署的自動(dòng)化和易用不僅對(duì)我們的工程師產(chǎn)生了積極影響,我們的成功以及市場(chǎng)團(tuán)隊(duì)可以在一些倉(cāng)庫(kù)中更新markdown文件,當(dāng)要合并到主分支時(shí),踢出自動(dòng)部署進(jìn)程以便可以在幾分鐘內(nèi)看到這些改變。
快速響應(yīng)和成長(zhǎng)
由于我們選擇花費(fèi)精力重新思考和自動(dòng)化我們的開(kāi)發(fā)流程及其配套的基礎(chǔ)設(shè)施,使得我們的工程團(tuán)隊(duì)反應(yīng)更加迅速和自信。我們花費(fèi)更多時(shí)間研究我們所熱愛(ài)的高杠桿工作--發(fā)布產(chǎn)品、打造內(nèi)部工具,并減少花費(fèi)在yak shaving的時(shí)間。
也就是說(shuō),這絕不是我們的基礎(chǔ)設(shè)施自動(dòng)化的最后一次迭代。我們不斷地嘗試新的工具并測(cè)試新的想法,來(lái)看看我們可以探索出什么可以進(jìn)一步提高效率的東西。
對(duì)于我們來(lái)說(shuō),這是一個(gè)漫長(zhǎng)的學(xué)習(xí)過(guò)程,我們很樂(lè)意傾聽(tīng)社區(qū)中其他人有關(guān)他們開(kāi)發(fā)工作流相關(guān)的實(shí)踐。如果你最終要實(shí)現(xiàn)這樣的事情(或者已經(jīng)實(shí)現(xiàn)),讓我們知道!我們很想聽(tīng)聽(tīng)你做了什么,對(duì)于遇到相似問(wèn)題的人,你所做的哪些對(duì)他們有所幫助,哪些是不奏效的。