尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

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

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 凭据。
返回列表