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脚本代码

sh 复制代码
# 验证环境
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/ 目录,并且保持两者内容完全一致(会删除宿主机上多余的文件)

js 复制代码
**`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做了代理,如下:

nginx 复制代码
# react的一些示例
location /reactExamples/ {
    alias /var/www/html/reactExamples/;
    try_files $uri $uri/ /reactExamples/index.html;
}

7. 通过钉钉机器人实现钉钉群构建成功消息推送

使用curl命令(要提前新建群,创建好钉钉机器人,用于得知构建成功消息结果)

不清楚如何创建钉钉机器人,可以参考官方文档:open.dingtalk.com/document/di...

提醒一下,别忘了保存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的
  • 也就是构建的任务名字

即:

txt 复制代码
// 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流水线脚本代码

代码如下:

sh 复制代码
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 "🔄 构建状态发生变化"
        }
    }
}

注意,这里要特别注意这个代码

js 复制代码
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脚本方式,比如可参考笔者之前的文:juejin.cn/post/733054...
  • Shell脚本大致是这样的,比如我们新建一个deploy.sh脚本
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 "✅ 部署成功!"

这种方式,需要确保脚本权限,终端执行

bash 复制代码
chmod +x deploy.sh

前端npm run build打包完毕以后,直接./deploy.sh就能直接部署了

不过得输服务器密码,可以考虑配置 SSH 密钥免密登录,就是本地生成 SSH 密钥对,再把公钥复制一份传到服务器上,这样就不用输入密码了

  1. NodeJs中间层服务如Express、Koa、Fastify的部署
sh 复制代码
#!/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命令,推荐这两个网站 www.linuxcool.com/www.masswerk.at/jsuix/index...

方式三、自动化Jenkins部署

就是上述本文操作流程...

补充bff的jenkins部署

  • 流程和上面一样,依旧是git push提交代码
  • 触发gitlab的webhook告知jenkins可以拉取最新代码了
  • 然后使用Publish Over SSH 插件
  • Add SSH Server,并做对应的信息填写
  • 就可以在流水线脚本里面派发命令了
sh 复制代码
stages {
  stage('派发命令到生产服务器') {
    steps {
      sshPublisher(publishers: [
        sshPublisherDesc(
          configName: '生产服务器',
          transfers: [
            sshTransfer(
              execCommand: """
                pm2 restart fastify-app-bff || true
              """
            )
          ]
        )
      ])
    }
  }
}
  • 或者直接使用jenkins的执行shell脚本搭配SSH命令(适合复杂场景)
  • 篇幅原因,不再赘述

额外的docker部署

  • 如果服务器上,要部署多个node项目,多个node版本环境要求严格

  • 主推使用docker优雅部署,笔者也有一篇docker部署前端项目的文:juejin.cn/post/755126...

  • 实际上,项目部署是很灵活的,打包构建这一块也不一定都得由Jenkins去做(假设Jenkins服务器配置不太好)

  • 假设本机配置高,也可以把npm run builddocker build -t 本地镜像名:版本号 .交给本机来做

  • 然后,给再把构建好的镜像打个标签,推送到Harbor仓库里面

  • 再推送代码到gitlab仓库里面,依旧能够通过webhook触发Jenkins的执行

  • jenkins不用再去gitlab里面拉取代码了,直接把harbor里面的镜像拉取过来

  • 然后通过 SSH 连接生产服务器,最后执行 docker run 启动容器完成部署

所以,谁来做构建、谁来做传输,亦或是派发命令执行,本就是灵活的选择

总而言之,jenkins是一个很灵活的东西,具体怎么设置看实际情况...

相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
金刚猿7 小时前
01_虚拟机中间件部署_root 用户安装 docker 容器,配置非root用户权限
docker·中间件·容器
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端