前言
産品需求評審後,各自拆分任務,從
master
分支切出一個
release
分支,根據各自任務情況切出
update
或
feature
的開發分支;
開發調試或提測時,将代碼
push
到遠端分支,提
merge request
(以下簡稱 mr)到
test
分支,
GitLab CI
将項目代碼自動建構并部署到測試環境;
測試完畢後提
mr
到
release
分支,待本次需求的開發分支都
code review
并合并後,從
release
分支提
mr
到
pre
分支,
GitLab CI
将項目代碼自動建構并部署到預生産環境,然後進行回歸測試,有問題再從
release
分支切出開發分支進行修改,重複之前的流程。
預生産環境沒問題後,從
release
分支提
mr
到
master
分支,,然後打
tag
上線,
GitLab CI
将項目代碼自動建構并部署到生産環境,然後進行回歸測試,有問題再發版。
至此一次需求的完整開發流程就告一段落了,其中建構/部署等一些重複工作都是
GitLab CI
幫我們完成,對此一直很好奇,接下來我們就來嘗試搭建一個使用
GitLab CI
的項目。
搭建新項目
現有項目中使用
GitLab CI
可以直接跳過這步,從這裡開始
可以按下面的步驟一步一步搭建,也可以直接克隆這個倉庫:gitlab-ci-example
初始化項目
建立項目檔案夾
mkdir gitlab-ci-example
cd gitlab-ci-example
初始化 git 和 npm
git init
npm init -y
建立項目檔案
mkdir src build
建立 .gitignore 檔案
gitlab-ci-example/.gitignore
dist
node_modules
建立 .editorconfig 檔案
gitlab-ci-example/.editorconfig
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
建立 index.html 檔案
gitlab-ci-example/src/index.html
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<h1>Learn Gitlab CI</h1>
</div>
</body>
</html>
建立 main.js 檔案
gitlab-ci-example/src/main.js
function appendElementToAPP({ tag = "div", content = "" }) {
const appEle = document.getElementById("app");
const newEle = document.createElement(tag);
newEle.innerHTML = content;
appEle.append(newEle);
}
appendElementToAPP({
tag: "div",
content: `append content by js on ${new Date().toUTCString()}`,
});
建立 webpack.dev.js 檔案
gitlab-ci-example/build/webpack.dev.js
"use strict";
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const resolve = (dir) => path.resolve(__dirname, "../", dir);
module.exports = {
mode: "development",
entry: {
app: "./src/main.js",
},
output: {
path: resolve("dist"),
filename: "[name].[hash].js",
},
resolve: {
extensions: [".js"],
},
devServer: {
port: 8090,
contentBase: resolve("dist"),
},
plugins: [
new HtmlWebpackPlugin({
filename: resolve("dist/index.html"),
template: "src/index.html",
}),
],
};
建立 webpack.prod.js 檔案
gitlab-ci-example/build/webpack.prod.js
"use strict";
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const resolve = (dir) => path.resolve(__dirname, "../", dir);
module.exports = {
mode: "production",
entry: {
app: "./src/main.js",
},
output: {
path: resolve("dist"),
filename: "[name].[hash].js",
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: resolve("dist/index.html"),
template: "src/index.html",
}),
],
};
建立 append-element.js 檔案
gitlab-ci-example/build/append-element.js.sh
const path = require("path");
const fs = require("fs");
const cheerio = require("cheerio");
const htmlFilePath = path.resolve(__dirname, "../dist/index.html");
fs.readFile(htmlFilePath, (err, data) => {
if (err) {
return;
}
const $ = cheerio.load(data);
$("#app").append(
`<div style="color: red;">append content by build on ${new Date().toUTCString()}</div>`
);
fs.writeFileSync(htmlFilePath, $.html());
});
建立 deploy-test.sh 檔案
gitlab-ci-example/build/deploy-test.sh
cp -rf dist/* /www/test/gitlab-ci-example
修改 package.json 檔案
gitlab-ci-example/package.json
{
"name": "gitlab-ci-example",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"deploy-test": "build/deploy-test.sh",
"dev": "webpack-dev-server --config build/webpack.dev.js",
"build": "webpack --config build/webpack.prod.js && node build/append-element.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
安裝項目依賴
npm i -D cheerio webpack webpack-cli webpack-dev-server clean-webpack-plugin html-webpack-plugin
運作項目
npm run dev
在浏覽器中打開連結:http://localhost:8090/ ,你應該能看到:
打包項目
npm run build
在浏覽器中打開
dist
目錄下的
index.html
檔案,你應該能看到:
至此項目的基本功能搭建完成,接下來開始在項目中使用
GitLab CI
。
項目中使用 GitLab CI
使用
GitLab CI
之前,你得先準備一下:
- 一台雲伺服器
- 一個
倉庫GitLab
設定 GitLab Runner
在倉庫首頁,點選側邊欄 -
Settings
-
CI / CD
,跳轉
CI / CD Settings
頁面,展開
Runners
選項,按步驟手動設定
GitLab Runner
:
安裝 GitLab Runner
根據系統架構,下載下傳并安裝對應的軟體包,檢視詳情
# 下載下傳(适用于amd64的軟體包)
curl -LJO https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_amd64.deb
# 如果下載下傳太慢,建議在本地下載下傳好之後,通過scp指令複制到遠端,類似這樣
# scp ~/gitlab-runner_amd64.deb [email protected]:/home/yourUserName
# 安裝
sudo dpkg -i gitlab-runner_amd64.deb
# 輸出
Selecting previously unselected package gitlab-runner.
(Reading database ... 67015 files and directories currently installed.)
Preparing to unpack gitlab-runner_amd64.deb ...
Unpacking gitlab-runner (13.0.1) ...
Setting up gitlab-runner (13.0.1) ...
GitLab Runner: detected user gitlab-runner
Runtime platform arch=amd64 os=linux pid=28968 revision=21cb397c version=13.0.1
gitlab-runner: Service is not installed.
Runtime platform arch=amd64 os=linux pid=28975 revision=21cb397c version=13.0.1
gitlab-ci-multi-runner: Service is not installed.
Runtime platform arch=amd64 os=linux pid=28993 revision=21cb397c version=13.0.1
Runtime platform arch=amd64 os=linux pid=29039 revision=21cb397c version=13.0.1
# 如果你收到類似上面的報錯,運作下面的指令,如果能輸出資訊表示正常
sudo gitlab-runner status
# 輸出
Runtime platform arch=amd64 os=linux pid=29971 revision=21cb397c version=13.0.1
gitlab-runner: Service is running!
對于上面的報錯資訊,可以看看這個 gitlab issue
注冊 GitLab Runnner
開始注冊,下面是
Linux
的例子,其他系統請看這裡
# 注冊
sudo gitlab-runner register
# 輸出
Runtime platform arch=amd64 os=linux pid=31237 revision=21cb397c version=13.0.1
Running in system-mode.
# 指定 GitLab 執行個體 URL
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
https://gitlab.com/
# 輸入注冊令牌(從項目-設定-CI/CD 設定-Runners 那裡拷貝)
Please enter the gitlab-ci token for this runner:
JhXh7o********yDXATd
# 輸入描述
Please enter the gitlab-ci description for this runner:
[hostname]: runner-001
# 輸入關聯标簽
Please enter the gitlab-ci tags for this runner (comma separated):
runner-001-tag
# 輸出
Registering runner... succeeded runner=JhXh7oEx
# 選擇執行環境,這裡選擇的是 shell
Please enter the executor: virtualbox, docker-ssh+machine, kubernetes, parallels, shell, ssh, docker+machine, custom, docker, docker-ssh:
shell
# 輸出
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
下載下傳安裝并注冊完
Runner
後,傳回
CI / CD Settings
頁面,現在應該能看到項目關聯的
Runner
配置 GitLab CI
設定完
GitLab Runner
後,我們就可以開始配置
GitLab CI
了,建立
.gitlab-ci.yml
檔案
gitlab-ci-example/.gitlab-ci.yml
# 工作名稱
job-test:
# 階段
stage: test
# 觸發條件:test 分支更新時
only:
- test
# 指定工作給具有特定标簽的 Runners
tags:
- runner-001-tag
# 腳本
script:
- npm install
- npm run build
- npm run deploy-test
預設情況下
GitLab Runner
不會運作沒有
tags
的工作,是以這裡我們指定注冊
GitLab Runner
時候設定的标簽:
runner-001-tag
,檢視更多 GitLab CI/CD 配置選項
如果你不想設定
tags
,可以修改
GitLab Runner
的配置,勾選
Run untagged jobs
,表示允許
GitLab Runner
運作沒有設定
tags
的任務。
儲存
.gitlab-ci.yml
檔案後,将改動
push
到遠端倉庫
觸發 GitLab CI
配置檔案有了之後,我們需要将其觸發,從包含上面改動的分支,切出一個
test
分支,送出到遠端,用于觸發
GitLab CI
(新送出和合并
test
分支都會觸發
CI
),當然通過圖形化界面建立
test
分支也是可以的
git checkout test
git push -u origin test
在倉庫首頁,點選側邊欄 -
CI / CD
-
Pipelines
,就能看到目前倉庫所有的
CI
記錄,類似下面這樣:
遇到的問題
1. mkdir: cannot create directory ‘/home/gitlab-runner/builds/3-1Hb5zy’: Permission denied
Running with gitlab-runner 13.0.1 (21cb397c)
on runner-001 3-1Hb5zy
Preparing the "shell" executor 00:00
Using Shell executor...
Preparing environment 00:00
Running on xx-ubuntu...
Getting source from Git repository 00:00
mkdir: cannot create directory ‘/home/gitlab-runner/builds/3-1Hb5zy’: Permission denied
Uploading artifacts for failed job 00:00
mkdir: cannot create directory ‘/home/gitlab-runner/builds/3-1Hb5zy’: Permission denied
ERROR: Job failed: exit status 1
原因: 将代碼
push
到遠端之後,建構出現了上面的報錯,
GitLab Runner
建構時使用的是
gitlab-runner
使用者,建立目錄的時候提示權限不足,嘗試檢視目錄資訊:
# 檢視檔案和目錄資訊
ls -alF /home/gitlab-runner
# drwxr-xr-x 4 root root 4096 Jun 2 17:45 builds/
目前目錄的權限和權限組都是
root
,
gitlab-runner
使用者不在
root
權限組下,是以沒權限操作。
仔細想想 發現不對勁,
GitLab Runner
建構時使用的是
gitlab-runner
使用者,但是為什麼
builds
目錄在
root
權限組下?回想一下在此之前做過哪些和
root
使用者相關的操作,經過确認和查閱資料後發現,原來是在這次建構之前,手動安裝服務(
gitlab-runner install
)的時候指定使用
root
使用者(
--user root
)導緻的:
# 安裝服務,指定工作目錄,指定運作任務的使用者為 root 使用者
sudo gitlab-runner install --working-directory /home/gitlab-runner --user root
如何解決: 删除
builds
目錄、解除安裝重裝
gitlab-runner
服務,将服務關聯的使用者指回
gitlab-runner
使用者
# 停止服務
sudo gitlab-runner stop
# 解除安裝服務
sudo gitlab-runner uninstall
# 重新安裝服務,指定工作目錄和使用者
sudo gitlab-runner install --working-directory /home/gitlab-runner --user gitlab-runner
# 完整配置
# sudo gitlab-runner install --working-directory /home/gitlab-runner --config /etc/gitlab-runner/config.toml --service gitlab-runner --syslog --user gitlab-runner
# 校驗
sudo gitlab-runner verify
# 啟動服務
sudo gitlab-runner start
# 檢視狀态
sudo gitlab-runner status
# 再次檢視檔案和目錄資訊
ls -alF /home/gitlab-runner
# drwxrwxr-x 3 gitlab-runner gitlab-runner 4096 Jun 3 16:21 builds/
現在
builds
目錄的權限歸回
gitlab-runner
使用者所有了,在
gitlab
倉庫的
Pipelines
或
Jobs
頁面找到這次工作關聯的
retry
按鈕,點選按鈕嘗試重新運作建構
2. bash: line 92: npm: command not found
Running with gitlab-runner 13.0.1 (21cb397c)
on runner-001 3-1Hb5zy
Preparing the "shell" executor 00:00
Using Shell executor...
Preparing environment 00:00
Running on VM-0-5-ubuntu...
Getting source from Git repository 00:03
Fetching changes with git depth set to 50...
Reinitialized existing Git repository in /home/gitlab-runner/builds/3-1Hb5zy/0/Lsnsh/gitlab-ci-example/.git/
Checking out 4e716630 as test...
Skipping Git submodules setup
Restoring cache 00:00
Downloading artifacts 00:00
Running before_script and script 00:00
$ npm install
bash: line 92: npm: command not found
Running after_script 00:00
Uploading artifacts for failed job 00:00
ERROR: Job failed: exit status 1
原因: 重裝服務後
retry
建構後,出現了上面的報錯,原因是因為
gitlab-runner
使用者所處的環境沒有安裝
node
導緻的(預設情況下在
root
或者其他使用者上安裝的
node
在
gitlab-runner
使用者所處環境是通路不到的)
如何解決:登入伺服器,切換到
gitlab-runner
使用者,安裝
nvm
,再安裝
node
# 切換到 root 使用者
sudo su
# 登入 gitlab-runner 使用者
su -l gitlab-runner
# 安裝 nvm(https://github.com/nvm-sh/nvm),如果通路腳本443,嘗試用其他方式安裝 nvm 或者直接安裝 node
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
# 檢視 nvm
nvm ls
# 安裝 node(截止目前最新LTS版本為:12.18.0,自行選擇版本安裝)
nvm install 12.16.2
# 檢視 node 和 npm 版本
node -v
npm -v
現在
gitlab-runner
使用者所處環境已經安裝
node
了,高興的嘗試
retry
後,發現依然是
bash: line 92: npm: command not found
,一度以為是錯覺、是服務沒有檢測到
node
的存在,嘗試重裝、重新開機
gitlab-runner
服務後
retry
依然是
failed
。
冷靜一番後,查閱大量類似案例,初步判斷可能是環境變量沒加載,也就是
nvm
沒有加載導緻的,嘗試在建構過程中手動加載
~/.bashrc
檔案:
before_script:
- source ~/.bashrc
重新
retry
後依然還是
failed
,最後還是在
~/.profile
和
~/.bashrc
兩個配置檔案頭部的一些注釋資訊裡,找到了一些新的靈感:
~/.profile# ~/.profile: executed by the command interpreter for login shells.
# ...
直譯過來:
~/.profile
: 由指令解釋器針對登入
shell
執行。
~/.bashrc# ~/.bashrc: executed by bash(1) for non-login shells.
# ...
直譯過來:
~/.bashrc
:由
bash(1)
對非登入
shell
執行。
以上資訊中提到了登入與非登入兩種狀态,配置檔案在對應狀态下才會執行,通過添加調試資訊發現,在
gitlab-runner
執行任務建構時,不會加載
~/.bashrc
檔案,隻會加載
~/.profile
檔案;而通過
ssh
登入伺服器時,兩個檔案都會加載,是不是有些疑惑 ,這是因為
~/.profile
檔案在開頭會根據環境(
bash
)決定是否要先加載
~/.bashrc
檔案,具體代碼如下:
# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# see /usr/share/doc/bash/examples/startup-files for examples.
# the files are located in the bash-doc package.
# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
#umask 022
# if running bash
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
要解決
npm
指令找不到這個問題,需要在
~/.profile
配置檔案添加上加載
nvm
的代碼:
# 編輯配置檔案
vi ~/.profile
# 配置 nvm 加載,将下面的代碼添加到配置檔案中(https://github.com/nvm-sh/nvm#installing-and-updating)
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
# 儲存配置檔案後,重新加載配置檔案
source ~/.profile
3. sh: 1: build/deploy-test.sh: Permission denied
$ npm run deploy-test
> [email protected] deploy-test /home/ubuntu/builds/3-1Hb5zy/0/Lsnsh/gitlab-ci-example
> build/deploy-test.sh
sh: 1: build/deploy-test.sh: Permission denied
npm ERR! code ELIFECYCLE
npm ERR! errno 126
npm ERR! [email protected] deploy-test: `build/deploy-test.sh`
npm ERR! Exit status 126
npm ERR!
npm ERR! Failed at the [email protected] deploy-test script.
# ...
原因: # 在項目建構目錄下(類似:/home/ubuntu/builds/3-1Hb5zy/0/Lsnsh/gitlab-ci-example),檢視部署腳本的權限資訊
ls -alF ./build/deploy-test.sh
# -rw-rw-r-- 1 ubuntu ubuntu 42 Jun 2 19:40 deploy-test.sh
通過以上指令發現和搜尋相關問題,發現
deploy-test.sh
檔案不具備可執行權限,是以無法執行
如何解決:- 直接更改
檔案權限deploy-test.sh
# 表明 deploy-test.sh 檔案是可執行的
git update-index --chmod=+x ./build/deploy-test.sh
# 改動會直接進入暫存區,編輯器的 git state 可能表明還有新的更改,忽略後直接送出本次更改後,git state 的狀态會更新
git commit -m 'Make build.sh executable'
# 送出到遠端,master, test 或者其他分支
git push
- 通過
命名執行sh
檔案deploy-test.sh
# package.json
- "deploy-test": "build/deploy-test.sh",
+ "deploy-test": "sh build/deploy-test.sh",
4. /www/test/gitlab-ci-example: No such file or directory
$ npm run deploy-test
> [email protected] deploy-test /home/gitlab-runner/builds/3-1Hb5zy/0/Lsnsh/gitlab-ci-example
> build/deploy-test.sh
/www/test/gitlab-ci-example: No such file or directory
Please create this directory and then assign the directory permissions to the gitlab-runner user.
You can execute the following command as root:
mkdir /www/test/gitlab-ci-example
chown gitlab-runner /www/test/gitlab-ci-example
原因: build/deploy-test.sh
腳本會将建構好的代碼,拷貝到
/www/test/gitlab-ci-example
目錄下,是以在建構之前需要先建立好這個目錄
如何解決:參考
build/deploy-test.sh
腳本中列印出的提示資訊,建立并配置設定目錄權限即可:
# 建立目錄(使用 root 使用者或者其他 gitlab-runner 使用者以為的使用者)
mkdir /www/test/gitlab-ci-example
# 配置設定 gitlab-runner 使用者檔案夾權限
chown gitlab-runner /www/test/gitlab-ci-example
總結
至此,
CI
終于可以跑通了,部署後頁面的内容是這樣的,點選檢視:
通過
.gitlab-ci.yml
配置檔案,你可以在建構的各個階段做處理,比如你可以在
before_script
和
after_script
階段調用釘釘機器人接口,及時将部署狀态同步到個人/群:
before_script:
# 釘釘通知 釘釘群
- curl -X POST 'https://oapi.dingtalk.com/robot/send?access_token=xxx&xxxx'
通知類似下面這樣:
更多關于
.gitlab-ci.yml
檔案的配置資訊,請看官方文檔
好利用
CI / CD
這件工具,相信會大大提升團隊協作和開發效率。萬事開頭難,起初肯定會有抵觸心理,邁過這道坎之後,還有下一道坎等着你[手動狗頭]
示例項目的倉庫連結如下,歡迎
star
:
github
倉庫(
template
):https://github.com/Lsnsh/gitlab-ci-example
gitlab
倉庫:https://gitlab.com/Lsnsh/gitlab-ci-example
指令彙總
# 檢視 gitlab-runner 相關程序
ps aux|grep gitlab-runner
# 注冊 gitlab-runner
sudo gitlab-runner register
# 重裝 gitlab-runner 服務
# 停止服務
sudo gitlab-runner stop
# 解除安裝服務
sudo gitlab-runner uninstall
# 重新安裝服務,指定工作目錄和使用者
sudo gitlab-runner install --working-directory /home/gitlab-runner --user gitlab-runner
# 完整配置
# sudo gitlab-runner install --working-directory /home/gitlab-runner --config /etc/gitlab-runner/config.toml --service gitlab-runner --syslog --user gitlab-runner
# 校驗
sudo gitlab-runner verify
# 啟動服務
sudo gitlab-runner start
# 檢視狀态
sudo gitlab-runner status
# 拷貝檔案到遠端主機
# scp ~/gitlab-runner_amd64.deb [email protected]:/home/yourUserName
# eg: (将檔案 ~/gitlab-runner_amd64.deb 拷貝到遠端主機,公網 IP 為 110.120.130 的 root 使用者目錄下)
scp ~/gitlab-runner_amd64.deb [email protected]:/home/root
# 檢視檔案和目錄資訊
# eg: (檢視 /home/gitlab-runner 目錄)
ls -alF /home/gitlab-runner
# 切換到 root 使用者
sudo su
# root 使用者登入其他使用者
# eg: (登入 gitlab-runner 使用者)
su -l gitlab-runner
# 表明檔案是可執行的
# git update-index --chmod=+x 檔案路徑
# 表明檔案是不可執行的
# git update-index --chmod=-x 檔案路徑
# eg: (表明 ./build/deploy-test.sh 檔案是可執行的)
git update-index --chmod=+x ./build/deploy-test.sh
# 給使用者配置設定檔案或檔案夾權限
# chown 使用者名 檔案或檔案夾路徑
# eg: (配置設定 gitlab-runner 使用者 /www/test/gitlab-ci-example 檔案夾權限)
chown gitlab-runner /www/test/gitlab-ci-example
參考連結
- Install GitLab Runner
- Registering Runners
- Error when installing gitlab-runner
- Permission denied for build.sh file
- GitLab CI/CD Pipeline Configuration Reference
- Gitlab CI Failed: NPM command not found
- How to create file execute mode permissions in Git on Windows?