成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

GitLab CI/CD 在 Node.js 項(xiàng)目中的實(shí)踐

Profeel / 2584人閱讀

摘要:近期在按照業(yè)務(wù)劃分項(xiàng)目時(shí),我們組被分了好多的項(xiàng)目過來,大量的是基于的,也是我們組持續(xù)在使用的語言。部署環(huán)境強(qiáng)依賴本地,因?yàn)樾枰诒镜亟}庫的臨時(shí)目錄,并經(jīng)過多次的方式完成部署上線的操作。

近期在按照業(yè)務(wù)劃分項(xiàng)目時(shí),我們組被分了好多的項(xiàng)目過來,大量的是基于 Node.js 的,也是我們組持續(xù)在使用的語言。
現(xiàn)有流程中的一些問題

在維護(hù)多個(gè)項(xiàng)目的時(shí)候,會(huì)暴露出一些問題:

如何有效的使用 測(cè)試用例

如何有效的使用 ESLint

部署上線還能再快一些嗎

使用了 TypeScript 以后帶來的額外成本

測(cè)試用例

首先是測(cè)試用例,最初我們?cè)O(shè)計(jì)在了 git hooks 里邊,在執(zhí)行 git commit 之前會(huì)進(jìn)行檢查,在本地運(yùn)行測(cè)試用例。
這會(huì)帶來一個(gè)時(shí)間上的問題,如果是日常開發(fā),這么操作還是沒什么問題的,但如果是線上 bug 修復(fù),執(zhí)行測(cè)試用例的時(shí)間依據(jù)項(xiàng)目大小可能會(huì)持續(xù)幾分鐘。
而為了修復(fù) bug,可能會(huì)采用 commit 的時(shí)候添加 -n 選項(xiàng)來跳過 hooks ,在修復(fù) bug 時(shí)這么做無可厚非,但是即使大家在日常開發(fā)中都采用commit -n 的方式來跳過繁瑣的測(cè)試過程,這個(gè)也是沒有辦法管控的,畢竟是在本地做的這個(gè)校驗(yàn),是否遵循這個(gè)規(guī)則,全靠大家自覺。

所以一段時(shí)間后發(fā)現(xiàn),通過這種方式執(zhí)行測(cè)試用例來規(guī)避一些風(fēng)險(xiǎn)的作用可能并不是很有效。

ESLint

然后就是 ESLint,我們團(tuán)隊(duì)基于airbnb的 ESLint 規(guī)則自定義了一套更符合團(tuán)隊(duì)習(xí)慣的規(guī)則,我們會(huì)在編輯器中引入插件用來幫助高亮一些錯(cuò)誤,以及進(jìn)行一些自動(dòng)格式化的操作。
同時(shí)我們也在 git hooks 中添加了對(duì)應(yīng)的處理,也是在 git commit 的時(shí)候進(jìn)行檢查,如果不符合規(guī)范則不允許提交。
不過這個(gè)與測(cè)試用例是相同的問題:

編輯器是否安裝 ESLint 插件無從得知,即使安裝插件、是否人肉忽略錯(cuò)誤提示也無從得知。

git hooks 可以被繞過

部署上線的方式

之前團(tuán)隊(duì)的部署上線是使用shipit周邊套件進(jìn)行部署的。
部署環(huán)境強(qiáng)依賴本地,因?yàn)樾枰诒镜亟}庫的臨時(shí)目錄,并經(jīng)過多次ssh XXX "command"的方式完成 部署 + 上線 的操作。
shipit提供了一個(gè)有效的回滾方案,就是在部署后的路徑添加多個(gè)歷史部署版本的記錄,回滾時(shí)將當(dāng)前運(yùn)行的項(xiàng)目目錄指向之前的某個(gè)版本即可。_不過有一點(diǎn)兒坑的是,很難去選擇我要回滾到那個(gè)節(jié)點(diǎn),以及保存歷史記錄需要占用額外的磁盤空間_
不過正因?yàn)槿绱耍?b>shipit在部署多臺(tái)服務(wù)器時(shí)會(huì)遇到一些令人不太舒服的地方。

如果是多臺(tái)新增的服務(wù)器,那么可以通過在shipit配置文件中傳入多個(gè)目標(biāo)服務(wù)器地址來進(jìn)行批量部署。
但是假設(shè)某天需要上線一些小流量(比如四臺(tái)機(jī)器中的一臺(tái)),因?yàn)榍斑吿岬降?b>shipit回滾策略,這會(huì)導(dǎo)致單臺(tái)機(jī)器與其他三臺(tái)機(jī)器的歷史版本時(shí)間戳不一致(因?yàn)檫@幾臺(tái)機(jī)器不是同一時(shí)間上線的)
提到了這個(gè)時(shí)間戳就另外提一嘴,這個(gè)時(shí)間戳的生成是基于執(zhí)行上線操作的那臺(tái)機(jī)器的本地時(shí)間,之前有遇到過同事在本地測(cè)試代碼,將時(shí)間調(diào)整為了幾天前的時(shí)間,后時(shí)間沒有改回正確的時(shí)間時(shí)進(jìn)行了一次部署操作,代碼出現(xiàn)問題后卻發(fā)現(xiàn)回滾失敗了,原因是該同事部署的版本時(shí)間戳太小,shipit 找不到之前的版本(shipit 可以設(shè)置保留歷史版本的數(shù)量,當(dāng)時(shí)最早的一次時(shí)間戳也是大于本次出問題的時(shí)間戳的)

也就是說,哪怕有一次進(jìn)行過小流量上線,那么以后就用不了批量上線的功能了 (沒有去仔細(xì)研究shipit官方文檔,不知道會(huì)不會(huì)有類似--force之類的忽略歷史版本的操作)

基于上述的情況,我們的部署上線耗時(shí)變?yōu)榱耍?(__機(jī)器數(shù)量__)X(__基于本地網(wǎng)速的倉庫克隆、多次 ssh 操作的耗時(shí)總和__)。 P.S. 為了保證倉庫的有效性,每次執(zhí)行 shipit 部署,它都會(huì)刪除之前的副本,重新克隆

尤其是服務(wù)端項(xiàng)目,有時(shí)緊急的 bug 修復(fù)可能是在非工作時(shí)間,這意味著可能當(dāng)時(shí)你所處的網(wǎng)絡(luò)環(huán)境并不是很穩(wěn)定。
我曾經(jīng)晚上接到過同事的微信,讓我?guī)退暇€項(xiàng)目,他家的 Wi-Fi 是某博士的,下載項(xiàng)目依賴的時(shí)候出了些問題。
還有過使用移動(dòng)設(shè)備開熱點(diǎn)的方式進(jìn)行上線操作,有一次非前后分離的項(xiàng)目上線后,直接就收到了聯(lián)通的短信:「您本月流量已超出XXX」(當(dāng)時(shí)還在用合約套餐,一月就800M流量)。

TypeScript

在去年下半年開始,我們團(tuán)隊(duì)就一直在推動(dòng) TypeScript 的應(yīng)用,因?yàn)樵诖笮晚?xiàng)目中,擁有明確類型的 TypeScript 顯然維護(hù)性會(huì)更高一些。
但是大家都知道的, TypeScript 最終需要編譯轉(zhuǎn)換為 JavaScript(也有 tsc 那種的不生成 JS 文件,直接運(yùn)行,不過這個(gè)更多的是在本地開發(fā)時(shí)使用,線上代碼的運(yùn)行我們還是希望變量越少越好)。

所以之前的上線流程還需要額外的增加一步,編譯 TS。
而且因?yàn)?b>shipit是在本地克隆的倉庫并完成部署的,所以這就意味著我們必須要把生成后的 JS 文件也放入到倉庫中,最直觀的,從倉庫的概覽上看著就很丑(50% TS、50% JS),同時(shí)這進(jìn)一步增加了上線的成本。

總結(jié)來說,現(xiàn)有的部署上線流程過于依賴本地環(huán)境,因?yàn)槊總€(gè)人的環(huán)境不同,這相當(dāng)于給部署流程增加了很多不可控因素。
如何解決這些問題

上邊我們所遇到的一些問題,其實(shí)可以分為兩塊:

有效的約束代碼質(zhì)量

快速的部署上線

所以我們就開始尋找解決方案,因?yàn)槲覀兊脑创a是使用自建的 GitLab 倉庫來進(jìn)行管理的,首先就找到了 GitLab CI/CD。
在研究了一番文檔以后發(fā)現(xiàn),它能夠很好的解決我們現(xiàn)在遇到的這些問題。

要使用 GitLab CI/CD 是非常簡(jiǎn)單的,只需要額外的使用一臺(tái)服務(wù)器安裝 gitlab-runner,并將要使用 CI/CD 的項(xiàng)目注冊(cè)到該服務(wù)上就可以了。
GitLab 官方文檔中有非常詳細(xì)的安裝注冊(cè)流程:

install | runner
register | runner
group register | repo 注冊(cè) Group 項(xiàng)目時(shí)的一些操作

上邊的注冊(cè)選擇的是注冊(cè) group ,也就是整個(gè) GitLab 某個(gè)分組下所有的項(xiàng)目。  
主要目的是因?yàn)槲覀冞@邊項(xiàng)目數(shù)量太多,單個(gè)注冊(cè)太過繁瑣(還要登錄到 runner 服務(wù)器去執(zhí)行命令才能夠注冊(cè))
安裝時(shí)需要注意的地方
官網(wǎng)的流程已經(jīng)很詳細(xì)了,不過還是有一些地方可以做一些小提示,避免踩坑
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner

這是 Linux 版本的安裝命令,安裝需要 root (管理員) 權(quán)限,后邊跟的兩個(gè)參數(shù):

--userCI/CD 執(zhí)行 job (后續(xù)所有的流程都是基于 job 的)時(shí)所使用的用戶名

--working-directoryCI/CD 執(zhí)行時(shí)的根目錄路徑 個(gè)人的踩坑經(jīng)驗(yàn)是將目錄設(shè)置為一個(gè)空間大的磁盤上,因?yàn)?CI/CD 會(huì)生成大量的文件,尤其是如果使用 CI/CD 進(jìn)行編譯 TS 文件并且將其生成后的 JS 文件緩存;這樣的操作會(huì)導(dǎo)致 innode 不足產(chǎn)生一些問題

--user 的意思就是 CI/CD 執(zhí)行使用該用戶進(jìn)行執(zhí)行,所以如果要編寫腳本之類的,建議在該用戶登錄的狀態(tài)下編寫,避免出現(xiàn)無權(quán)限執(zhí)行 sudo su gitlab-runner
注冊(cè)時(shí)需要注意的地方

在按照官網(wǎng)的流程執(zhí)行時(shí),我們的 tag 是留空的,暫時(shí)沒有找到什么用途。。
以及 executor 這個(gè)比較重要了,因?yàn)槲覀兪菑氖謩?dòng)部署上線還是往這邊靠攏的,所以穩(wěn)妥的方式是一步步來,也就是說我們選擇的是 shell ,最常規(guī)的一種執(zhí)行方式,對(duì)項(xiàng)目的影響也是比較小的(官網(wǎng)示例給的是 docker

.gitlab-ci.yml 配置文件

上邊的環(huán)境已經(jīng)全部裝好了,接下來就是需要讓 CI/CD 真正的跑起來
runner 以哪種方式運(yùn)行,就靠這個(gè)配置文件來描述了,按照約定需要將文件放置到 repo 倉庫的根路徑下。
當(dāng)該文件存在于倉庫中,執(zhí)行 git push 命令后就會(huì)自動(dòng)按照配置文件中所描述的動(dòng)作進(jìn)行執(zhí)行了。

quick start

configuration

上邊的兩個(gè)鏈接里邊信息非常完整,包含各種可以配置的選項(xiàng)。

一般來講,配置文件的結(jié)構(gòu)是這樣的:

stages:
  - stage1
  - stage2
  - stage3

job 1:
  stage: stage1
  script: echo job1

job 2:
  stage: stage2
  script: echo job2

job 3:
  stage: stage2
  script:
    - echo job3-1
    - echo job3-2

job 4:
  stage: stage3
  script: echo job4

stages 用來聲明有效的可被執(zhí)行的 stage,按照聲明的順序執(zhí)行。
下邊的那些 job XXX 名字不重要,這個(gè)名字是在 GitLab CI/CD Pipeline 界面上展示時(shí)使用的,重要的是那個(gè) stage 屬性,他用來指定當(dāng)前的這一塊 job 隸屬于哪個(gè) stage。
script 則是具體執(zhí)行的腳本內(nèi)容,如果要執(zhí)行多行命令,就像job 3那種寫法就好了。

如果我們將上述的 stagejob 之類的換成我們項(xiàng)目中的一些操作install_dependencies、test、eslint之類的,然后將script字段中的值換成類似npx eslint之類的,當(dāng)你把這個(gè)文件推送到遠(yuǎn)端服務(wù)器后,你的項(xiàng)目就已經(jīng)開始自動(dòng)運(yùn)行這些腳本了。
并且可以在Pipelines界面看到每一步執(zhí)行的狀態(tài)。

P.S. 默認(rèn)情況下,上一個(gè) stage 沒有執(zhí)行完時(shí)不會(huì)執(zhí)行下一個(gè) stage 的,不過也可以通過額外的配置來修改:  
allow failure
when
設(shè)置僅在特定的情況下觸發(fā) CI/CD

上邊的配置文件存在一個(gè)問題,因?yàn)樵谂渲梦募胁]有指定哪些分支的提交會(huì)觸發(fā) CI/CD 流程,所以默認(rèn)的所有分支上的提交都會(huì)觸發(fā),這必然不是我們想要的結(jié)果。
CI/CD 的執(zhí)行會(huì)占用系統(tǒng)的資源,如果因?yàn)橐恍╅_發(fā)分支的執(zhí)行影響到了主干分支的執(zhí)行,這是一件得不償失的事情。

所以我們需要限定哪些分支才會(huì)觸發(fā)這些流程,也就是要用到了配置中的 only 屬性。

使用only可以用來設(shè)置哪些情況才會(huì)觸發(fā) CI/CD,一般我們這邊常用的就是用來指定分支,這個(gè)是要寫在具體的 job 上的,也就是大致是這樣的操作:

具體的配置文檔
job 1:
  stage: stage1
  script: echo job1
  only:
    - master
    - dev

單個(gè)的配置是可以這樣寫的,不過如果 job 的數(shù)量變多,這么寫就意味著我們需要在配置文件中大量的重復(fù)這幾行代碼,也不是一個(gè)很好看的事情。
所以這里可能會(huì)用到一個(gè)yaml的語法:

這是一步可選的操作,只是想在配置文件中減少一些重復(fù)代碼的出現(xiàn)
.access_branch_template: &access_branch
  only:
    - master
    - dev

job 1:
  <<: *access_branch
  stage: stage1
  script: echo job1

job 2:
  <<: *access_branch
  stage: stage2
  script: echo job2

一個(gè)類似模版繼承的操作,官方文檔中也沒有提到,這個(gè)只是一個(gè)減少冗余代碼的方式,可有可無。

緩存必要的文件

因?yàn)槟J(rèn)情況下,CI/CD在執(zhí)行每一步(job)時(shí)都會(huì)清理一下當(dāng)前的工作目錄,保證工作目錄是干凈的、不包含一些之前任務(wù)留下的數(shù)據(jù)、文件。
不過這在我們的 Node.js 項(xiàng)目中就會(huì)帶來一個(gè)問題。
因?yàn)槲覀兊?ESLint、單元測(cè)試 都是基于 node_modules 下邊的各種依賴來執(zhí)行的。
而目前的情況就相當(dāng)于我們每一步都需要執(zhí)行npm install,這顯然是一個(gè)不必要的浪費(fèi)。

所以就提到了另一個(gè)配置文件中的選項(xiàng):cache

用來指定某些文件、文件夾是需要被緩存的,而不能清除:

cache:
  key: ${CI_BUILD_REF_NAME}
  paths:
    - node_modules/

大致是這樣的一個(gè)操作,CI_BUILD_REF_NAME是一個(gè) CI/CD 提供的環(huán)境變量,該變量的內(nèi)容為執(zhí)行 CI/CD 時(shí)所使用的分支名,通過這種方式讓兩個(gè)分支之間的緩存互不影響。

部署項(xiàng)目

如果基于上邊的一些配置,我們將 單元測(cè)試、ESLint 對(duì)應(yīng)的腳本放進(jìn)去,他就已經(jīng)能夠完成我們想要的結(jié)果了,如果某一步執(zhí)行出錯(cuò),那么任務(wù)就會(huì)停在那里不會(huì)繼續(xù)向后執(zhí)行。
不過目前來看,后邊已經(jīng)沒有多余的任務(wù)供我們執(zhí)行了,所以是時(shí)候?qū)?部署 這一步操作接過來了。

部署的話,我們目前選擇的是通過 rsync 來進(jìn)行同步多臺(tái)服務(wù)器上的數(shù)據(jù),一個(gè)比較簡(jiǎn)單高效的部署方式。

P.S. 部署需要額外的做一件事情,就是建立從gitlab runner所在機(jī)器gitlab-runner用戶到目標(biāo)部署服務(wù)器對(duì)應(yīng)用戶下的機(jī)器信任關(guān)系。  
有 N 多種方法可以實(shí)現(xiàn),最簡(jiǎn)單的就是在runner機(jī)器上執(zhí)行 ssh-copy-id 將公鑰寫入到目標(biāo)機(jī)器。
或者可以像我一樣,提前將 runner 機(jī)器的公鑰拿出來,需要與機(jī)器建立信任關(guān)系時(shí)就將這個(gè)字符串寫入到目標(biāo)機(jī)器的配置文件中。
類似這樣的操作:ssh 10.0.0.1 "echo "XXX" >> ~/.ssh/authorized_keys"

大致的配置如下:

variables:
  DEPLOY_TO: /home/XXX/repo # 要部署的目標(biāo)服務(wù)器項(xiàng)目路徑
deploy:
  stage: deploy
  script:
    - rsync -e "ssh -o StrictHostKeyChecking=no" -arc --exclude-from="./exclude.list" --delete . 10.0.0.1:$DEPLOY_TO
    - ssh 10.0.0.1 "cd $DEPLOY_TO; npm i --only=production"
    - ssh 10.0.0.1 "pm2 start $DEPLOY_TO/pm2/$CI_ENVIRONMENT_NAME.json;"
同時(shí)用到的還有variables,用來提出一些變量,在下邊使用。

ssh 10.0.0.1 "pm2 start $DEPLOY_TO/pm2/$CI_ENVIRONMENT_NAME.json;",這行腳本的用途就是重啟服務(wù)了,我們使用pm2來管理進(jìn)程,默認(rèn)的約定項(xiàng)目路徑下的pm2文件夾存放著個(gè)個(gè)環(huán)境啟動(dòng)時(shí)所需的參數(shù)。

當(dāng)然了,目前我們?cè)谟玫臎]有這么簡(jiǎn)單,下邊會(huì)統(tǒng)一提到

并且在部署的這一步,我們會(huì)有一些額外的處理

這是比較重要的一點(diǎn),因?yàn)槲覀兛赡軙?huì)更想要對(duì)上線的時(shí)機(jī)有主動(dòng)權(quán),所以 deploy 的任務(wù)并不是自動(dòng)執(zhí)行的,我們會(huì)將其修改為手動(dòng)操作還會(huì)觸發(fā),這用到了另一個(gè)配置參數(shù):

deploy:
  stage: deploy
  script: XXX
  when: manual  # 設(shè)置該任務(wù)只能通過手動(dòng)觸發(fā)的方式運(yùn)行

當(dāng)然了,如果不需要,這個(gè)移除就好了,比如說我們?cè)跍y(cè)試環(huán)境就沒有配置這個(gè)選項(xiàng),僅在線上環(huán)境使用了這樣的操作

更方便的管理 CI/CD 流程

如果按照上述的配置文件進(jìn)行編寫,實(shí)際上已經(jīng)有了一個(gè)可用的、包含完整流程的 CI/CD 操作了。

不過它的維護(hù)性并不是很高,尤其是如果 CI/CD 被應(yīng)用在多個(gè)項(xiàng)目中,想做出某項(xiàng)改動(dòng)則意味著所有的項(xiàng)目都需要重新修改配置文件并上傳到倉庫中才能生效。

所以我們選擇了一個(gè)更靈活的方式,最終我們的 CI/CD 配置文件是大致這樣子的(省略了部分不相干的配置):

variables:
  SCRIPTS_STORAGE: /home/gitlab-runner/runner-scripts
  DEPLOY_TO: /home/XXX/repo # 要部署的目標(biāo)服務(wù)器項(xiàng)目路徑

stages:
  - install
  - test
  - build
  - deploy_development
  - deploy_production

install_dependencies:
  stage: install
  script: bash $SCRIPTS_STORAGE/install.sh

unit_test:
  stage: test
  script: bash $SCRIPTS_STORAGE/test.sh

eslint:
  stage: test
  script: bash $SCRIPTS_STORAGE/eslint.sh

# 編譯 TS 文件
build:
  stage: build
  script: bash $SCRIPTS_STORAGE/build.sh

deploy_development:
  stage: deploy_development
  script: bash $SCRIPTS_STORAGE/deploy.sh 10.0.0.1
  only: dev     # 多帶帶指定生效分支

deploy_production:
  stage: deploy_production
  script: bash $SCRIPTS_STORAGE/deploy.sh 10.0.0.2
  only: master  # 多帶帶指定生效分支

我們將每一步 CI/CD 所需要執(zhí)行的腳本都放到了 runner 那臺(tái)服務(wù)器上,在配置文件中只是執(zhí)行了那個(gè)腳本文件。
這樣當(dāng)我們有什么策略上的調(diào)整,比如說 ESLint 規(guī)則的變更、部署方式之類的。
這些都完全與項(xiàng)目之間進(jìn)行解耦,后續(xù)的操作基本都不會(huì)讓正在使用 CI/CD 的項(xiàng)目重新修改才能夠支持(部分需要新增環(huán)境變量的導(dǎo)入之類的確實(shí)需要項(xiàng)目的支持)。

接入釘釘通知

實(shí)際上,當(dāng) CI/CD 執(zhí)行成功或者失敗,我們可以在 Pipeline 頁面中看到,也可以設(shè)置一些郵件通知,但這些都不是時(shí)效性很強(qiáng)的。
鑒于我們目前在使用釘釘進(jìn)行工作溝通,所以就研究了一波釘釘機(jī)器人。
發(fā)現(xiàn)有支持 GitLab 機(jī)器人,不過功能并不適用,只能處理一些 issues 之類的, CI/CD 的一些通知是缺失的,所以只好自己基于釘釘?shù)南⒛0鎸?shí)現(xiàn)一下了。

因?yàn)樯线呂覀円呀?jīng)將各個(gè)步驟的操作封裝了起來,所以這個(gè)修改對(duì)同事們是無感知的,我們只需要修改對(duì)應(yīng)的腳本文件,添加釘釘?shù)南嚓P(guān)操作即可完成,封裝了一個(gè)簡(jiǎn)單的函數(shù):

function sendDingText() {
  local text="$1"

  curl -X POST "$DINGTALK_HOOKS_URL" 
  -H "Content-Type: application/json" 
  -d "{
    "msgtype": "text",
    "text": {
        "content": """$text"""
    }
  }"
}

# 具體發(fā)送時(shí)傳入的參數(shù)
sendDingText "proj: $CI_PROJECT_NAME[$CI_JOB_NAME]
env: $CI_ENVIRONMENT_NAME
deploy success
$CI_PIPELINE_URL
created by: $GITLAB_USER_NAME
message: $CI_COMMIT_MESSAGE"

# 某些 case 失敗的情況下 是否需要更多的信息就看自己自定義咯
sendDingText "error: $CI_PROJECT_NAME[$CI_JOB_NAME]
env: $CI_ENVIRONMENT_NAME"

上述用到的環(huán)境變量,除了DINGTALK_HOOKS_URL是我們自定義的機(jī)器人通知地址以外,其他的變量都是有 GitLab runenr所提供的。

各種變量可以從這里找到:predefined variables

回滾處理

聊完了正常的流程,那么也該提一下出問題時(shí)候的操作了。
人非圣賢孰能無過,很有可能某次上線一些沒有考慮到的地方就會(huì)導(dǎo)致服務(wù)出現(xiàn)異常,這時(shí)候首要任務(wù)就是讓用戶還可以照常訪問,所以我們會(huì)選擇回滾到上一個(gè)有效的版本去。
在項(xiàng)目中的 Pipeline 頁面 或者 Enviroment 頁面(這個(gè)需要在配置文件中某些 job 中手動(dòng)添加這個(gè)屬性,一般會(huì)寫在 deploy 的那一步去),可以在頁面上選擇想要回滾的節(jié)點(diǎn),然后重新執(zhí)行 CI/CD 任務(wù),即可完成回滾。

不過這在 TypeScript 項(xiàng)目中會(huì)有一些問題,因?yàn)槲覀兓貪L一般來講是重新執(zhí)行上一個(gè)版本 CI/CD 中的 deploy 任務(wù),在 TS 項(xiàng)目中,我們?cè)?runner 中緩存了 TS 轉(zhuǎn)換 JS 之后的 dist 文件夾,并且部署的時(shí)候也是直接將該文件夾推送到服務(wù)器的(TS項(xiàng)目的源碼就沒有再往服務(wù)器上推過了)。

而如果我們直接點(diǎn)擊 retry 就會(huì)帶來一個(gè)問題,因?yàn)槲覀兊?dist 文件夾是緩存的,而 deploy 并不會(huì)管這種事兒,他只會(huì)把對(duì)應(yīng)的要推送的文件發(fā)送到服務(wù)器上,并重啟服務(wù)。

而實(shí)際上 dist 還是最后一次(也就是出錯(cuò)的那次)編譯出來的 JS 文件,所以解決這個(gè)問題有兩種方法:

deploy 之前執(zhí)行一下 build

deploy 的時(shí)候進(jìn)行判斷

第一個(gè)方案肯定是不可行的,因?yàn)閲?yán)重依賴于操作上線的人是否知道有這個(gè)流程。
所以我們主要是通過第二種方案來解決這個(gè)問題。

我們需要讓腳本在執(zhí)行的時(shí)候知道,dist 文件夾里邊的內(nèi)容是不是自己想要的。
所以就需要有一個(gè) __標(biāo)識(shí)__,而做這個(gè)標(biāo)識(shí)最簡(jiǎn)單有效唾手可得的就是,git commit id。
每一個(gè) commit 都會(huì)有一個(gè)唯一的標(biāo)識(shí)符號(hào),而且我們的 CI/CD 執(zhí)行也是依靠于新代碼的提交(也就意味著一定有 commit)。
所以我們?cè)?build 環(huán)節(jié)將當(dāng)前的commit id也緩存了下來:

git rev-parse --short HEAD > git_version

同時(shí)在 deploy 腳本中添加額外的判斷邏輯:

currentVersion=`git rev-parse --short HEAD`
tagVersion=`touch git_version; cat git_version`

if [ "$currentVersion" = "$tagVersion" ]
then
    echo "git version match"
else
    echo "git version not match, rebuild dist"
    bash ~/runner-scripts/build.sh  # 額外的執(zhí)行 build 腳本
fi

這樣一來,就避免了回滾時(shí)還是部署了錯(cuò)誤代碼的風(fēng)險(xiǎn)。

關(guān)于為什么不將 build 這一步操作與 deploy 合并的原因是這樣的:  
因?yàn)槲覀儠?huì)有很多臺(tái)機(jī)器,同時(shí) job 會(huì)寫很多個(gè),類似 deploy_1、deploy_2deploy_all,如果我們將 build 的這一步放到 deploy
那就意味著我們每次 deploy,即使是一次部署,但因?yàn)槲覀冞x擇一臺(tái)臺(tái)機(jī)器多帶帶操作,它也會(huì)重新生成多次,這也會(huì)帶來額外的時(shí)間成本
hot fix 的處理

CI/CD 運(yùn)行了一段時(shí)間后,我們發(fā)現(xiàn)偶爾解決線上 bug 還是會(huì)比較慢,因?yàn)槲覀兲峤淮a后要等待完整的 CI/CD 流程走完。
所以在研究后我們決定,針對(duì)某些特定情況hot fix,我們需要跳過ESLint、單元測(cè)試這些流程,快速的修復(fù)代碼并完成上線。

CI/CD 提供了針對(duì)某些 Tag 可以進(jìn)行不同的操作,不過我并不想這么搞了,原因有兩點(diǎn):

這需要修改配置文件(所有項(xiàng)目)

這需要開發(fā)人員熟悉對(duì)應(yīng)的規(guī)則(打 Tag

所以我們采用了另一種取巧的方式來實(shí)現(xiàn),因?yàn)槲覀兊姆种Ф际侵唤邮?b>Merge Request那種方式上線的,所以他們的commit title實(shí)際上是固定的:Merge branch "XXX"。
同時(shí) CI/CD 會(huì)有環(huán)境變量告訴我們當(dāng)前執(zhí)行 CI/CDcommit message
我們通過匹配這個(gè)字符串來檢查是否符合某種規(guī)則來決定是否跳過這些job

function checkHotFix() {
  local count=`echo $CI_COMMIT_TITLE | grep -E "^Merge branch "(hot)?fix/w+" | wc -l`

  if [ $count -eq 0 ]
  then
    return 0
  else
    return 1
  fi
}

# 使用方法

checkHotFix

if [ $? -eq 0 ]
then
  echo "start eslint"
  npx eslint --ext .js,.ts .
else
  # 跳過該步驟
  echo "match hotfix, ignore eslint"
fi

這樣能夠保證如果我們的分支名為 hotfix/XXX 或者 fix/XXX 在進(jìn)行代碼合并時(shí), CI/CD 會(huì)跳過多余的代碼檢查,直接進(jìn)行部署上線。 沒有跳過安裝依賴的那一步,因?yàn)?TS 編譯還是需要這些工具的

小結(jié)

目前團(tuán)隊(duì)已經(jīng)有超過一半的項(xiàng)目接入了 CI/CD 流程,為了方便同事接入(主要是編輯 .gitlab-ci.yml 文件),我們還提供了一個(gè)腳手架用于快速生成配置文件(包括自動(dòng)建立機(jī)器之間的信任關(guān)系)。

相較之前,部署的速度明顯的有提升,并且不再對(duì)本地網(wǎng)絡(luò)有各種依賴,只要是能夠?qū)⒋a push 到遠(yuǎn)程倉庫中,后續(xù)的事情就和自己沒有什么關(guān)系了,并且可以方便的進(jìn)行小流量上線(部署單臺(tái)驗(yàn)證有效性)。

以及在回滾方面則是更靈活了一些,可在多個(gè)版本之間快速切換,并且通過界面的方式,操作起來也更加直觀。

最終可以說,如果沒有 CI/CD,實(shí)際上開發(fā)模式也是可以忍受的,不過當(dāng)使用了 CI/CD 以后,再去使用之前的部署方式,則會(huì)明顯的感覺到不舒適。(沒有對(duì)比,就沒有傷害

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/104282.html

相關(guān)文章

  • Kubernetes如何加速UCloud內(nèi)部代碼部署的CI/CD流程

    摘要:內(nèi)部長期使用來管理代碼。審核通過并且成功后,觸發(fā)靜態(tài)測(cè)試單元測(cè)試鏡像構(gòu)建鏡像部署集成測(cè)試等測(cè)試通過后,創(chuàng)建一個(gè)從到的,由負(fù)責(zé)人進(jìn)行審核。從圖中我們可以看到,部分是一個(gè)單元測(cè)試,預(yù)發(fā)布部署,集成測(cè)試,,提交代碼的循環(huán)過程。UCloud內(nèi)部長期使用 Gitlab 來管理代碼。雖然Gitlab作為一套開源平臺(tái)已很優(yōu)秀,但我們對(duì)于其能為CI/CD提供的敏捷性并不十分滿意,內(nèi)部實(shí)踐中的代碼發(fā)布周期仍需...

    gougoujiang 評(píng)論0 收藏0
  • Node項(xiàng)目Gitlab自動(dòng)部署實(shí)踐(基于Docker)

    摘要:只要的項(xiàng)目有提交,相關(guān)就根據(jù)來決定是否跑自動(dòng)部署的命令。項(xiàng)目的自動(dòng)部署添加執(zhí)行的注冊(cè)命令,按照說明進(jìn)行參數(shù)配置。至此,和服務(wù)都已經(jīng)自動(dòng)部署完成。 準(zhǔn)備工作 說明 公司最近準(zhǔn)備了一臺(tái)新的開發(fā)服務(wù)器,正好用以實(shí)踐docker的基本應(yīng)用。docker的好處不再贅述,詳情可參考阮一峰的這篇入門。(關(guān)于Docker最好的中文介紹,沒有之一)。 公司目前主要使用了EggJs + ReactJS的技...

    oysun 評(píng)論0 收藏0
  • 超長干貨:基于Docker的DevOps CI/CD實(shí)踐——來自iHealth的分享

    摘要:在貓屎氤氳的霧氣里角仰望天花板,手機(jī)微信提醒這次構(gòu)建成功或失敗,并附帶污言穢語。這時(shí)他可以開始往工位走,坐下時(shí),微信又會(huì)提醒本次部署到成功或失敗。與企業(yè)微信的集成在決定使用之前,需要知道的是,是一個(gè)高度依賴社區(qū)的項(xiàng)目。 前言 相信我,一切事情的發(fā)生都是趕鴨子上架,沒有例外。人類所有偉大的變革都是迫不得已,可又是那么順其自然。比如容器(docker)技術(shù)的誕生,比如箭在弦上的創(chuàng)業(yè),比如野...

    Dongjie_Liu 評(píng)論0 收藏0
  • 容器環(huán)境下的持續(xù)集成最佳實(shí)踐:構(gòu)建基于 Drone + GitFlow + K8s 的云原生語義化

    摘要:集成測(cè)試完成后,由運(yùn)維同學(xué)從發(fā)起一個(gè)到分支,此時(shí)會(huì)會(huì)運(yùn)行單元測(cè)試,構(gòu)建鏡像,并發(fā)布到預(yù)發(fā)布環(huán)境測(cè)試人員在預(yù)發(fā)布環(huán)境下再次驗(yàn)證功能,團(tuán)隊(duì)做上線前的其他準(zhǔn)備工作運(yùn)維同學(xué)合并,將為本次發(fā)布的代碼及鏡像自動(dòng)打上版本號(hào)并書寫,同時(shí)發(fā)布到生產(chǎn)環(huán)境。 云原生 (Cloud Native) 是伴隨的容器技術(shù)發(fā)展出現(xiàn)的的一個(gè)詞,最早出自 Pivotal 公司(即開發(fā)了 Spring 的公司)的一本技術(shù)小...

    asoren 評(píng)論0 收藏0
  • 容器環(huán)境下的持續(xù)集成最佳實(shí)踐:構(gòu)建基于 Drone + GitFlow + K8s 的云原生語義化

    摘要:集成測(cè)試完成后,由運(yùn)維同學(xué)從發(fā)起一個(gè)到分支,此時(shí)會(huì)會(huì)運(yùn)行單元測(cè)試,構(gòu)建鏡像,并發(fā)布到預(yù)發(fā)布環(huán)境測(cè)試人員在預(yù)發(fā)布環(huán)境下再次驗(yàn)證功能,團(tuán)隊(duì)做上線前的其他準(zhǔn)備工作運(yùn)維同學(xué)合并,將為本次發(fā)布的代碼及鏡像自動(dòng)打上版本號(hào)并書寫,同時(shí)發(fā)布到生產(chǎn)環(huán)境。 云原生 (Cloud Native) 是伴隨的容器技術(shù)發(fā)展出現(xiàn)的的一個(gè)詞,最早出自 Pivotal 公司(即開發(fā)了 Spring 的公司)的一本技術(shù)小...

    DevTalking 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<