Windows 使用 Docker Jenkins 自动部署 Vue 前端项目

发布时间:2026/10/2 7:28:36

Windows 使用 Docker Jenkins 自动部署 Vue 前端项目 Windows 使用 Docker Jenkins 自动部署 Vue 前端项目本文以 joy-admin-frontend 为例将 Mac/Linux 流程迁移到 Windows 10/11。前端使用 Vue 2、Vue CLI 4、webpack 4构建环境固定为 Node.js 16.20.2、npm 8.19.4。发布链路Codeup - Jenkins Linux 容器构建 dist - 共享挂载写入 Windows D:/work 发布目录 - Windows OpenSSH 执行 PowerShell 切换 current - Windows Nginx 检查并 reload - HTTP 验证失败时恢复旧版本Jenkins 在 Docker Desktop 的 Linux 容器内运行所以 Pipeline 使用 shWindows 主机命令使用 PowerShell。一、目录和 Nginx先手动创建 D:/workNew-Item-ItemType Directory-Force-PathD:/work/joy-admin-frontend-deploy/releases,D:/work/joy-admin-frontend-deploy/scripts,D:/work/joy-admin-deploy/jenkins,D:/work/nginx,D:/work/sshNew-Item-ItemType File-Force-PathD:/work/joy-admin-deploy/jenkins/known_hosts|Out-Null目录结构D:/work/ ├── joy-admin-frontend-deploy/ │ ├── releases/ │ └── scripts/ ├── nginx/ │ ├── nginx.exe │ └── conf/ └── ssh/将 Windows 版 Nginx 解压到 D:/work/nginx检查Test-PathD:/work/nginx/nginx.exeTest-PathD:/work/nginx/conf/nginx.conf在 nginx.conf 的 http 块中加入include D:/work/nginx/conf/nginx-joy-admin.conf;创建 nginx-joy-admin.confserver { listen 80; server_name dev.joy.admin.com; root D:/work/joy-admin-frontend-deploy/current; index index.html; location /prod-api/ { proxy_pass http://127.0.0.1:9080/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 10s; proxy_read_timeout 60s; } location /index.html { add_header Cache-Control no-cache, no-store, must-revalidate; } location /static/ { expires 7d; add_header Cache-Control public, max-age604800, immutable; try_files $uri 404; } location / { try_files $uri $uri/ /index.html; } gzip on; gzip_min_length 1k; gzip_comp_level 6; gzip_types text/plain text/css application/javascript application/json image/svgxml; }检查和启动 NginxD:/work/nginx/nginx.exe-t-p D:/work/nginx-c D:/work/nginx/conf/nginx.confStart-Process-FilePathD:/work/nginx/nginx.exe-WorkingDirectoryD:/work/nginx已运行时只 reloadD:/work/nginx/nginx.exe-s reload-p D:/work/nginx-c D:/work/nginx/conf/nginx.conf本机测试域名时管理员 PowerShell 执行Add-Content-PathC:/Windows/System32/drivers/etc/hosts-Value127.0.0.1 dev.joy.admin.comipconfig/flushdns二、Windows OpenSSHJenkins 通过 SSH 调用 Windows PowerShell 和 Nginx不使用 rsync 传输文件文件由 Docker Desktop 共享挂载提供。管理员 PowerShell 安装并启动 OpenSSH ServerAdd-WindowsCapability-Online-Name OpenSSH.Server~~~~0.0.1.0Start-ServicesshdSet-Service-Name sshd-StartupType Automaticif(-not(Get-NetFirewallRule-Name sshd-ErrorAction SilentlyContinue)){New-NetFirewallRule-Name sshd-DisplayNameOpenSSH Server-Enabled True-Direction Inbound-Protocol TCP-Action Allow-LocalPort 22}创建普通部署用户$PasswordRead-Host输入 jenkinsdeploy 密码-AsSecureStringNew-LocalUser-Namejenkinsdeploy-Password$Password-DescriptionJenkins frontend deployment userAdd-LocalGroupMember-GroupUsers-Memberjenkinsdeploy在 Windows 主机生成密钥ssh-keygen-t ed25519-Cjenkins-nginx-deploy-fD:/work/ssh/jenkins-nginx-host写入授权文件$SshDirC:/Users/jenkinsdeploy/.sshNew-Item-ItemType Directory-Force-Path$SshDir|Out-NullCopy-ItemD:/work/ssh/jenkins-nginx-host.pub$SshDir/authorized_keys-Force测试ssh-i D:/work/ssh/jenkins-nginx-host-o IdentitiesOnlyyes jenkinsdeploy127.0.0.1whoami输出 jenkinsdeploy 即可。Nginx 最好由该部署用户启动确保该用户可以执行 reload。三、修改 Jenkins Compose 挂载在 Windows Jenkins Compose 的 jenkins 服务 volumes 增加-D:/work/joy-admin-frontend-deploy:/frontend-deploy相关片段volumes:-jenkins_home:/var/jenkins_home-jenkins_npm_cache:/root/.npm-/var/run/docker.sock:/var/run/docker.sock-./jenkins/known_hosts:/root/.ssh/known_hosts:ro-D:/work/joy-admin-frontend-deploy:/frontend-deploy重新创建 JenkinsSet-LocationD:/work/joy-admin-deploydocker compose--env-file.env-f compose.jenkins.yml up-d--force-recreate docker exec joy-jenkins test-d/frontend-deploy/releasesJenkins 容器能访问 /frontend-deploy/releases 才能继续。从 Jenkins 容器扫描 Windows OpenSSH 主机密钥写入宿主机挂载文件docker exec joy-jenkins ssh-keyscan-T 10-H-t ed25519,rsa host.docker.internal|Set-Content-Encoding asciiD:/work/joy-admin-deploy/jenkins/known_hostsdocker exec joy-jenkins ssh-keygen-lf/root/.ssh/known_hosts确认指纹和 Windows 主机一致后再继续。known_hosts 文件通过挂载实时提供给 Jenkins。四、创建 current 切换脚本创建 D:/work/joy-admin-frontend-deploy/scripts/switch-current.ps1param([Parameter(Mandatory $true)][ValidatePattern(^[A-Za-z0-9._-]$)][string]$Tag)$ErrorActionPreferenceStop$RootD:/work/joy-admin-frontend-deploy$NginxExeD:/work/nginx/nginx.exe$NginxPrefixD:/work/nginx$NginxConfD:/work/nginx/conf/nginx.conf$ReleaseJoin-Path$Rootreleases/$Tag$CurrentJoin-Path$Rootcurrent$PreviousJoin-Path$Rootcurrent.previous$PreviousTagFileJoin-Path$Rootprevious-tag.txtif(-not(Test-Path(Join-Path$Releaseindex.html))){throw发布目录不存在:$Release}$PreviousTagif(Test-Path$Current){$LinkGet-Item-LiteralPath$Current$LinkTarget$Link.Targetif(-not$LinkTarget){$LinkTarget$Link.LinkTarget}if($LinkTarget){$PreviousTagSplit-Path-Leaf$LinkTarget}}Set-Content-Path$PreviousTagFile-Value$PreviousTag-Encoding asciiif(Test-Path$Previous){Remove-Item-LiteralPath$Previous-Force}if(Test-Path$Current){Move-Item-LiteralPath$Current-Destination$Previous-Force}try{New-Item-ItemType Junction-Path$Current-Target$Release|Out-Null$NginxExe-t-p$NginxPrefix-c$NginxConfif($LASTEXITCODE-ne0){throwNginx 配置检查失败}$NginxExe-s reload-p$NginxPrefix-c$NginxConfif($LASTEXITCODE-ne0){throwNginx reload 失败}Write-Output切换成功:$Tagexit0}catch{if(Test-Path$Current){Remove-Item-LiteralPath$Current-Force}if($PreviousTag){$OldReleaseJoin-Path$Rootreleases/$PreviousTagNew-Item-ItemType Junction-Path$Current-Target$OldRelease|Out-Null$NginxExe-s reload-p$NginxPrefix-c$NginxConf|Out-Null}Write-Error$_exit1}五、Jenkins 凭据和 PipelineJenkins 中配置codeup-ssh Codeup 代码拉取私钥 nginx-host-ssh jenkinsdeploy 用户的 Windows SSH 私钥nginx-host-ssh 凭据填写KindSSH Username with private key Usernamejenkinsdeploy Private KeyD:/work/ssh/jenkins-nginx-host 的私钥内容 IDnginx-host-ssh创建 New Item - joy-admin-frontend - Pipeline。完整脚本如下替换 Codeup 仓库地址pipeline{agent any parameters{choice(name:ACTION,choices:[DEPLOY,ROLLBACK],description:发布或回滚)string(name:GIT_BRANCH,defaultValue:main,description:前端分支)string(name:FRONTEND_URL,defaultValue:http://dev.joy.admin.com,description:前端访问地址)string(name:ROLLBACK_TAG,defaultValue:,description:回滚版本)}options{timestamps()timeout(time:45,unit:MINUTES)disableConcurrentBuilds()skipDefaultCheckout(true)buildDiscarder(logRotator(numToKeepStr:30))}environment{DEPLOY_HOSThost.docker.internalDEPLOY_USERjenkinsdeployDEPLOY_ROOT/frontend-deployHOST_DEPLOY_ROOTD:/work/joy-admin-frontend-deployFRONTEND_HOSTdev.joy.admin.comNGINX_PORT80SWITCH_SCRIPTD:/work/joy-admin-frontend-deploy/scripts/switch-current.ps1NODE_OPTIONS--max-old-space-size4096}stages{stage(拉取代码){when{expression{params.ACTIONDEPLOY}}steps{deleteDir()sshagent(credentials:[codeup-ssh]){sh set -eu export GIT_SSH_COMMANDssh -o StrictHostKeyCheckingyes -o UserKnownHostsFile/root/.ssh/known_hosts git clone --depth 1 --branch $GIT_BRANCH gitcodeup.aliyun.com:组织/仓库.git frontend }script{env.GIT_SHORTsh(script:git -C frontend rev-parse --short8 HEAD,returnStdout:true).trim()env.RELEASE_TAG${env.BUILD_NUMBER}-${env.GIT_SHORT}}}}stage(构建前端){when{expression{params.ACTIONDEPLOY}}steps{dir(frontend){sh set -eu printf %s\n VUE_APP_BASE_API/prod-api VUE_APP_FRONT_URL$FRONTEND_URL .env.production.local node -v npm -v npm ci --no-audit --no-fund npm run build:prod test -f dist/index.html test -d dist/static }}}stage(发布或回滚){steps{sshagent(credentials:[nginx-host-ssh]){script{if(params.ACTIONROLLBACK){if(!params.ROLLBACK_TAG.trim()){error(回滚必须填写 ROLLBACK_TAG)}env.TARGET_TAGparams.ROLLBACK_TAG.trim()}else{env.TARGET_TAGenv.RELEASE_TAG}sh set -eu case $TARGET_TAG in *[!A-Za-z0-9._-]*) echo 非法版本标签; exit 1 ;; esac RELEASE_DIR/frontend-deploy/releases/$TARGET_TAG REMOTE$DEPLOY_USER$DEPLOY_HOST SSH_OPTS-o PreferredAuthenticationspublickey -o PasswordAuthenticationno -o StrictHostKeyCheckingyes -o UserKnownHostsFile/root/.ssh/known_hosts if [ $ACTION DEPLOY ]; then rm -rf $RELEASE_DIR mkdir -p $RELEASE_DIR cp -a frontend/dist/. $RELEASE_DIR/ else ssh $SSH_OPTS $REMOTE powershell.exe -NoProfile -NonInteractive -Command \if (-not (Test-Path $HOST_DEPLOY_ROOT/releases/$TARGET_TAG/index.html)) { exit 1 }\ fi ssh $SSH_OPTS $REMOTE powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File $SWITCH_SCRIPT -Tag $TARGET_TAG if curl --fail --silent --show-error -H Host: $FRONTEND_HOST http://$DEPLOY_HOST:$NGINX_PORT/ /dev/null; then echo 前端发布成功: $TARGET_TAG exit 0 fi PREVIOUS_TAG$(ssh $SSH_OPTS $REMOTE powershell.exe -NoProfile -NonInteractive -Command \(Get-Content $HOST_DEPLOY_ROOT/previous-tag.txt)\ | tr -d \r) if [ -n $PREVIOUS_TAG ]; then ssh $SSH_OPTS $REMOTE powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File $SWITCH_SCRIPT -Tag $PREVIOUS_TAG fi exit 1 }}}}}post{always{deleteDir()}}}Pipeline 中继续使用 sh因为 Jenkins 运行在 Linux 容器内。六、发布、回滚和验证首次发布参数ACTIONDEPLOY GIT_BRANCHmain FRONTEND_URLhttp://dev.joy.admin.com ROLLBACK_TAG留空版本目录格式D:/work/joy-admin-frontend-deploy/releases/构建号-Git短Commit回滚参数ACTIONROLLBACK ROLLBACK_TAG需要恢复的版本目录名Windows 验证Get-ItemD:/work/joy-admin-frontend-deploy/current|Format-List*curl.exe-I-HHost: dev.joy.admin.comhttp://127.0.0.1/ curl.exe-I-HHost: dev.joy.admin.comhttp://127.0.0.1/dashboard curl.exe-i-HHost: dev.joy.admin.comhttp://127.0.0.1/prod-api/七、常见问题Jenkins 不能访问发布目录docker exec joy-jenkins test-d/frontend-deploy/releases检查 Jenkins Compose 是否挂载 D:/work/joy-admin-frontend-deploy 到 /frontend-deploy。npm ci 失败确认仓库包含 package-lock.json并且 Jenkins 中的 Node.js 版本为 16.20.2、npm 版本为 8.19.4。不要在发布流水线中自动生成新的 lock 文件如果 lockfile 版本与 npm 8 不兼容应统一项目锁文件和构建镜像中的 npm 版本后再发布。Windows OpenSSH 连接失败Get-ServicesshdGet-NetFirewallRule-Name sshd ssh-i D:/work/ssh/jenkins-nginx-host jenkinsdeploy127.0.0.1whoamiJunction 创建失败以管理员 PowerShell 执行或开启 Windows Developer Mode并确保部署用户对 D:/work 有读写权限。不要预先创建普通 current 目录。Nginx reload 失败D:/work/nginx/nginx.exe-t-p D:/work/nginx-c D:/work/nginx/conf/nginx.confGet-Processnginx确认 Nginx 由 jenkinsdeploy 用户启动或者该用户拥有控制 Nginx 进程的权限。/prod-api 返回 403 或 404proxy_pass 末尾必须保留 /这样 /prod-api/login 会转发为后端 /login。跨域部署时后端 CORS_ALLOWED_ORIGINS 必须包含实际前端来源。八、安全和迁移Jenkins 使用普通 Windows 用户不要使用管理员账号。Jenkins 只绑定 127.0.0.1不直接暴露公网。Jenkins Docker Socket 等同于宿主机 Docker 管理权限。SSH 私钥、环境文件不得提交 Git。releases 目录保留最近 5 至 10 个版本。Nginx 配置变更必须先 nginx -t再 reload。迁移到新 Windows 机器时重新配置 D:/work、Nginx、OpenSSH、known_hosts 和 Jenkins 凭据。
延伸阅读

更多相关文章

2026/10/2 7:28:28

Ryujinx:终极跨平台Switch模拟器完全指南

Ryujinx:终极跨平台Switch模拟器完全指南 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx 想要在Windows、macOS或Linux系统上免费畅玩任天堂Switch独占游戏吗?R…

2026/10/2 7:28:17

PyTorch线性回归实践:从数据构造到梯度下降全流程解析

1. 这个项目到底在做什么:先看清线性回归在深度学习里的位置线性回归这个项目,我做了不下二十遍。从最早用 Excel 做趋势线,到后来用 sklearn 一行代码拟合,再到用 PyTorch 手写训练循环,每一遍的理解层次都不一样。很…

2026/10/2 7:28:17

GitHub日榜全解读:从趋势算法到项目落地评估

每天早上十点,我基本会先打开GitHub Trending页面看一眼。这不是闲得慌,而是这些年养成的职业习惯:GitHub日榜其实是一个异常真实的技术风向标,今天(2026-09-26)的榜单上,AI基础设施、智能体工具…

2026/10/2 7:28:17

UiPath安装配置指南:从下载docx到激活与Windows-Legacy兼容

简介:面向对办公自动化与RPA感兴趣的职场新人,尤其适合RPA之家社群中希望快速上手的读者,这份docx文档系统整理了UiPath从官网下载到本机安装的完整流程。内容从进入官方首页、区分社区版与企业版的选择逻辑,到注册环节中个人邮箱…

2026/10/2 7:28:17

Delphi 13.1 安装 DevExpress VCL 25.2.7 完整指南:从解压到避坑

简介:DevExpress VCL Controls 25.2.7 HH 是面向 Delphi 13.1(RAD Studio 13.1 Alexandria)开发者的一套企业级可视化组件库,适合需要构建 Windows 桌面业务系统的中高级程序员。它覆盖数据展示、编辑输入、导航布局、图表分析、报…

2026/10/1 5:21:14

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/10/1 17:09:46

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 10:48:55

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/2 0:02:57

PWN入门:从栈溢出原理到ROP链实战

1. 这不是“学PWN”,是重新理解你每天敲的每一行C代码我第一次在CTF赛场上写出能控制程序流的exp时,手抖得连gdb的c命令都输错三次。那道题只有23行C代码,一个gets()调用,一个printf(),一个return——它甚至没开NX&…

2026/10/2 0:02:57

Windows下cudaMallocHost显存占用之谜:WDDM与TCC模式差异及优化方案

1. 一个反直觉的显存占用现象第一次在 Windows 上看到cudaMallocHost把显存吃掉的时候,我的反应是打开任务管理器反复确认了三遍。明明调用的是主机端锁页内存分配,按 CUDA 文档的说法,这块内存应该落在系统 RAM 里,跟 GPU 的显存…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑