Files
huachuang/前端Jenkins部署指南.md
2026-07-22 09:31:40 +08:00

348 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 前端 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` 与部署路径匹配。