侧边栏壁纸
  • 累计撰写 38 篇文章
  • 累计创建 8 个标签
  • 累计收到 3 条评论

目 录CONTENT

文章目录

实战记录:Jenkins(Linux) 部署 Spring Boot 到 Windows 的全坑排查与终极方案

Administrator
2025-11-30 / 0 评论 / 0 点赞 / 166 阅读 / 0 字
温馨提示:
本文最后更新于2026-04-11,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

这是一篇基于我真实项目经历沉淀的技术复盘。在最近的一次 CI/CD 搭建中,我需要从 Linux 版的 Jenkins 自动部署 Spring Boot 应用到 Windows 服务器,期间遭遇了 Maven 多模块依赖、本地 Jar 包路径错误、SSH 远程进程被杀、Windows 文件锁等一系列“水土不服”的顽疾。

本文由我提供实战场景和调试过程,并借助 AI 助手协助整理和润色,旨在还原最真实的排坑路径。文中提到的每一个报错都是我亲眼所见,每一个脚本都已经过生产环境验证。希望这套最终落地的“Windows 任务计划程序”方案,能为同样受困于 Windows 部署的开发者节省宝贵的时间。


一、 项目背景与目标

最近有一个维护项目,需要搭建一套自动化的 CI/CD 流水线。环境极其典型,也因此坑点重重:

  • 构建环境:Jenkins 运行在 Linux 服务器的 Docker 容器中。
  • 部署目标:一台 Windows Server 云服务器。
  • 项目结构:一个老旧的 Spring Boot 多模块项目(包含 common, common-auth, wxApp 等模块)。
  • 特殊依赖:项目根目录下有个 lib 文件夹,里面存放了一堆没有发布到 Maven 私服的第三方 Jar 包(system scope 依赖)。

核心目标:在 Jenkins 上点击一次“立即构建”,就能自动完成以下全流程:

  1. 编译 common 等底层模块。
  2. 打包主程序 wxApp(包含本地 lib 包)。
  3. 将 Jar 包传输到 Windows 服务器。
  4. 在 Windows 上安全地重启服务。

二、 踩坑之路:问题逐个击破

整个过程并非一帆风顺,我们遇到了几个非常经典的问题。

坑点 1:Maven 多模块与本地 Jar 包的纠缠

刚开始构建时,Maven 频频报错。

现象 A:找不到兄弟模块依赖
主项目 wxApp 依赖 common 模块,构建时提示找不到 common 的 jar 包。

  • 原因:在多模块项目中,必须先把被依赖的模块安装到本地 Maven 仓库。
  • 解决:确保底层模块(如 common)的 Jenkins 构建命令使用的是 mvn clean install,而不仅仅是 package

现象 B:本地 lib 包打包失败或路径错误
项目使用了 <scope>system</scope> 引入本地 lib 下的 Jar 包。最初尝试在 pom.xml 中使用 <resource> 标签手动将 lib 目录复制到 BOOT-INF/lib
结果在 Linux Jenkins 环境下报错 NoSuchFileException,因为它试图将文件复制到 Linux 系统根目录的 /BOOT-INF/ 下。

  • 解决:放弃手动的 <resource> 复制配置,使用 Spring Boot 插件的标准官方支持。
    pom.xmlspring-boot-maven-plugin 插件配置中添加一行即可完美解决:
    <configuration>
        <includeSystemScope>true</includeSystemScope>
    </configuration>
    

坑点 2:Windows SSH 的路径“水土不服”

为了传输文件,我们在 Windows 上安装了 OpenSSH Server,并使用了 Jenkins 的 Publish Over SSH 插件。

现象:配置 Remote Directory 时,如果写绝对路径(如 C:\Users\Administrator\Downloads)或带反斜杠的路径,文件传输经常失败。

  • 解决:Windows 的 OpenSSH 对路径支持比较微妙。最稳妥的方式是使用相对于用户主目录的相对路径,例如直接填写 tempDownloads

坑点 3:令人抓狂的 “0kb 日志” (Jenkins 进程杀手)

这是最耗时的一个坑。文件传过去了,Bat 脚本也执行了,但是 Java 服务就是起不来,生成的日志文件永远是 0kb。

  • 原因:这是 Jenkins 的机制问题。为了防止僵尸进程,Jenkins 在 SSH 会话断开时,会通过 “Process Tree Killer” 杀死该会话衍生出的所有子进程。我们启动的 Java 进程成了牺牲品。
  • 尝试过的(不完美 ai提供的)方案
    • 在 Bat 脚本头部设置 set BUILD_ID=dontKillMe(旧版 Jenkins)。
    • 设置 set JENKINS_NODE_COOKIE=dontKillMe(新版 Jenkins)。
    • 实测结果:都不行。

坑点 4:Windows 文件锁 (Permission denied)

当 Java 程序在 Windows 上运行时,它的 Jar 包文件会被系统锁定。此时如果 Jenkins 试图上传新文件覆盖旧文件,就会报错。

  • 解决思路:不能直接覆盖。必须采用 “上传到临时目录 -> 杀掉旧进程解锁 -> 移动新文件覆盖 -> 启动” 的迂回策略。

三、 终极方案:脱离 Jenkins 的“任务计划程序”

为了彻底解决 SSH 进程被杀和文件锁的问题,我们最终采用了最稳妥的方案:让 Jenkins 只负责“发令”,让 Windows 系统自己负责“执行”

我们利用了 Windows 自带的 “任务计划程序 (Task Scheduler)”

步骤 1:在 Windows 上准备“任务”

  1. 远程登录 Windows 服务器,打开“任务计划程序”。
  2. 创建一个新任务,命名为 RestartWxApp(名字随意,稍后要用到)。
  3. 关键配置
    • 【常规】:勾选“不管用户是否登录都要运行”,勾选“使用最高权限运行”。
    • 【操作】:启动程序,指向我们接下来要写的 Bat 脚本(例如 D:\Downloads\restart.bat)。起始于一定要填脚本所在的文件夹。
    • 【设置】:务必取消勾选“如果运行时间超过以下时间,停止任务”。(否则3天后你的服务会被系统干掉)。

步骤 2:编写极简版部署脚本 (restart.bat)

由于该脚本将由 Windows 系统直接托管运行,与 Jenkins 的 SSH 会话无关,因此不需要任何“免死金牌”变量,脚本可以写得非常纯净。

该脚本实现了:杀进程解锁 -> 从临时文件夹移动新包 -> 自动识别 Jar 包名 -> 后台启动。

文件位置示例:D:\Downloads\restart.bat

@echo off
setlocal enabledelayedexpansion
:: 确保进入正确的目录
cd /d C:\Users\Administrator\Downloads

:: ================= 1. 停止旧服务 =================
:: 强制杀掉 java 进程以释放文件锁
taskkill /F /IM java.exe >nul 2>&1
:: 使用 ping 命令进行延时等待(约3秒),确保句柄释放。
:: 在非交互式后台环境下,ping 比 timeout 命令更稳定。
ping -n 4 127.0.0.1 >nul

:: ================= 2. 更新文件 =================
:: 如果 Jenkins 上传了新包到 temp 目录,则移动过来覆盖
if exist temp\*.jar (
    move /Y temp\*.jar . >nul
    :: 再次等待文件系统索引更新
    ping -n 4 127.0.0.1 >nul
)

:: ================= 3. 自动查找 Jar 包名 =================
set "JAR_NAME="
:: 查找当前目录下以 wx 开头的 jar 包,按时间倒序,取最新的一个
for /f "delims=" %%i in ('dir /b /o-d wx*.jar') do (
    set "JAR_NAME=%%i"
    goto :Found
)
:Found

:: ================= 4. 启动新服务 =================
if not "!JAR_NAME!"=="" (
    :: 使用 start /b 在后台启动 Java。
    :: >nul 2>&1 表示丢弃控制台输出(假设你的应用已经配置了 Logback/Log4j 写日志文件)
    start /b java -jar "!JAR_NAME!" >nul 2>&1
)

exit

步骤 3:配置 Jenkins 构建后操作

最后,回到 Jenkins 的任务配置中。

“Send build artifacts over SSH” 的配置里:

  1. Source files: target/*.jar
  2. Remove prefix: target/
  3. Remote directory: temp (先把文件传到临时目录,避开锁定)
  4. Exec command:
    这里只需要执行一条极其简单的命令,告诉 Windows 触发我们刚才创建的任务即可:
schtasks /Run /TN "RestartWxApp"

四、 总结

通过这套方案,我们成功实现了目标:

  1. Jenkins 负责最擅长的编译和打包,并将文件安全传输到 Windows 的临时区。
  2. Jenkins 发送一个瞬间完成的触发指令,然后立刻断开 SSH 连接,不留后患。
  3. Windows 接收指令后,通过系统级的“任务计划程序”在后台稳定地执行更新和重启脚本。

这套方案完美绕过了 SSH 会话管理和文件锁的限制,是目前 Linux Jenkins 部署到 Windows 环境下成功的方案之一。希望这篇记录能帮助大家少踩几个坑!

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区