Git Hooks、GitLab CI持續集成以及使用Jenkins實現自動化任務
前言
在一個共享項目(或者說多人協同開發的項目)的開發過程中,為有效確保團隊成員編碼風格的統一,確保部署方式的統一,等等(git的用戶經常會涉及到此類場景),常常會使用類似 Git Flow 這種比較復雜的工作流開發模式。在較大型的項目中,雖然這種工作流模式比較成熟,但在分支處理方面,這種工作流就會造成較多的重復勞動。
因此,如果能借助某些工具來自動化處理這些重復性事務,比如自動合並分支,那么對於提升我們的工作效率,將會有很大的幫助。
本文將從以下三種方法對自動化任務處理做介紹,並對每一種方法的優缺點做個簡單的總結,以及在實際工作中我們該如何做出選擇。
三種實現方法:
- 客戶端與服務端 Git hooks
- GitLab-Runner 以及編寫 .gitlab-ci.yml 文件
- GitLab Webhooks 與 Jenkins 的配合使用
Git 鈎子
Git hooks是基於事件的。當你執行特定的git指令時,該軟件會從git倉庫下的hooks目錄下檢查是否有相對應的腳本,如果有就執行。
有些腳本是在動作執行之前被執行的,這種“先行腳本”可用於實現代碼規范的統一、完整性檢查、環境搭建等功能。有些腳本則在事件之后被執行,這種“后行腳本”可用於實現代碼的部署、權限錯誤糾正(git在這方面的功能有點欠缺)等功能。
安裝一個鈎子
鈎子都被存儲在Git目錄下的hooks子目錄中。也即絕大部分項目中的.git/hooks。當你用git init初始化一個新版本庫時,Git 默認會在這個目錄中放置一些示例腳本。這些腳本除了本身可以被調用外,它們還暴露了被觸發時所傳入的參數。這些示例的名字都是以 .sample 結尾,如果想啟用它們,移除這個后綴即可。
把一個正確命名且可執行的文件放入 Git 目錄下的 hooks 子目錄中,即可激活該鈎子腳本。這樣一來,它就能 被 Git 調用。
客戶端和服務器端 Git hooks
git hooks 采用 事件機制, 在相應的操作(比如 git commit / git merge)下觸發, 分為 2 種:
-
服務端 hooks, github 的 webhooks 就是在此基礎上建立起來的;
-
客戶端 hooks, 每個 git 版本庫的 .git/hooks/ 文件夾下就有可以使用的例子。
注意: 客戶端 hooks 並不會同步到版本庫中
客戶端鈎子由諸如提交和合並這樣的操作所調用,而服務器端鈎子作用於諸如接收被推送的提交這樣的聯網操作。
客戶端鈎子位於項目根目錄 your_project/.git/hooks 文件夾下
服務端鈎子則位於 your_project.git 文件夾下的 hooks 和 custom_hooks
hooks與custom_hooks文件夾
可以看到GitLab為我們創建了一個軟連接連接到了GitLab自定義的鈎子目錄,這樣所有創建的項目都可以使用同一個腳本規則,減少了維護成本。
那我們如何結合GitLab定制自己的腳本呢?git會首先觸發GitLab的腳本,然后GitLab執行完自己的腳本文件后會再調用掉用戶放在custom_hooks下的腳本,所以我們只需要將我們定制好的腳本放在custom_hooks下即可,腳本名稱和之前一樣。例如:touch post-receive,這個腳本理論上可以使用任何腳本語言例如Perl、Python、Ruby等,不過執行這個腳本的用戶將是git,要注意git用戶對系統的操作權限,還要注意post-receive這個腳本需要能夠有執行權限。
客戶端與服務端鈎子圖示
(圖片來自於網絡)
示例
post-receive 推送代碼后自動部署
將目錄切換至 ../BRIDGE_REPO.git/hooks,用 cp post-receive.sample post-receive 復制並重命名文件后用 vim post-receive 修改。其內容大致如下:
#!/bin/sh
unset GIT_DIR
NowPath=`pwd`
DeployPath="../../www"
cd $DeployPath
git pull origin master
cd $NowPath
exit 0
使用 chmod +x post-receive 改變一下權限即可,服務器端的配置就基本完成了。
GitLab-CI與GitLab-Runner
持續集成(Continuous Integration)
要了解GitLab-CI與GitLab Runner,我們得先了解持續集成是什么。
持續集成是一種軟件開發實踐,即團隊開發成員經常集成他們的工作,通常每個成員每天至少集成一次,也就意味着每天可能會發生多次集成。每次集成都通過自動化的構建(包括編譯,發布,自動化測試)來驗證,從而盡快地發現集成錯誤。許多團隊發現這個過程可以大大減少集成的問題,讓團隊能夠更快的開發內聚的軟件。
GitLab-CI
GitLab-CI就是一套配合GitLab使用的持續集成系統(當然,還有其它的持續集成系統,同樣可以配合GitLab使用,比如接下來要說的Jenkins)。
持續集成,我們通常使用CI來做一些自動化工作,比如程序的打包,單元測試,部署等,這種構建方式避免了打包環境差異引起的錯誤,提高了工作效率。Gitlab-CI是Gitlab官方提供的持續集成服務,我們可以在倉庫的根目錄下新建.gitlab-ci.yml文件,自己定義持續集成流程模板,並且在Gitlab中配置runner,在之后的每次提交或合並中將會觸發構建,並且可以通過Gitlab的hook, 在代碼提交的各個環節自動地完成一系列的構建工作,總之對於一些非復雜性的集成需求,都是可以滿足的。
實際上,GitLab-CI中有一個概念叫 Pipeline ,一次 Pipeline 其實相當於一次構建任務,里面可以包含多個流程,如安裝依賴、運行測試、編譯、部署測試服務器、部署生產服務器等流程。任何提交或者 Merge Request 的合並都可以觸發 Pipeline。
思考:
為什么不是 GitLab CI 來運行那些構建任務?
一般來說,構建任務都會占用很多的系統資源 (譬如編譯代碼),而 GitLab CI 又是 GitLab 的一部分,如果由 GitLab CI 來運行構建任務的話,在執行構建任務的時候,GitLab 的性能會大幅下降。
GitLab CI 最大的作用是管理各個項目的構建狀態,因此,運行構建任務這種浪費資源的事情就交給 GitLab Runner 來做了!
因為 GitLab Runner 可以安裝到不同的機器上,所以在構建任務運行期間並不會影響到 GitLab 的性能~
GitLab-Runner
GitLab-Runner是配合GitLab-CI進行使用的。一般地,GitLab里面的每一個工程都會定義一個屬於這個工程的軟件集成腳本,用來自動化地完成一些軟件集成工作。當這個工程的倉庫代碼發生變動時,比如有人push了代碼,GitLab就會將這個變動通知GitLab-CI。這時GitLab-CI會找出與這個工程相關聯的Runner,並通知這些Runner把代碼更新到本地並執行預定義好的執行腳本。
所以,GitLab-Runner就是一個用來執行軟件集成腳本的東西。你可以想象一下:Runner就像一個個的工人,而GitLab-CI就是這些工人的一個管理中心,所有工人都要在GitLab-CI里面登記注冊,並且表明自己是為哪個工程服務的。當相應的工程發生變化時,GitLab-CI就會通知相應的工人執行軟件集成腳本。
Runner一共有三種類型
- 本地Runner
- 普通的服務器上的Runner
- 基於Docker的Runner
MAC環境安裝gitlab-ci-multi-runner
- sudo curl --output /usr/local/bin/gitlab-runner https://gitlab-ci-multi-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-ci-multi-runner-darwin-amd64
- gitlab-runner register
- gitlab-runner install (需到相關的項目文件夾下)
- gitlab-runner start
- gitlab-runner run
具體說下gitlab-runner register
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/ci):
http://xxxxxx // 在這里輸入gitlab安裝的服務器ip/ci 即可
Please enter the gitlab-ci token for this runner:
xxxxxxxxxxxxxxxxxx // 這里的token可通過Gitlab上的項目Runners選項查看
Please enter the gitlab-ci description for this runner:[E5]:demo
// 這里填寫一個描述信息
Please enter the gitlab-ci tags for this runner (comma separated):
demo // 在這里填寫tag信息,多個tag可通過逗號,分割。
tag:一個項目可能有多個runner,是根據tag來區別runner的。
Registering runner... succeeded. runner=eaYyokc5
Please enter the executor: docker, docker-ssh, parallels, shell, ssh, virtualbox, docker+machine, docker-ssh+machine:
shell // 在這里需要輸入runner的執行方式,直接輸入shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! // 出現這樣信息表示服務端的配置就已經成功結束了。
如何編寫 .gitlab-ci.yml 文件
如果你在項目倉庫里面加入.gitlab-ci.yml文件,同時給項目配置了gitlab-runner, 那么每次提交代碼或者合並 mr , 都會觸發你的 CI Pipeline (持續集成管道)。
stages:
- deploy
deploy:
stage: deploy
script:
- echo "start deploy....."
- deploy
only:
- master
tags:
- shell
其中deploy是編寫的shell腳本,可以實現將要發布的內容自動部署到發布目錄下:
#!/bin/bash
deploy_path="xxx"
project_path="xxx;
judge_path = "$deploy_path/$project_path"
if [ ! -d "$judge_path" ]
then
project_url="xxx.git"
git clone $project_path $deploy_path
else
cd $deploy_path
git pull
fi
.gitlab-ci.yml配置詳解請參考:
gitlab ci/cd .gitlab-ci.yml配置詳解
Gitlab Webhooks
Webhooks 允許第三方應用監聽 GitLab 上的特定事件,在這些事件發生時通過 HTTP POST 方式通知( 超時5秒) 到第三方應用指定的 Web URL。 例如項目有新的內容 Push,或是 Merge Request 有更新等。 WebHooks 可方便用戶實現自動部署,自動測試,自動打包,監控項目變化等。
webhooks, 可以在 pull request / merge master 等幾個場景下, 設置異步回調通知(http 請求)。這個背后就是 git hooks 在起作用。
因此,利用 WebHooks 的特性,可配合 Jenkins 實現一系列的自動化任務。
Jenkins
Jenkins是一個用Java編寫的開源的持續集成工具,可以與Git打通,監聽Git的merge, push事件,觸發執行Jenkins的指定任務(job)。例如發布的任何一個環節都可自動完成,無需太多的人工干預,有利於減少重復過程以節省時間和工作量等。
實例:Jenkins、Gitlab webhooks實現開發分支自動合並
步驟梳理
- GitLab上准備一個web工程;
- GitLab上配置Jenkins的webhook地址;
- Jenkins安裝GitLab Plugin插件;
- Jenkins配置GitLab訪問權限;
- Jenkins上創建一個構建項目,對應的源碼是步驟1中的web工程;
- 修改web工程的源碼,並提交到GitLab上;
- 檢查Jenkins的構建項目是否會觸發自動任務腳本。
(Jenkins Job 和 GitLab 的關聯,在網上已經有許多完善的文檔了,在這里就不贅述了)
以下為開發分支develop自動合並master分支的腳本示例,僅供參考:
#!/bin/sh
echo *****************Start*****************
date
# 獲取最近一次提交的 commit id
sha1=`git rev-parse HEAD`
# 獲取姓名及郵箱,來配置git提交者信息
name=`git show $sha1 | grep 'Author:' | cut -d' ' -f2`
email=`git show $sha1 | grep 'Author:' | cut -d' ' -f3 | sed -e 's/<//g' | sed -e 's/>//g'`
echo '當前提交人信息:'
echo $name
echo $email
git config --global user.name $name
git config --global user.email $email
echo '***************** git checkout develop & git pull:'
git checkout develop
git pull
# develop合並master
echo '***************** git merge origin/master:'
conflict=`git merge origin/master`
echo $conflict | grep 'CONFLICT'
if [ $? -ne 0 ]; then
echo '***************** git push origin HEAD:'
git push origin HEAD
echo '***************** git status:'
git status
else
git status
echo 'Automatic merge failed...'
echo 'Please fix conflicts and then commit the result...'
exit 1
fi
echo *****************End*****************
三種實現方法的優缺點對比:
- 客戶端與服務端 Git hooks :如果僅涉及客戶端鈎子,用這種方法比較好,比如 husky 這個插件;但如果是服務端鈎子,就必須在服務端配置才可使用,比如 post-receive 鈎子;
- GitLab-Runner 以及編寫 .gitlab-ci.yml 文件:需服務端安裝 gitlab-runner 來支持自動化腳本的執行;
- GitLab Webhooks 與 Jenkins 的配合使用:Jenkins是比較成熟的第三方持續集成系統,可與GitLab完美的結合使用,但配置過程仍是稍顯復雜,但在自動化任務處理方面,Jenkins無疑是個較好的選擇。