Files
huachuang/前端Jenkins部署指南.md

348 lines
11 KiB
Markdown
Raw Normal View History

2026-07-22 09:31:40 +08:00
# 前端 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 |
| 构建工具 | Turbomonorepo 编排)+ 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 环境)
```groovy
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 简化版流水线(仅构建 + 归档,手动部署)
```groovy
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 配置很简单:
```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
# 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;"]
```
构建镜像:
```bash
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"`,流水线中已默认设置。
### Q3API 地址不对
检查 `.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` 与部署路径匹配。