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

11 KiB
Raw Blame History

前端 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 环境)

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 能访问代码仓库
构建机资源 内存 >= 4GBNODE_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 为完整 URLhttps://api.your-domain.com/admin-api)。

Q4页面 404 白屏

确认 nginx 配置了 try_files $uri $uri/ /index.html,以及 VITE_BASE 与部署路径匹配。