11 KiB
11 KiB
前端 Jenkins 部署指南
一、项目概况
| 项目 | 说明 |
|---|---|
| 源码路径 | nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/ |
| 框架 | Vue 3 + Vite + Ant Design Vue 4(基于 vue-vben-admin 5.7.0) |
| 包管理器 | pnpm 11.7.0(强制,不可用 npm/yarn) |
| Node 版本 | >= 22.18.0(.node-version 指定 24.16.0) |
| 构建工具 | Turbo(monorepo 编排)+ Vite(打包) |
| 构建产物 | apps/web-antdv-next/dist/ |
| 路由模式 | hash 模式(VITE_ROUTER_HISTORY=hash) |
二、环境变量说明
构建时通过 .env.production 控制关键配置,可按环境覆盖。
文件位置: apps/web-antdv-next/.env.production
| 变量 | 默认值 | 说明 |
|---|---|---|
VITE_BASE |
/ |
部署路径前缀,部署在子路径时改为 /admin/ |
VITE_GLOB_API_URL |
http://127.0.0.1:48080/admin-api |
后端 API 地址(完整 URL) |
VITE_ROUTER_HISTORY |
hash |
路由模式,hash 模式无需 nginx 特殊配置 |
VITE_COMPRESS |
none |
压缩方式(none / gzip / brotli) |
VITE_ARCHIVER |
true |
是否生成 dist.zip |
VITE_PWA |
false |
是否启用 PWA |
多环境覆盖方式:在 Jenkins 构建步骤中创建 .env.production.local 文件覆盖 VITE_GLOB_API_URL 等变量,Vite 会优先读取 local 文件。
三、Jenkins 流水线(Pipeline)
3.1 声明式流水线(推荐,使用 Docker 镜像自带 Node 环境)
pipeline {
// 使用 Docker 镜像,自带 Node 24 + pnpm,不需要 Jenkins 宿主机装 Node
agent {
docker {
image 'node:24-slim'
args '-u root --memory=4g'
}
}
// 构建参数,支持按环境选择
parameters {
choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: '部署环境')
string(name: 'API_BASE_URL', defaultValue: 'http://127.0.0.1:48080/admin-api', description: '后端 API 地址')
}
environment {
// 源码子目录(仓库根目录下的相对路径)
SOURCE_DIR = 'nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben'
// pnpm 缓存目录,挂到宿主机避免重复下载
PNPM_HOME = '/root/.local/share/pnpm'
}
stages {
// ==================== 第一步:拉取代码 ====================
stage('Checkout') {
steps {
checkout scm
}
}
// ==================== 第二步:安装依赖 ====================
stage('Setup') {
steps {
dir(env.SOURCE_DIR) {
sh 'corepack enable'
sh 'corepack prepare pnpm@11.7.0 --activate'
// 安装依赖(锁定版本)
sh 'pnpm install --frozen-lockfile'
}
}
}
// ==================== 第三步:代码检查(可选) ====================
stage('Lint') {
steps {
dir(env.SOURCE_DIR) {
sh 'pnpm run lint || true'
}
}
}
// ==================== 第四步:构建 ====================
stage('Build') {
steps {
dir(env.SOURCE_DIR) {
script {
// 根据部署环境覆盖 API 地址
def apiUrl = params.API_BASE_URL
if (params.DEPLOY_ENV == 'prod') {
apiUrl = 'https://api.your-domain.com/admin-api'
} else if (params.DEPLOY_ENV == 'staging') {
apiUrl = 'https://staging-api.your-domain.com/admin-api'
}
// 写入环境变量(Vite 构建时读取)
writeFile file: 'apps/web-antdv-next/.env.production.local', text: """
VITE_GLOB_API_URL=${apiUrl}
""".stripIndent().trim()
// 执行构建
sh '''#!/bin/bash
export NODE_OPTIONS="--max-old-space-size=8192"
pnpm run build --filter=@vben/web-antdv-next
'''
}
}
}
}
// ==================== 第五步:打包产物 ====================
stage('Archive') {
steps {
dir(env.SOURCE_DIR) {
script {
def distPath = 'apps/web-antdv-next/dist'
sh "tar -czf dist-${params.DEPLOY_ENV}.tar.gz -C ${distPath} ."
archiveArtifacts artifacts: "dist-${params.DEPLOY_ENV}.tar.gz", fingerprint: true
}
}
}
}
// ==================== 第六步:部署 ====================
stage('Deploy') {
when {
expression { params.DEPLOY_ENV == 'dev' || params.DEPLOY_ENV == 'staging' || params.DEPLOY_ENV == 'prod' }
}
steps {
dir(env.SOURCE_DIR) {
script {
def serverIp = ''
def deployPath = '/usr/share/nginx/html/admin'
if (params.DEPLOY_ENV == 'dev') { serverIp = '192.168.1.10' }
else if (params.DEPLOY_ENV == 'staging') { serverIp = '192.168.1.20' }
else if (params.DEPLOY_ENV == 'prod') { serverIp = '192.168.1.30' }
sshagent(['deploy-ssh-key']) {
sh """
ssh root@${serverIp} 'mkdir -p ${deployPath}'
scp dist-${params.DEPLOY_ENV}.tar.gz root@${serverIp}:/tmp/
ssh root@${serverIp} '
rm -rf ${deployPath}/*
tar -xzf /tmp/dist-${params.DEPLOY_ENV}.tar.gz -C ${deployPath}
rm -f /tmp/dist-${params.DEPLOY_ENV}.tar.gz
nginx -t && nginx -s reload
'
"""
}
}
}
}
}
}
post {
success { echo "构建成功!环境:${params.DEPLOY_ENV}" }
failure { echo "构建失败!请检查日志。" }
always { cleanWs() }
}
}
3.2 简化版流水线(仅构建 + 归档,手动部署)
pipeline {
agent {
docker {
image 'node:24-slim'
args '-u root --memory=4g'
}
}
parameters {
string(name: 'API_BASE_URL', defaultValue: 'http://127.0.0.1:48080/admin-api', description: '后端 API 地址')
string(name: 'VITE_BASE', defaultValue: '/', description: '部署子路径')
}
environment {
SOURCE_DIR = 'nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben'
}
stages {
stage('Checkout') { steps { checkout scm } }
stage('Install & Build') {
steps {
dir(env.SOURCE_DIR) {
sh 'corepack enable && corepack prepare pnpm@11.7.0 --activate'
sh 'pnpm install --frozen-lockfile'
writeFile file: 'apps/web-antdv-next/.env.production.local', text: """
VITE_GLOB_API_URL=${params.API_BASE_URL}
VITE_BASE=${params.VITE_BASE}
""".stripIndent().trim()
sh '''
export NODE_OPTIONS="--max-old-space-size=8192"
pnpm run build --filter=@vben/web-antdv-next
'''
}
}
}
stage('Package') {
steps {
dir("${env.SOURCE_DIR}/apps/web-antdv-next") {
sh 'tar -czf dist.tar.gz -C dist .'
archiveArtifacts artifacts: 'dist.tar.gz', fingerprint: true
}
}
}
}
post {
always { cleanWs() }
}
}
四、Nginx 部署配置
项目使用 hash 路由,nginx 配置很简单:
server {
listen 80;
server_name admin.your-domain.com;
# 前端静态文件
root /usr/share/nginx/html/admin;
index index.html;
# hash 路由模式,单页应用标准配置
location / {
try_files $uri $uri/ /index.html;
}
# API 反向代理到后端网关
location /admin-api/ {
proxy_pass http://backend-gateway:48080/admin-api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
五、Docker 部署(可选)
项目自带 Dockerfile 但默认构建的是 playground 应用,需要调整。推荐新建一个:
# Dockerfile 放在 yudao-ui-admin-vben/ 目录下
# ============ 构建阶段 ============
FROM node:24-slim AS builder
RUN npm install -g pnpm@11.7.0
WORKDIR /app
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json turbo.json .npmrc ./
COPY internal/ internal/
COPY packages/ packages/
COPY apps/web-antdv-next/ apps/web-antdv-next/
RUN pnpm install --frozen-lockfile
ARG VITE_GLOB_API_URL=http://127.0.0.1:48080/admin-api
ENV VITE_GLOB_API_URL=${VITE_GLOB_API_URL}
RUN NODE_OPTIONS="--max-old-space-size=8192" pnpm run build --filter=@vben/web-antdv-next
# ============ 运行阶段 ============
FROM nginx:stable-alpine
COPY --from=builder /app/apps/web-antdv-next/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
构建镜像:
docker build \
--build-arg VITE_GLOB_API_URL=https://api.your-domain.com/admin-api \
-t nl-admin-ui:latest \
.
六、Jenkins 配置清单
在 Jenkins 上配置前需要准备:
| 配置项 | 说明 |
|---|---|
| Node.js 插件 | 安装 Jenkins NodeJS Plugin,添加 Node.js 24.x 安装 |
| SSH 凭据 | 添加目标服务器的 SSH 私钥(ID 为 deploy-ssh-key) |
| Git 仓库 | 确保 Jenkins 能访问代码仓库 |
| 构建机资源 | 内存 >= 4GB(NODE_OPTIONS=--max-old-space-size=8192) |
| 网络 | 构建机能访问 registry.npmmirror.com(或换内网 npm 镜像) |
七、常见问题
Q1:构建报 pnpm: command not found
Jenkins 环境中 corepack enable 可能不生效,改用 npm install -g pnpm@11.7.0。
Q2:构建 OOM(内存溢出)
调大 Node 内存限制:NODE_OPTIONS="--max-old-space-size=8192",流水线中已默认设置。
Q3:API 地址不对
检查 .env.production.local 是否在构建前正确写入,且 VITE_GLOB_API_URL 为完整 URL(如 https://api.your-domain.com/admin-api)。
Q4:页面 404 白屏
确认 nginx 配置了 try_files $uri $uri/ /index.html,以及 VITE_BASE 与部署路径匹配。