An engineer, it is said, is someone who can do for a dime what any fool can do for a dollar.
項(xiàng)目在演進(jìn)過(guò)程中死姚,團(tuán)隊(duì)有了一套較為完備的開(kāi)發(fā)pipeline麻诀,這篇文章將講述全棧系統(tǒng)中這個(gè)較為遠(yuǎn)離業(yè)務(wù)的環(huán)節(jié) —— DevOps。
概念淺析
開(kāi)宗明義扭勉,為了讓大家更好的理解工作腌且,我們先來(lái)一波名次的定義摩瞎。
CI: Continuous Integration, 持續(xù)集成乱灵,指的是頻繁的將代碼集成到主干上硫朦,每次集成都通過(guò)自動(dòng)化的構(gòu)建(包括編譯、發(fā)布鹃唯、自動(dòng)化測(cè)試)來(lái)驗(yàn)證爱榕;
CD: Continuous Deliver, 持續(xù)交付瓣喊,指的是在代碼集成坡慌,也就是分支代碼合并到主干之后,能夠被手動(dòng)的觸發(fā)藻三,是否發(fā)布到生產(chǎn)環(huán)境洪橘,直接讓用戶體驗(yàn)最新代碼跪者;
CD: Continuous Deployment, 持續(xù)部署,和持續(xù)交付一樣熄求,只不過(guò)由手動(dòng)觸發(fā)變成自動(dòng)發(fā)布渣玲,不再需要人為干擾。
如果想更了解CI/CD在業(yè)務(wù)層面的意思弟晚,可以閱讀我的另一篇文章 精益開(kāi)發(fā)忘衍,關(guān)于持續(xù)交付。
鏈路重點(diǎn)
項(xiàng)目開(kāi)發(fā)經(jīng)歷了這樣幾個(gè)階段:
- 每個(gè)sprint的交付日都緊張兮兮卿城,不同分支的代碼都需要往master上合并枚钓,push、merge request瑟押、conflict搀捷、rebase、test多望,如此往復(fù)嫩舟,單單集成開(kāi)發(fā)的代碼都需要一下午的時(shí)間;
- 為了敢于合并怀偷,大膽上線家厌,團(tuán)隊(duì)寫足了自動(dòng)化測(cè)試,完善了Gitlab的pipeline椎工,隨后在交付日就只需要半個(gè)小時(shí)到兩個(gè)小時(shí)像街;
- 隨后發(fā)現(xiàn)部署新版本不方便,就用dockerfile來(lái)打包可運(yùn)行的鏡像晋渺,然后在交付日我們只需要點(diǎn)擊部署就OK了镰绎,上線不過(guò)十分鐘。
在奇跡般的變化背后木西,有這樣幾件事值得注意畴栖。
階段一:緊張交付一下午
團(tuán)隊(duì)會(huì)盡可能在上午完成feature的開(kāi)發(fā),一般有三個(gè)分支需要合并八千,每個(gè)分支合并前后都需要手動(dòng)運(yùn)行測(cè)試吗讶,由于測(cè)試都依賴開(kāi)發(fā)環(huán)境數(shù)據(jù)庫(kù)而不能并發(fā)運(yùn)行,上線之后需要持續(xù)監(jiān)控半小時(shí)恋捆,很有可能hotfix改bug照皆。
條件的艱苦,使每一次上線都是團(tuán)隊(duì)的大事件沸停,也讓我學(xué)會(huì)了代碼部署的每一個(gè)小細(xì)節(jié)膜毁,更在之后的日子里讓我感受到計(jì)算機(jī)對(duì)效率的提升之巨。
階段二:輕松部署半小時(shí)
手動(dòng)測(cè)試到自動(dòng)測(cè)試
利用Gitlab的pipeline流程,在配置好runner后瘟滨,就可以很輕松的運(yùn)行自動(dòng)化測(cè)試了候醒。(在剛開(kāi)始自動(dòng)化時(shí),我們選用了gitlab的webhook杂瘸,webhook會(huì)在push和merge事件之后倒淫,觸發(fā)一個(gè)鉤子到特定的機(jī)器上運(yùn)行測(cè)試,然后機(jī)器上會(huì)出現(xiàn)各種耦合败玉,不建議使用)
完善測(cè)試
感受到自動(dòng)化測(cè)試的便利之后敌土,為了讓團(tuán)隊(duì)對(duì)上線有信心,我們編寫了大量的測(cè)試运翼,方方面面測(cè)了個(gè)遍纯赎,終于不再懼怕重構(gòu),敢于上線南蹂。
testStage:
stage: test
image:
name: image # 使用合適的鏡像犬金,具備測(cè)試運(yùn)行所需要的環(huán)境
services:
- name: https://hub.docker.com/_/mongo
alias: mongo # 使用官方的mongo鏡像當(dāng)作service,可以隔離集成測(cè)試的環(huán)境六剥,讓測(cè)試并發(fā)運(yùn)行而不互相干擾
script:
- python -m unittest tests.test_a.TestA
only:
- develop # 個(gè)性化設(shè)置晚顷,僅在develop分支上運(yùn)行,讓集成測(cè)試更快更方便
Advantages:
- 并行的測(cè)試大大提高了集成的效率疗疟;
- 每次提交都會(huì)運(yùn)行的測(cè)試该默,杜絕了忘記測(cè)試的隱患;
- 測(cè)試覆蓋率很高策彤,代碼上線很放心栓袖;
- yml文件中的注釋都有一些痛的經(jīng)歷:
- 在runner上準(zhǔn)備python的虛擬環(huán)境非常困難,下文中將說(shuō)到鏡像的由來(lái)店诗;
- 為解決共享耦合的測(cè)試環(huán)境裹刮,我依次選用了mock mongo代碼技術(shù),local mongod本地mongo方案庞瘸,和最終確定的mongo service階段捧弃;
- 測(cè)試的數(shù)量很大,只運(yùn)行合適的測(cè)試可以有效加快效率擦囊;
Disadvantages:
- 測(cè)試急劇增多违霞,測(cè)試時(shí)間變得夸張的長(zhǎng),導(dǎo)致團(tuán)隊(duì)害怕push瞬场,害怕觸發(fā)測(cè)試买鸽;
- 測(cè)試不穩(wěn)定,漫長(zhǎng)的測(cè)試中一點(diǎn)出錯(cuò)贯被,全部測(cè)試都必須重跑眼五;
階段三:持續(xù)交付真方便
在“痛苦”的自動(dòng)化測(cè)試半小時(shí)之后妆艘,我們終于有了可交付的master代碼庫(kù),然后就是重啟服務(wù)弹砚,也就是重啟一下機(jī)器双仍,這很簡(jiǎn)單枢希。但是桌吃,如果想要增加一臺(tái)機(jī)器、刪除一臺(tái)機(jī)器就比較麻煩了苞轿。為了實(shí)現(xiàn)灰度發(fā)布茅诱,藍(lán)綠發(fā)布,我緊接著開(kāi)發(fā)了以docker為核心的部署系統(tǒng)搬卒。
deliverStage:
stage: deliver
script:
# 把舊的鏡像拉下來(lái)瑟俭,如果是第一次運(yùn)行(會(huì)拉不到就報(bào)錯(cuò))就直接true跳過(guò)
- docker pull ${OLD_IMAGE} || true
# 獲得sh文件的執(zhí)行權(quán)限,以便運(yùn)行鏡像是容器有權(quán)限執(zhí)行
- chmod a+x *.sh
# 打包可運(yùn)行鏡像
- docker build --pull --cache-from ${OLD_IMAGE} -t ${NEW_IMAGE} -f Dockerfile .
# 將鏡像推送至遠(yuǎn)端倉(cāng)庫(kù)
- docker push ${NEW_IMAGE}
only:
- master
tags:
- docker
# Dockerfile —— 打包可運(yùn)行服務(wù)的dockerfile
# 使用這個(gè)緩存鏡像作為源鏡像
FROM ${my-image-repo-url}/gitlab-ci-cache/my-project:cache
# 用gitlab runner上的最新代碼契邀,直接覆蓋摆寄,這樣鏡像就得到了最新代碼
COPY . $APP_HOME
# 覆蓋鏡像中的run.sh,鏡像將在啟動(dòng)最后運(yùn)行此文件
COPY run.sh /run.sh
無(wú)論是重啟還是新增坯门,都只需要new一個(gè)新的容器微饥,然后拉取可運(yùn)行的鏡像就可以了。
在學(xué)會(huì)了docker和gitlab CI/CD的流程之后古戴,我重構(gòu)了系統(tǒng)中的部分環(huán)節(jié)欠橘。
- 使用docker緩存python虛擬環(huán)境
docker系統(tǒng)的緩存層做的很棒,每執(zhí)行一條新的語(yǔ)句就會(huì)生成一個(gè)新的鏡像層现恼,如果語(yǔ)句沒(méi)有變化肃续,則會(huì)直接使用已有的緩存層來(lái)繼續(xù)執(zhí)行下一條語(yǔ)句。
這樣的話叉袍,如果我的Pipfile和Pipfile.lock(pipenv的依賴管理文件)沒(méi)有發(fā)生變動(dòng)始锚,就直接使用現(xiàn)有的虛擬環(huán)境包文件,可以極大的優(yōu)化pipeline的流程喳逛,極大的縮短pipeline的時(shí)長(zhǎng)疼蛾。
# Dockerfile.cache
# 環(huán)境緩存層的鏡像dockerfile
FROM ${my-image-repo-url}/centos_py3:latest
ENV APP_HOME /home/my-project/
# 這里將最新的Pipfile復(fù)制到鏡像中
COPY Pipfile $APP_HOME
COPY Pipfile.lock $APP_HOME
# 如果文件Pip依賴沒(méi)有變更,這里的RUN語(yǔ)句將直接用鏡像層艺配,不會(huì)重新運(yùn)行察郁,可以節(jié)省大量的時(shí)間
RUN cd $APP_HOME && \
pipenv install -d --skip-lock
- 提前打包鏡像,使用待發(fā)布的鏡像做測(cè)試
有了虛擬環(huán)境的緩存鏡像转唉,幾乎消除了為自動(dòng)化測(cè)試準(zhǔn)備環(huán)境的時(shí)間皮钠,這樣我就可以把測(cè)試分成很多小的runner job,就一舉解決了測(cè)試運(yùn)行時(shí)間過(guò)長(zhǎng)赠法、測(cè)試不穩(wěn)定的問(wèn)題麦轰。
cacheStage:
stage: cache
script:
- do cache with centos_py3 image
tags:
- docker
buildStage:
stage: build
script:
- do build with cache image
tags:
- docker
testStage-1:
stage: test
image:
name: ${BUILD_IMAGE}
services:
- name: mongo
alias: mongo
script:
- do test
tags:
- docker
testStage-2:
stage: test
image:
name: ${BUILD_IMAGE}
services:
- name: mongo
alias: mongo
script:
- do test
tags:
- docker
deliverStage:
stage: deliver
script:
- push build image to prod environment
tags:
- docker
做個(gè)小結(jié)
今年寫了幾篇文章乔夯,個(gè)人感覺(jué)是更偏技術(shù)了,但是閱讀者了了款侵,甚至投稿審核都難以通過(guò)末荐,還是比較令人受挫的。(可能我并非科班出身新锈,無(wú)論是技術(shù)重點(diǎn)和寫作重點(diǎn)甲脏,都需要更多的學(xué)習(xí)。)
這篇文章也只是一個(gè)方向的探索妹笆、總結(jié)和分享块请,具體要落地,需要有完備的容器化和docker技術(shù)拳缠,需要深入了解gitlab.yml和dockerfile的語(yǔ)法知識(shí)墩新,需要團(tuán)隊(duì)的支持和時(shí)間的投入,當(dāng)然結(jié)果也是顯而易見(jiàn)的窟坐,會(huì)極大的優(yōu)化團(tuán)隊(duì)開(kāi)發(fā)效率海渊,加快敏捷團(tuán)隊(duì)的迭代速度。
最后鼓勵(lì)一下自己哲鸳,哪怕看官不多臣疑,但是學(xué)習(xí)和總結(jié)的過(guò)程已經(jīng)令人愉悅了,下面幾期講講數(shù)據(jù)結(jié)構(gòu)和基礎(chǔ)算法的學(xué)習(xí)吧帕胆。
既然這可能是全棧的最后一篇文章朝捆,那么我也做個(gè)鏈接吧。
- 如何轉(zhuǎn)行做一名程序員懒豹? How to build?
- 全棧的概念芙盘。 從零開(kāi)始的異世界python全棧
- 人生苦短,我選Python脸秽。 后端演進(jìn) —— python生態(tài)
- 不會(huì)寫前端的后端不是好架構(gòu)儒老。 前端淺析 —— Vue的妙用
- 持續(xù)交付,你值得擁有记餐。 運(yùn)維基礎(chǔ) —— 派普蘭實(shí)踐
歡迎大家關(guān)注驮樊、點(diǎn)贊,也非常希望讀者能說(shuō)出自己關(guān)心的內(nèi)容片酝,共同進(jìn)步囚衔,共同成長(zhǎng)!