動態

詳情 返回 返回

50多張圖詳細記錄——使用Jenkins完成前端項目CICD自動化部署教程(不踩坑!) - 動態 詳情

前言概述

本文記錄使用docker安裝jenkins以後,推送代碼到giteegitlab上,而後執行構建前端項目的過程,對應cicd的操作設置流程環節

包含:手動觸發構建和webhook自動化構建

50多張圖,詳細記錄過程,且按照筆者的這篇文章操作,不會踩坑被卡好久(筆者已經踩過坑了)

前端CICD自動化部署角色分工

前端開發過程中,關於項目發佈上線,標準化而言涉及四個角色(假設是四台硬件設備)

四個角色分工

  1. 自己的電腦——用來寫代碼(通過git push把本機代碼推送到gitlab服務器)
  2. gitlab服務器——用來存代碼(注意,gitlab上面的代碼,一般無法直接pushjenkins服務器上,需要jenkins主動從gitlab那裏去“撈”代碼
  3. jenkins服務器——用來執行自動化cicd操作(jenkins把“撈”到的代碼執行npm run build構建操作,並把構建產物傳送到生產服務器)
  4. 生產服務器——存放前端構建產物,並使用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、支付寶完成、微信支付完成等

核心邏輯

  1. WebHook 的本質是 “事件回調”:當gitlab倉庫發生提交、合併等操作時,gitlab會向我配置的jenkins回調地址,發送一個包含事件信息(如提交作者、分支、變動文件列表)的http請求
  2. 這個請求僅起到 “觸發信號” 的作用,不攜帶任何代碼文件本身
  3. 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的請求能發過來
  • 但是在自動化構建過程中,我們在流水線腳本里面,手動去拉取代碼了
  • 需要需要指定對應的憑證idcredentialsId: '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部署

方式一、純手動部署

  1. 傳統前端項目如React、Vue的部署
  • 第一步,本地執行npm run build把前端項目打包成dist文件夾
  • 第二步,通過winscp或xmanager之類的文件傳輸工具遠程鏈接服務器
  • 第三步,找到對應服務器上的文件夾目錄(存放生產服務文件)
  • 第四步,先刪再增,把現在本機電腦的dist文件夾裏面的靜態資源文件替換掉服務器上的
  1. NodeJs中間層服務如Express、Koa、Fastify的部署
  • 第一步,通過Git 或者winscp更新最新代碼(刪除原來的)
  • 第二步,命令行執行pm2 restart myNodeProject重新加載項目

需要人工操作各個步驟,稍微有些耗時,且不優雅穩妥

方式二、腳本半自動化部署

  1. 傳統前端項目如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 密鑰對,再把公鑰複製一份傳到服務器上,這樣就不用輸入密碼了

  1. 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 builddocker build -t 本地鏡像名:版本號 .交給本機來做
  • 然後,給再把構建好的鏡像打個標籤,推送到Harbor倉庫裏面
  • 再推送代碼到gitlab倉庫裏面,依舊能夠通過webhook觸發Jenkins的執行
  • jenkins不用再去gitlab裏面拉取代碼了,直接把harbor裏面的鏡像拉取過來
  • 然後通過 SSH 連接生產服務器,最後執行 docker run 啓動容器完成部署

所以,誰來做構建、誰來做傳輸,亦或是派發命令執行,本就是靈活的選擇

總而言之,jenkins是一個很靈活的東西,具體怎麼設置看實際情況...
user avatar kobe_fans_zxc 頭像 gushiio 頭像 wuyagege 頭像 yangy5hqv 頭像 mihuartuanr 頭像 liulhf 頭像 awbeci 頭像 huobaodejianpan 頭像 carlsonblog 頭像 familyaboveall 頭像 xiaoxxuejishu 頭像 ouysh1981 頭像
點贊 18 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.