第一次用 Jenkins 从 0 到 1 部署 jar 包到 CentOS 虚拟机并启动,终于搞懂了整个流程 摘要这是我第一次完整跑通 Jenkins 部署 jar 包到虚拟机的流程。以前总觉得 Jenkins 很抽象知道它能自动化构建和部署但并没有真正理解它是怎么把构建机、目标机、制品和启动流程串起来的。这次实操之后我终于把这条链路跑通了手动上传 jar 包到 Jenkins 服务器固定目录再通过 Jenkins Pipeline 调度目标虚拟机这个 Agent 节点把 jar 包下发到目标目录并完成启动。过程中也踩了不少坑比如 Java 版本不一致、节点执行器占用、后台进程被 Jenkins 回收、Apollo 配置中心参数缺失、日志文件权限问题等。这篇文章就把我的第一次实操过程完整记录下来。正文一、为什么我要做这件事这是我第一次不是在本地直接跑 jar也不是靠人工一步一步手敲命令而是通过 Jenkins 去完成一次标准化部署。这次的目标很明确手动把 jar 包上传到 Jenkins 服务器固定目录。Jenkins 读取这个 jar 包。把目标虚拟机挂成 Jenkins 的 Agent 节点。在 Pipeline 中把 jar 包发布到目标虚拟机。停掉旧进程启动新进程并校验服务是否成功启动。一开始我对这件事的理解其实是模糊的总觉得 Jenkins 只是“帮我跑一段脚本”。但真正做下来之后我发现 Jenkins 在这里扮演的角色远不止“执行脚本”这么简单它本质上是在做流程编排。二、先说结论这次部署链路到底是什么这次我最终跑通的链路是这样的我手动把 jar 包上传到 Jenkins 服务器固定目录。Jenkins Pipeline 的第一个阶段在 Jenkins 主节点读取这个 jar 包。通过stash/unstash在不同阶段之间传递制品。目标虚拟机作为 Jenkins 的 Agent 节点接收部署任务。Pipeline 在目标节点本地执行部署脚本。目标节点本地完成 jar 包覆盖、旧进程停止、新进程启动和端口检查。从这个角度看这次我真正理解了一件事Jenkins 不是“遥控目标服务器执行命令”而是“把目标服务器接入成自己的执行节点然后把部署动作调度到目标机本地执行”。三、整体流程时序图Apollo配置中心Java应用目标虚拟机AgentJenkins主节点开发者Apollo配置中心Java应用目标虚拟机AgentJenkins主节点开发者手动上传jar到固定目录触发PipelinePrepare Jar阶段读取jarstash制品调度Deploy To Target阶段unstash制品复制jar到/data/apps停止旧进程使用java 17启动jar拉取配置(env/apollo.meta)返回配置监听8162端口返回启动结果Pipeline成功/失败四、环境准备这次我的环境大概是这样Jenkins 主节点负责读取手动上传的 jar 包。目标服务器是一台 CentOS 虚拟机。目标虚拟机需要挂成 Jenkins 的 Agent 节点。应用是一个 Spring Boot 的 jar 包。应用最终验证必须使用 Java 17 启动。这里最开始踩到的第一个坑就是 Java 版本。Jenkins Agent 启动时报错业务 jar 启动也报错原因都是一样的class file version 61 对应的是 Java 17而目标机默认只有 Java 8。所以第一步不是写 Pipeline而是先在目标机上准备 Java 17。五、第一步在目标虚拟机上准备 Java 17我最后的做法是单独放一套 JDK 17 到目标机不动系统原有环境。例如放到/data/jdk17验证方式/data/jdk17/bin/java-version如果输出的是17说明这一步就准备好了。这一步很关键因为后面无论是 Jenkins Agent 还是业务 jar都依赖 Java 17。六、第二步把目标虚拟机挂成 Jenkins Agent 节点这是我这次最大的认知升级之一。以前我对“Jenkins 部署到目标机”的理解很模糊总觉得是 Jenkins 直接去目标机执行命令。实际跑通之后才明白更标准的做法是让目标虚拟机成为 Jenkins 的 Agent 节点。这样做之后Pipeline 中指定到这个节点的sh命令本质上就是在目标机本地执行。我这次节点的关键配置是节点名称deploy-uat执行器数量1远程工作目录/data/jenkins-agent标签deploy-uat启动方式通过 Java Web 启动代理保存后Jenkins 页面会生成一段 Agent 启动命令。在目标虚拟机执行这段命令后节点就能连回 Jenkins。七、第三步在 Jenkins 主节点准备 jar 包这次我没有让 Jenkins 从 Git 拉代码构建而是选择了一个更直观的方式手动把 jar 包放到 Jenkins 主节点固定目录。例如/tmp/jar/space-construction-cost-seven-provider.jar这样 Jenkins 的第一阶段只需要做两件事判断这个文件是否存在。复制到当前工作区供后续stash使用。这个方式虽然不是最完整的 CI但非常适合第一次理解 Jenkins 的部署流程。八、第四步编写 Pipeline最终的 Pipeline 思路其实很清晰1. Prepare Jar 阶段运行在 Jenkins 主节点作用是检查 jar 包是否存在。拷贝到当前工作区。通过stash进行暂存。2. Deploy To Target 阶段运行在deploy-uat节点作用是unstash拿到 jar 包。复制到目标目录/data/apps。停旧进程。启动新进程。检查端口是否监听成功。这一步也让我第一次真正理解了 Jenkins Pipeline 的价值同一个流水线不同阶段可以运行在不同节点上。九、我这次踩过的几个典型坑1. Jenkins Agent 和业务 jar 都要求 Java 17一开始我以为只有 Jenkins Agent 需要 Java 17业务 jar 还能继续用 Java 8。结果实际验证发现业务 jar 本身也是按 Java 17 编译的。这意味着部署时不能再用系统默认 Java 8而必须显式指定/data/jdk17/bin/java2. Pipeline 顶层节点和阶段节点重复申请导致一直等待执行器我一开始把最外层 Pipeline 也绑定到了部署节点结果进入部署阶段时又要申请同一个节点但该节点只有 1 个执行器于是流水线就卡住了。后来定位出来后最终写法改成agent none然后每个 stage 自己申请节点。这个坑非常典型也非常容易误判成“节点没连上”。3. 后台进程被 Jenkins 构建结束后清理掉最开始应用明明已经启动了但构建结束后进程就没了。后来才知道 Jenkins 会清理构建过程中拉起的子进程所以必须在启动前加exportJENKINS_NODE_COOKIEdontKillMe如果不加这句部署看起来成功服务实际上活不下来。4. Apollo 配置中心参数缺失导致服务起不来后面日志里提示env为空apollo.meta未配置回退到默认地址最终解析失败最后确认需要补充启动参数-DenvUAT-Dapollo.metahttp://192.168.251.8/apollo这一步也让我意识到Jenkins 只是负责“执行部署”真正决定服务能否成功启动的还是应用本身依赖的配置。5. 日志权限问题后面我手动切换成普通用户验证启动时又遇到了日志文件写不进去的问题。最终发现是因为之前日志文件是 root 进程创建的后续普通用户没有写权限。这说明部署里不仅有“脚本”和“命令”还涉及启动用户日志目录权限文件归属运行环境一致性这些在真实环境里都非常重要。十、部署成功后我真正理解了什么这次我最大的收获不是“会写一段 Pipeline 脚本”了而是终于把 Jenkins 在部署里的角色理解清楚了。Jenkins 在这个流程里做的事情本质上是编排部署步骤调度不同节点执行不同阶段在阶段之间传递制品自动执行部署动作记录结果和日志也就是说Jenkins 的核心价值不是“帮我省几条命令”而是把部署过程工程化、标准化、可追踪化。十一、总结这是我第一次从 0 到 1 跑通 Jenkins 部署 jar 包到虚拟机并启动。虽然中间踩了很多坑但也正因为这些坑我对 Jenkins 的理解从“知道它是干什么的”变成了“知道它在部署里到底是怎么工作的”。如果用一句话总结这次收获那就是Jenkins 部署 jar 包的本质是通过 Pipeline 把目标虚拟机挂成执行节点再由节点本地完成制品部署和服务启动。结尾这是我第一次完整跑通 Jenkins 部署 jar 包到虚拟机的全过程。对我来说最大的收获不是“终于部署成功了”而是第一次真正理解了 Jenkins、Pipeline、Agent 节点和应用启动之间的关系。后面我也会继续整理一篇关于 Jenkins 在 CI/CD 中作用的理解文章把这次 jar 部署和公司里 DevOps 中 Git Docker 的流程串起来。