前言概述
本文記錄使用docker安裝jenkins以後,推送代碼到gitee和gitlab上,而後執行構建前端項目的過程,對應cicd的操作設置流程環節
包含:手動觸發構建和webhook自動化構建
50多張圖,詳細記錄過程,且按照筆者的這篇文章操作,不會踩坑被卡好久(筆者已經踩過坑了)
前端CICD自動化部署角色分工
前端開發過程中,關於項目發佈上線,標準化而言涉及四個角色(假設是四台硬件設備)
四個角色分工
- 自己的電腦——用來寫代碼(通過
git push把本機代碼推送到gitlab服務器) gitlab服務器——用來存代碼(注意,gitlab上面的代碼,一般無法直接push到jenkins服務器上,需要jenkins主動從gitlab那裏去“撈”代碼)jenkins服務器——用來執行自動化cicd操作(jenkins把“撈”到的代碼執行npm run build構建操作,並把構建產物傳送到生產服務器)- 生產服務器——存放前端構建產物,並使用
nginx代理對應html\css\js靜態資源給用户訪問用
若是前端寫的node中間層BFF服務(BFF服務不需要再執行npm run build,直接用源碼js跑),則使用pm2進行管理,推薦也使用nginx做代理
比如nginx把/bff/*前綴過來的請求轉發給http://localhost:3000上運行的fastify服務(不暴露接口ip統一使用nginx作為入口)
角色分工圖示
如圖
獲取gitlab的代碼,可以手動,也可以自動
上圖中,代碼git push推送不用贅述,關鍵在於gitlab和jenkins的通信這一塊
————如何讓jenkins能夠從gitlab中獲取(撈)對應代碼、如何觸發?以及
————如何讓jenkins能夠收到gitlab發來webhook請求,如何配置
當然還有jenkins和測試/生產服務器的通信——把代碼丟過去(畢竟生產服務器不允許隨意上傳文件操作命令啥的)
我們先看:如何讓jenkins能夠從gitlab中獲取(撈)對應代碼、如何觸發?
jenkins撈gitlab代碼的兩種方式
以下兩種方式的前提是相關配置已經提前做好了
-
方式一,人工手動點擊撈代碼觸發構建
- 我們直接登錄jenkins系統,找到對應項目列表中
- 點擊“立即構建”按鈕,觸發jenkins去gitlab拿代碼,如下圖
-
方式二,使用gitlab自帶的webhook
- 當代碼寫完push到gitlab後,gitlab能夠感知到有人提交代碼了
- 並使用自帶的webhook鈎子函數功能,給jenkins發送一個請求,告知jenkins可以過來拿代碼了
jenkins收到請求後,就會自動拉取gitlab的代碼了,如下圖
最終jenkins構建打包dist以後,就可以把dist目錄裏面的代碼,丟到生產或者測試服務器上去了(筆者使用rsync命令傳輸文件的)
方式一、手動點擊觸發jenkins拉取gitee代碼構建CICD步驟流程
自動webhook觸發後文也會記錄
接下來,我們在jenkins創建一個項目(做相應配置),採取手動觸發的方式(點一下),讓jenkins拉取gitee倉庫的代碼,並通過編寫的shell腳本,構建dist產物,最後通過rsync把產物傳輸的生產目錄,再接入釘釘機器人告知構建成功
筆者用這台烏班圖服務,集成在一塊了,既有gitlab、又有jenkins,也有生產的nginx,就是服務運行端口不同
1. 安裝gitee插件
在jenkins中安裝gitee插件,插件很多很豐富,視情況安裝
注意,最初安裝jenkins的時候,這裏選擇默認安裝推薦的插件
2. 新建一個自由風格的jenkins任務,做基礎信息填寫
自由風格和流水線用的比較多一些
流水線更加靈活,我們先看一下自由風格的
然後填寫項目描述,丟棄舊的構建,只保留三個歷史構建結果
選擇git,填寫gitee倉庫,並準備新建憑證,並使用這個憑證
然後這一步,先停下來
3. 接下來打開gitee,準備生成一個私人令牌
個人主頁-->設置-->安全設置-->私人令牌
注意權限把控,具體勾選那個,看實際情況
生成私人令牌
4. 妥善保存私人令牌,然後在jenkins中添加憑據使用此令牌
然後選擇剛剛新建的憑證
注意,憑證可以在:系統管理-->憑據頁面 進行後續管理(比如刪除、更新等)
5. 構建基礎設置
指定master分支的代碼,為構建分支代碼(就是jenkin會通過上述的認證,讓gitee允許它進行代碼拉取操作,拉取master分支代碼)
Delete workspace before build starts
所謂的工作空間,就是一個文件夾目錄,裏面存放了新建的jenkins任務
因為筆者的jenkins是通過docker部署的,所以進入這個docker容器內部,能看到一些jenkins具體信息,比如工作空間
6. 編寫shell腳本進行構建
Build Steps-->增加構建步驟
這裏選擇使用使用shell腳本進行構建
構建腳本如下
shell腳本代碼
# 驗證環境
node -v
npm -v
rsync --version
curl --version
# 修改國內鏡像源頭
npm config set registry https://registry.npmmirror.com
# 安裝依賴並構建
npm install
npm run build
# 把構建的dist丟到宿主機目錄
rsync -avz --delete /var/jenkins_home/workspace/gitee/dist/ root@172.17.0.1:/var/www/html/reactExamples/
# 通知釘釘機器人構建成功了
curl -X POST "https://oapi.dingtalk.com/robot/send?access_token=access_token" -H "Content-Type: application/json" -d "{\"msgtype\": \"text\", \"text\": {\"content\": \"Jenkins 構建成功\"}}"
注意事項
- 1 因為要執行npm run build所以jenkins中,需要有node環境 所以容器裏面要提前準備好node環境!!!
- 2 rsync這個包比scp更好用,可以在服務器與服務器之間(也可以把容器內的文件傳到宿主機),傳輸文件 這裏所以容器裏面要提前準備好rsync包!!!
-
3 注意,想要順利使用rsync這個包,需在容器內生成一套 SSH 密鑰對,再將公鑰配置到宿主機即可
- 比如
ssh-keygen -t rsa -b 4096 -C "jenkins@container"
- 比如
- 4 rsync語法傳輸介紹
rsync -avz --delete /var/jenkins_home/workspace/gitee/dist/ root@172.17.0.1:/var/www/html/reactExamples/
意思是:將容器內jenkins工作空間目錄/var/jenkins_home/workspace/gitee/dist/ 的文件(裏面的html、css、js),同步到宿主機(IP 為 172.17.0.1)的 /var/www/html/reactExamples/ 目錄,並且保持兩者內容完全一致(會刪除宿主機上多餘的文件)
**`rsync`**:核心命令,用於文件同步(支持本地 / 遠程同步,增量傳輸)。
**`-avz`**:組合選項,常用核心參數:
- `-a`(archive,歸檔模式):遞歸同步目錄,保留文件權限、所有者、時間戳等元數據(最常用的 “安全同步” 模式)。
- `-v`(verbose,詳細輸出):顯示同步過程中的文件列表,方便查看進度。
- `-z`(compress,壓縮傳輸):傳輸時對文件進行壓縮,節省網絡帶寬(適合遠程同步)。
**`--delete`**:關鍵選項,**刪除目標目錄中 “源目錄沒有” 的文件 / 目錄**,確保目標目錄與源目錄完全一致(避免目標目錄殘留過期文件)。例如:如果源目錄刪除了 `old.txt`,同步時會自動刪除目標目錄的 `old.txt`。
**`/var/jenkins_home/workspace/gitee/dist/`** :源路徑(容器內的目錄),末尾的 `/` 表示 “同步該目錄下的內容”(而非目錄本身)。若不加 `/`,會將 `dist` 目錄本身同步到目標路徑下(變成 `reactExamples/dist/...`)。
**`root@172.17.0.1:/var/www/html/reactExamples/`** :目標路徑(宿主機的目錄):
- `root@172.17.0.1`:宿主機的 SSH 登錄信息(用户名 `root` + 宿主機 IP `172.17.0.1`)。
- `:/var/www/html/reactExamples/`:宿主機上的目標目錄。
### 執行效果:
- 首次同步:將源目錄的所有文件完整複製到目標目錄。
- 後續同步:只傳輸修改過的文件(增量同步,效率高),並刪除目標目錄中源目錄沒有的文件,最終兩者完全一致。
注意,/var/www/html/reactExamples/這個目錄筆者提前使用nginx做了代理,如下:
# react的一些示例
location /reactExamples/ {
alias /var/www/html/reactExamples/;
try_files $uri $uri/ /reactExamples/index.html;
}
7. 通過釘釘機器人實現釘釘羣構建成功消息推送
使用curl命令(要提前新建羣,創建好釘釘機器人,用於得知構建成功消息結果)
不清楚如何創建釘釘機器人,可以參考官方文檔:https://open.dingtalk.com/document/dingstart/custom-bot-creat...
提醒一下,別忘了保存jenkins的設置
8. 人工手動構建點擊,並查看構建結果
點擊立即構建
查看構建輸出日誌
最終成功嘍
釘釘羣的消息
方式二、gitlab的webhook觸發jenkins構建CICD步驟流程
- 一般來説,git push代碼以後,再到jenkins上手動點擊一下,發佈項目,適用於生產環境
- 而git push代碼以後,直接就發佈了(不需要登錄jenkins),使用於測試環境
1. 什麼是webhook?
- webhook可以理解成為鈎子函數——當某個預定義事件發生時,觸發軟件服務發送http請求
- 好多軟件服務都提供了webhook鈎子
- 比如釘釘、飛書、Zabbix、GitHub、GitLab、Gitee、支付寶完成、微信支付完成等
核心邏輯
- WebHook 的本質是 “事件回調”:當gitlab倉庫發生提交、合併等操作時,gitlab會向我配置的jenkins回調地址,發送一個包含事件信息(如提交作者、分支、變動文件列表)的http請求
- 這個請求僅起到 “觸發信號” 的作用,不攜帶任何代碼文件本身
- Jenkins 收到該通知後,會基於已配置的憑據(個人令牌或 SSH 密鑰),主動向gitlab倉庫發起拉取請求,獲取最新代碼(類似
git pull的邏輯)。
webhook只是消息通知,不能直接傳輸代碼,核心是觸發henkins主動拉取最新代碼
方式一,筆者是給到的gitee案例,接下來使用gitlab案例進行記錄
建議購買一個雲服務器,ssl證書,真正的跑通,或者使用vmware虛擬機,把gitlab、jenkins部署好去實踐
可以參考筆者之前的文章:Ubuntu服務器上使用docker-compose部署 gitlab(圖文並茂記錄)
2. gitlab放開出站請求並初步配置webhook
首先,有一個gitlab的倉庫代碼,要首先放開出站請求
然後到代碼倉庫裏面,找到設置中的webhook如下
點擊添加webhook
先填寫點基礎信息
3. gitlab創建流水線任務
如下圖,新建任務
填寫基本信息
4. 填寫身份驗證令牌
指定身份驗證令牌,並採用/buildWithParameters?token='TOKEN_NAME'這種方式觸發構建
5. 身份驗證令牌規則
這裏使用/buildByToken/build方式配置會少一些
- 現在,筆者的jenkin服務運行在29877端口上,TOKEN\_NAME的值是gitlab-jenkins-token-2025
- 那麼webhook的url就是:
https://ashuai.site:29877/buildByToken/build?job=gitlab&token=gitlab-jenkins-token-2025- 注意,這裏有兩個query參數,job的值,是此構建任務的名字,也就是上方瀏覽器地址欄去掉configure的
- 也就是構建的任務名字
即:
// jenkins服務的運行ip端口/buildByToken/build?job=任務名&token=TOKEN_NAME
https://ashuai.site:29877/buildByToken/build?job=gitlab&token=gitlab-jenkins-token-2025
6. 在gitlab中的webhook中使用對應的url
填寫
這個時候,測試跑不通的,因為還要在pipe line中編寫腳本呢(gitlab任務也要保存哦)
7. 編寫Pipeline script流水線腳本代碼
代碼如下:
pipeline {
agent any
stages {
stage('Clean Workspace') {
steps {
echo '🧹 清理工作空間確保構建純淨...'
cleanWs()
}
}
stage('Checkout Code') {
steps {
echo '📥 從GitLab拉取最新代碼...'
git branch: 'main',
url: 'https://ashuai.site:12345/lss-group/react-example.git',
credentialsId: 'gitlab'
sh 'ls -la'
}
}
stage('Install Dependencies') {
steps {
echo '📦 安裝項目依賴,使用npm ci替代npm install安裝...'
sh 'npm ci'
script {
if (fileExists('node_modules')) {
echo "✅ 依賴安裝成功"
} else {
error "❌ 依賴安裝失敗"
}
}
}
}
stage('Build Project') {
steps {
echo '🏗️ 構建生產版本...'
sh 'npm run build'
sh '''
echo "=== 構建驗證 ==="
if [ -d "dist" ] && [ -f "dist/index.html" ]; then
echo "✅ 構建成功"
ls -la dist/
echo "構建大小: \$(du -sh dist/ | cut -f1)"
else
echo "❌ 構建失敗:缺少必要文件"
exit 1
fi
'''
}
}
stage('Deploy') {
steps {
echo '🚀 部署到宿主機目錄...'
script {
// 執行 rsync 部署
sh '''
echo "開始部署..."
echo "源目錄: /var/jenkins_home/workspace/gitlab/dist/"
echo "目標目錄: root@172.17.0.1:/var/www/html/reactExamples/"
# 執行 rsync 同步代碼把構建產物發送到生產(宿主機)對應目錄
rsync -avz --delete /var/jenkins_home/workspace/gitlab/dist/ root@172.17.0.1:/var/www/html/reactExamples/
# 檢查部署結果
if [ $? -eq 0 ]; then
echo "✅ 部署成功完成"
else
echo "❌ 部署失敗"
exit 1
fi
'''
}
}
}
}
post {
always {
echo "📊 ===== 構建報告 ====="
echo "🔢 構建號: #${env.BUILD_NUMBER}"
echo "⏱️ 構建時間: ${currentBuild.durationString}"
echo "📋 構建結果: ${currentBuild.currentResult}"
echo "🌐 構建URL: ${env.BUILD_URL}"
sh '''
echo "💾 工作空間大小: \$(du -sh . | cut -f1)"
echo "📁 構建產物: \$(find dist/ -type f 2>/dev/null | wc -l) 個文件"
'''
}
success {
echo "🎉 ===== 構建部署成功 ====="
echo "✅ 前端項目已成功構建並打包"
echo "📦 構建產物位於 dist/ 目錄"
echo "🚀 已部署到宿主機: /var/www/html/reactExamples/"
# 成功了,通過釘釘告知
sh 'curl -X POST "https://oapi.dingtalk.com/robot/send?access_token=access_token" -H "Content-Type: application/json" -d \'{"msgtype": "text", "text": {"content": "Jenkins 構建成功"}} \''
}
failure {
echo "❌ ===== 構建失敗 ====="
echo "🔍 請檢查構建日誌排查問題"
echo "💡 常見問題:依賴安裝失敗、構建腳本錯誤、網絡問題、部署失敗"
# 失敗了,也通過釘釘告知
sh 'curl -X POST "https://oapi.dingtalk.com/robot/send?access_token=access_token" -H "Content-Type: application/json" -d \'{"msgtype": "text", "text": {"content": "Jenkins 構建失敗"}} \''
}
changed {
echo "🔄 構建狀態發生變化"
}
}
}
注意,這裏要特別注意這個代碼
stage('Checkout Code') {
steps {
echo '📥 從GitLab拉取最新代碼...'
git branch: 'main',
url: 'https://ashuai.site:12345/lss-group/react-example.git',
credentialsId: 'gitlab'
sh 'ls -la'
}
}
- 先前配置的TOKEN\_NAME然後把Jenkins的對應url搭配TOKEN\_NAME,作為gitlab的webhook的地址用,這個是讓gitlab的請求能發過來
- 但是在自動化構建過程中,我們在流水線腳本里面,手動去拉取代碼了
- 需要需要指定對應的憑證id
credentialsId: 'gitlab' - 這裏的憑證id和方式一設置憑證id一樣,也是要在gitlab中生成個人令牌
然後和先前的圖示(方式一的第四步)一樣,也要提前準備好憑證
credentialsId: 'gitlab'的值 也就是
這樣的話,在流水線腳本里面拉取代碼才能成功
但是 這個時候 webhook還是跑不通的 我們繼續往下看
8. 批准Pipeline script流水線腳本
腳本不批准,構建會報錯
或者在系統管理裏面 也能看到待批准的腳本
點擊 Approve 按鈕批准
但是 這個時候 webhook還是跑不通的 我們繼續往下看
9. 安裝Build Authorization Token Root Plugin插件
- 前面提到了gitlab的webhook的url的值填成:
https://ashuai.site:29877/buildByToken/build?job=gitlab&token=gitlab-jenkins-token-2025- 使用/buildByToken/build這種方式,一定要搭配Build Authorization Token Root Plugin插件
- 否則會403錯誤,這個是jenkins的安全策略,如下報錯圖
- 原因是Jenkins啓用了CSRF 保護(跨站請求偽造防護) ,而GitLab的Webhook請求中沒有包含有效的
crumb(校驗令牌) - 去設置校驗令牌有些麻煩,所以筆者採用了漸變一些的方式,使用Build Authorization Token Root Plugin這個插件——token認證繞過了CSRF保護
- 安裝啓用
這樣的話,就能夠正常收到gitlab的webhook發來的請求了,測試也成功了
10. 成功構建,自動發佈
也能看到對應構建日誌
釘釘也能收到對應消息
這樣的話,當筆者把代碼直接推送到gitlab的倉庫後,就自動觸發倉庫設置好的webhook發請求給jenkins,jenkins收到請求後,執行pipe line流水線的腳本,拉取代碼,構建項目,傳送到生產環境宿主機裏對應文件夾目錄,然後通知釘釘
- 代碼push提交,系統自動完成構建、測試、部署全過程
- 即為:cicd自動化部署
關於前端部署的三種方式
分為純手動部署,腳本半自動化部署、自動化cicd部署
方式一、純手動部署
- 傳統前端項目如React、Vue的部署
- 第一步,本地執行npm run build把前端項目打包成dist文件夾
- 第二步,通過winscp或xmanager之類的文件傳輸工具遠程鏈接服務器
- 第三步,找到對應服務器上的文件夾目錄(存放生產服務文件)
- 第四步,先刪再增,把現在本機電腦的dist文件夾裏面的靜態資源文件替換掉服務器上的
- NodeJs中間層服務如Express、Koa、Fastify的部署
- 第一步,通過Git 或者winscp更新最新代碼(刪除原來的)
- 第二步,命令行執行pm2 restart myNodeProject重新加載項目
需要人工操作各個步驟,稍微有些耗時,且不優雅穩妥
方式二、腳本半自動化部署
- 傳統前端項目如React、Vue的部署
- Js腳本方式,比如可參考筆者之前的文:https://segmentfault.com/a/1190000044616092
- Shell腳本大致是這樣的,比如我們新建一個deploy.sh腳本
#!/bin/bash
# 前端部署shell腳本
# --------------------------------- 全局的配置 ------------------------------
LOCAL_DIST="./dist" # 當前項目打包dist目錄
SERVER="root@192.168.1.100" # 服務器賬號+IP(例:root@1.2.3.4)
SERVER_DIR="/usr/share/nginx/html" # 服務器存放前端文件的目錄(Nginx默認是這個)
# ----------------------------------------------------------------------
# 1. 本地打包
echo "1. 開始打包..."
npm run build || { echo "打包失敗!"; exit 1; }
# 2. 刪服務器舊文件(先清再傳,避免殘留)
echo "2. 刪除服務器舊文件..."
ssh $SERVER "rm -rf $SERVER_DIR/*" || { echo "刪舊文件失敗!"; exit 1; }
# 3. 傳新文件到服務器
echo "3. 上傳新文件..."
scp -r $LOCAL_DIST/* $SERVER:$SERVER_DIR || { echo "傳文件失敗!"; exit 1; }
# 4. 部署完成
echo "✅ 部署成功!"
這種方式,需要確保腳本權限,終端執行
chmod +x deploy.sh
前端npm run build打包完畢以後,直接./deploy.sh就能直接部署了
不過得輸服務器密碼,可以考慮配置 SSH 密鑰免密登錄,就是本地生成 SSH 密鑰對,再把公鑰複製一份傳到服務器上,這樣就不用輸入密碼了
- NodeJs中間層服務如Express、Koa、Fastify的部署
#!/bin/bash
SERVER="root@192.168.1.100"
SERVER_PROJECT_DIR="/opt/my-node-app"
PM2_APP_NAME="my-node-service" # pm2 管理的服務名稱(啓動時定義的名稱)
# 1. 登錄服務器,拉取倉庫最新代碼(提前做好服務器的ssh秘鑰免密登錄,確保能拉取到gitlab代碼)
echo "1. 拉取最新代碼..."
ssh $SERVER "cd $SERVER_PROJECT_DIR && git pull origin main" || { echo "拉取代碼失敗!"; exit 1; }
# 2. 安裝生產環境依賴(忽略 devDependencies)
echo "2. 安裝依賴..."
ssh $SERVER "cd $SERVER_PROJECT_DIR && npm install --production" || { echo "安裝依賴失敗!"; exit 1; }
# 3. 用 pm2 重啓服務(使新代碼生效)
echo "3. 重啓服務..."
ssh $SERVER "pm2 restart $PM2_APP_NAME" || { echo "重啓服務失敗!"; exit 1; }
# 4. 部署完成
echo "✅ Node.js 服務部署成功!"
shell腳本需要熟悉一下linux命令,推薦這兩個網站 https://www.linuxcool.com/和https://www.masswerk.at/jsuix/ind...
方式三、自動化Jenkins部署
就是上述本文操作流程...
補充bff的jenkins部署
- 流程和上面一樣,依舊是git push提交代碼
- 觸發gitlab的webhook告知jenkins可以拉取最新代碼了
- 然後使用Publish Over SSH 插件
- Add SSH Server,並做對應的信息填寫
- 就可以在流水線腳本里面派發命令了
stages {
stage('派發命令到生產服務器') {
steps {
sshPublisher(publishers: [
sshPublisherDesc(
configName: '生產服務器',
transfers: [
sshTransfer(
execCommand: """
pm2 restart fastify-app-bff || true
"""
)
]
)
])
}
}
}
- 或者直接使用jenkins的執行shell腳本搭配SSH命令(適合複雜場景)
- 篇幅原因,不再贅述
額外的docker部署
- 如果服務器上,要部署多個node項目,多個node版本環境要求嚴格
- 主推使用docker優雅部署,筆者也有一篇docker部署前端項目的文:https://segmentfault.com/a/1190000047275173
- 實際上,項目部署是很靈活的,打包構建這一塊也不一定都得由Jenkins去做(假設Jenkins服務器配置不太好)
- 假設本機配置高,也可以把
npm run build和docker build -t 本地鏡像名:版本號 .交給本機來做 - 然後,給再把構建好的鏡像打個標籤,推送到Harbor倉庫裏面
- 再推送代碼到gitlab倉庫裏面,依舊能夠通過webhook觸發Jenkins的執行
- jenkins不用再去gitlab裏面拉取代碼了,直接把harbor裏面的鏡像拉取過來
- 然後通過 SSH 連接生產服務器,最後執行
docker run啓動容器完成部署
所以,誰來做構建、誰來做傳輸,亦或是派發命令執行,本就是靈活的選擇
總而言之,jenkins是一個很靈活的東西,具體怎麼設置看實際情況...