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是一个很灵活的东西,具体怎么设置看实际情况...

相关推荐
小刘鸭地下城4 小时前
UV、PV、P95:三大核心业务指标的全维度解析
前端·性能优化
张可爱4 小时前
20251031 -(Vue3 + Python)花瓣网图片爬取笔记 🎨
前端
老前端的功夫4 小时前
Web应用的"永生"之术:PWA落地与实践深度指南
前端·面试
jump6804 小时前
JWT 和 传统session登录的区别
前端
usdoc文档预览4 小时前
前端Word文件在线预览-文件预览修改背景色,动态修改在线预览颜色
前端·word
golang学习记4 小时前
从0死磕全栈之Next.js 中的 NextRequest 使用指南
前端
神秘的猪头4 小时前
ES6 字符串模板与现代 JavaScript 编程教学
前端·javascript
Java爱好狂.4 小时前
接上篇:如何在项目中实现ES查询功能?
java·运维·jenkins·es·java面试·后端开发·java程序员
kaikaile19955 小时前
如何使用React和Redux构建现代化Web应用程序
前端·react.js·前端框架