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

发布时间:2026/8/11 15:11:58
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 凭据。

相关新闻