news 2026/10/15 14:55:17

CentOS 7 上 Jenkins 远程部署 Spring Boot 实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CentOS 7 上 Jenkins 远程部署 Spring Boot 实战指南

1. 为什么在当下还值得聊 CentOS 7 上的 Jenkins 远程部署

先把结论摆在前面:这套组合看起来"老",但在大量中小团队和传统行业的内部系统里,CentOS 7 + Jenkins + Spring Boot 依然是主力生产配置。原因很现实——服务器操作系统不是说换就换的,很多业务机器跑着跑了五六年的 CentOS 7,上面还压着一堆历史服务,运维不可能为了追新去动它。而 Spring Boot 的 jar 包部署方式又足够简单,一个java -jar就能起服务,天然适合用 Jenkins 做自动化。

所以这篇内容要解决的核心问题很明确:怎么在一台 CentOS 7 机器上把 Jenkins 装起来、配好,然后让它自动把 Spring Boot 项目构建、打包、推送到远程服务器并重启服务。整套流程走通之后,你提交一次代码,剩下的编译、传输、停旧服务、起新服务全部自动完成,省掉的是每次手动上传 jar 包、手动 kill 进程、手动 nohup 启动的那一堆重复劳动。

适合谁看?如果你是会写 Spring Boot 但没怎么碰过 CI/CD 的后端开发,或者你是刚接手运维工作、需要给团队搭一套最简可用的自动部署流水线的人,这篇就是给你写的。我不打算堆一堆概念,而是按"我实际搭的时候是怎么一步步做的"来讲,包括踩过的坑和参数为什么这么设。

需要提前说清楚一点:Jenkins 的部署方式有很多种,我这里选的是最朴素也最稳的一种——Jenkins 通过 SSH 把构建产物推到目标服务器,再远程执行启动脚本。不用 Docker、不用 Kubernetes、不用各种花哨的插件编排,就是 SSH + Shell。为什么这么选?因为依赖最少,出问题最好排查,任何一台能 SSH 的 Linux 机器都能当目标机,对 CentOS 7 这种环境兼容性最好。

2. 环境准备与 Jenkins 安装的完整拆解

2.1 安装前的环境确认与依赖梳理

动手之前先把环境摸清楚,这一步很多人跳过,结果装到一半报错再回头查,浪费时间。Jenkins 本质是一个 Java 应用,所以第一件事是确认 JDK。CentOS 7 自带的 OpenJDK 版本往往偏低,而较新版本的 Jenkins 对 JDK 有明确要求,我一般直接装 JDK 11 或 JDK 17,具体看 Jenkins 版本。

确认命令很简单:

java -version

如果没装或者版本不对,先处理 JDK。这里有个经验:Jenkins 用的 JDK 和你的 Spring Boot 项目编译用的 JDK 可以是同一个,但要注意版本匹配。比如你的项目是 JDK 8 编译的,Jenkins 跑在 JDK 17 上没问题,但 Maven 编译时要用 JDK 8 的JAVA_HOME,这个后面配置里会讲。

除了 JDK,还要确认几件事:

  • 服务器时间是否准确,date看一下,时间不对会导致 Jenkins 的定时任务和构建记录时间错乱;
  • 内存是否够用,Jenkins 本身加上 Maven 构建,建议至少 2G 内存,1G 的机器构建大项目容易 OOM;
  • 磁盘空间,df -h看一眼,构建产物和历史记录会占空间;
  • 防火墙和 SELinux 的状态,这个后面单独说,是新手最容易卡住的地方。

提示:CentOS 7 默认开启 SELinux,Jenkins 访问某些目录或端口时可能被拦。如果排查半天权限都对但还是失败,先临时setenforce 0验证是不是 SELinux 的问题,确认后再决定是关掉还是加策略。

2.2 Jenkins 的安装方式选择与实操

Jenkins 在 CentOS 7 上的安装方式主要有三种:yum 源安装、war 包直接跑、Docker 安装。我这里推荐yum 源安装,原因是它会把 Jenkins 注册成 systemd 服务,开机自启、日志管理、启停命令都是标准化的,维护起来最省心。war 包方式虽然灵活,但你要自己写服务脚本,对新手不友好。

先添加 Jenkins 的 yum 源。注意 Jenkins 的源地址会随版本更新变化,实际操作时以官方当前提供的为准。添加完源之后:

sudo yum install -y jenkins

安装完成后,Jenkins 默认监听 8080 端口。启动服务:

sudo systemctl start jenkins sudo systemctl enable jenkins sudo systemctl status jenkins

status看到 active (running) 就说明起来了。如果起不来,八成是 JDK 路径问题,去看/var/log/jenkins/jenkins.log,日志里会写得很清楚。

接下来是初始化。浏览器访问http://服务器IP:8080,会要求输入初始管理员密码,位置在:

cat /var/lib/jenkins/secrets/initialAdminPassword

把这一串贴进去,然后选择安装推荐插件。这一步会联网下载插件,如果服务器网络慢,可能会卡很久甚至失败。我的建议是:先装推荐插件,装完后如果发现某个插件没装上,再单独去插件管理里补装,不要一上来就全选,容易超时。

初始化完成后创建管理员账号,Jenkins 就基本可用了。

2.3 关键插件安装与全局工具配置

Jenkins 装好只是空壳,要能构建 Spring Boot 项目,还得配几个东西。

第一是Maven。Jenkins 可以在全局工具配置里自动下载 Maven,也可以指定服务器上已装的 Maven。我倾向于让 Jenkins 自动安装,版本可控。进入"系统管理 → 全局工具配置",找到 Maven,点新增,勾选自动安装,选一个稳定版本。

第二是JDK。同样在全局工具配置里,可以配置多个 JDK 版本。这里就是前面提到的关键点:如果你的项目要用 JDK 8 编译,就在这里配一个 JDK 8,构建时指定用它,而不是用 Jenkins 自身运行的那个 JDK。

第三是Git 插件。推荐插件里通常已经包含,确认一下"系统管理 → 插件管理"里有 Git plugin 即可。如果代码托管在需要认证的仓库,还要配好凭据。

第四是Publish Over SSH 插件。这是远程部署的核心插件,它让 Jenkins 能通过 SSH 连到目标服务器执行命令、传文件。在插件管理里搜索安装,装完重启 Jenkins。

注意:Publish Over SSH 这个插件虽然经典好用,但它的配置项有点绕,尤其是密码和密钥两种认证方式的填法不一样,后面配置环节我会详细说。

全局工具配置里还有一个容易忽略的点:Maven 的 settings.xml。如果你们的 Maven 私服需要认证,或者要指定镜像仓库加速,就得在 Jenkins 里配置一个自定义的 settings.xml 文件,然后在构建时引用。不配的话,构建时拉依赖可能又慢又容易失败。

3. 远程部署的核心机制与配置细节

3.1 Publish Over SSH 的工作原理与认证配置

要理解远程部署,先得理解 Publish Over SSH 到底干了什么。简单说,它在 Jenkins 和目标服务器之间建立一条 SSH 通道,Jenkins 把构建好的文件通过 SFTP 传过去,然后通过 SSH 执行你指定的命令。整个过程对 Jenkins 来说就是"连过去、传文件、跑命令"三步。

配置入口在"系统管理 → 系统配置",拉到最下面找到 Publish over SSH 区域。这里有两块:上面是全局的 SSH 服务器列表,下面是全局的高级配置(比如密码短语)。

添加一台目标服务器需要填这些:

  • Name:给这台服务器起个名字,构建时引用;
  • Hostname:目标服务器 IP;
  • Username:登录用户,比如 root 或者专门的部署用户;
  • Remote Directory:远程基础目录,比如/data/deploy,后面传文件都相对于这个目录。

认证方式有两种:密码和密钥。生产环境强烈建议用密钥,密码方式在 Jenkins 里是明文存储的,不安全。密钥方式的配置稍微绕一点:你需要把 Jenkins 所在机器的公钥加到目标服务器的~/.ssh/authorized_keys里,然后在 Jenkins 里填私钥内容。

具体操作:在 Jenkins 服务器上生成密钥对(如果还没有的话):

ssh-keygen -t rsa -b 4096

然后把公钥~/.ssh/id_rsa.pub的内容追加到目标服务器的~/.ssh/authorized_keys。接着在 Jenkins 的 Publish over SSH 配置里,把私钥~/.ssh/id_rsa的完整内容贴到 Key 字段里。

配好后一定要点"Test Configuration"测试,看到 Success 才算通。这一步失败最常见的原因是目标服务器的 SSH 端口不是默认的 22,或者防火墙没放行。如果端口不是 22,要在 Hostname 里写成IP:端口的形式。

3.2 目标服务器上的目录规划与部署脚本设计

远程部署能不能稳定,一半取决于目标服务器上的目录和脚本设计得好不好。我见过太多人把所有东西堆在一个目录里,结果版本一多就乱套。

我的目录规划是这样的:

/data/deploy/ ├── app/ │ ├── current/ # 当前运行的 jar │ ├── backup/ # 历史版本备份 │ └── logs/ # 应用日志 ├── scripts/ │ ├── start.sh # 启动脚本 │ ├── stop.sh # 停止脚本 │ └── restart.sh # 重启脚本

为什么这么分?current放当前运行的包,backup留几个历史版本方便回滚,logs单独放日志方便排查,scripts放脚本和 jar 分离,升级时不会误删脚本。

启动脚本start.sh的核心逻辑:

#!/bin/bash APP_NAME="your-app.jar" APP_DIR="/data/deploy/app/current" LOG_DIR="/data/deploy/app/logs" cd $APP_DIR nohup java -jar -Xms512m -Xmx1024m $APP_NAME \ --spring.profiles.active=prod \ > $LOG_DIR/app.log 2>&1 & echo $! > $APP_DIR/app.pid

这里有几个细节值得说。-Xms和-Xmx根据服务器内存设,一般设成物理内存的一半左右,别设太大导致 OOM Killer 把进程干掉。--spring.profiles.active=prod指定生产环境配置,这个参数很关键,很多人忘了加,结果用开发配置跑生产。echo $! > app.pid把进程号存下来,停止脚本才能精准 kill。

停止脚本stop.sh:

#!/bin/bash APP_DIR="/data/deploy/app/current" PID_FILE="$APP_DIR/app.pid" if [ -f $PID_FILE ]; then PID=$(cat $PID_FILE) kill $PID sleep 5 # 检查是否还在,还在就强杀 if kill -0 $PID 2>/dev/null; then kill -9 $PID fi rm -f $PID_FILE fi

为什么先kill再等 5 秒再kill -9?因为 Spring Boot 应用收到正常终止信号会执行优雅关闭,释放数据库连接、处理完正在进行的请求。直接kill -9会粗暴中断,可能导致数据不一致。给它几秒钟优雅退出的机会,实在不退再强杀。

3.3 部署流程的整体串联思路

把上面的东西串起来,一次完整的远程部署流程是这样的:

  1. Jenkins 从 Git 拉取最新代码;
  2. 用 Maven 执行clean package打包;
  3. 通过 Publish Over SSH 把打好的 jar 传到目标服务器的current目录(传之前先把旧的备份到backup);
  4. 通过 SSH 执行restart.sh,先停旧服务再起新服务;
  5. 检查启动是否成功,失败则告警。

这个流程里,第 3 步的"先备份再覆盖"是很多人忽略的。如果不备份,新包传上去覆盖了旧包,结果新包有问题想回滚,旧包已经没了。备份逻辑可以放在传输前的远程命令里,也可以放在传输后的脚本里,我习惯放在传输前,用时间戳命名备份文件。

4. Jenkins 任务的完整配置与实操过程

4.1 创建一个自由风格任务的完整步骤

Jenkins 的任务类型有好几种,远程部署 Spring Boot 用自由风格(Freestyle)就够了,配置直观,不需要写 Jenkinsfile。如果你团队已经在用流水线,那用 Pipeline 也行,但自由风格对新手更友好。

新建任务,选"构建一个自由风格的软件项目",然后逐项配置。

源码管理:选 Git,填仓库地址,选凭据。如果是私有仓库,凭据要提前在"凭据管理"里加好。分支填你要部署的分支,比如main或release。

构建触发器:可以配轮询(比如H/5 * * * *每 5 分钟检查一次),也可以配 Webhook 让代码提交时触发。轮询简单但浪费资源,Webhook 实时但需要代码平台支持。小团队用轮询就够了。

构建环境:一般不用特别配,如果构建时需要特定环境变量可以在这里加。

构建步骤:这是核心。先加一个"调用顶层 Maven 目标",Goals 填clean package -DskipTests。-DskipTests跳过测试,加快构建速度,但生产环境建议至少跑单元测试,可以改成clean package,或者用-Dmaven.test.skip=true跳过编译测试代码。

提示:-DskipTests和-Dmaven.test.skip=true有区别。前者编译测试代码但不运行,后者连编译都跳过。如果测试代码本身有编译错误,用前者会失败,用后者才能过。按需选择。

构建后操作:加"Send build artifacts over SSH"。选好目标服务器,配置 Source files(要传的文件,比如target/*.jar)、Remove prefix(去掉路径前缀,比如target/)、Remote directory(远程目录)、Exec command(传输后执行的命令)。

Exec command 里就写调用重启脚本:

cd /data/deploy/scripts ./restart.sh

这里有个坑:Exec command 执行时的环境变量和交互式登录不一样,java命令可能找不到。解决办法是在脚本里用绝对路径,或者在命令前先source /etc/profile。我一般直接在脚本里写java的绝对路径,最稳。

4.2 构建参数与 jar 包传输的细节处理

传输 jar 包时,Source files 的写法有讲究。target/*.jar会匹配 target 下所有 jar,如果你的项目有多个 jar(比如多模块),可能传错。更精确的写法是target/your-app-*.jar,用项目名限定。

Remove prefix 填target/,这样传过去的就是纯 jar 文件名,不带 target 目录。Remote directory 填相对于全局配置里那个基础目录的路径,比如app/current。

关于传输覆盖的问题:Publish Over SSH 传文件时如果同名会直接覆盖。所以前面说的备份逻辑,要么在传输前用远程命令备份,要么在传输后、重启前备份。我推荐在传输前备份,因为传输后旧文件已经被覆盖了。

备份命令可以这样写(放在传输前的远程命令里,如果插件支持的话),或者更简单——在 restart.sh 里做备份:脚本启动时先把 current 里的 jar 复制到 backup 并加时间戳,然后再启动新的。但这样有个问题:新 jar 已经覆盖了 current,备份的就是新的了。所以正确顺序是:传输新 jar 到一个临时目录 → 脚本里备份 current → 把临时目录的新 jar 移到 current → 重启。

这个顺序有点绕,但想清楚就明白了:永远保证 current 里是"当前正在运行的版本",新版本先放别处,确认要切换了再移进来。这样任何时刻回滚都有据可依。

4.3 一次完整的部署实操记录

我把一次真实部署的过程完整走一遍,你可以对照着做。

假设项目叫demo-service,Git 仓库在内部平台,目标服务器是192.168.1.100。

第一步,在目标服务器建目录:

mkdir -p /data/deploy/app/current mkdir -p /data/deploy/app/backup mkdir -p /data/deploy/app/logs mkdir -p /data/deploy/scripts

第二步,把 start.sh、stop.sh、restart.sh 放到 scripts 目录,加执行权限:

chmod +x /data/deploy/scripts/*.sh

第三步,在 Jenkins 系统配置里加目标服务器,测试连接通过。

第四步,新建自由风格任务,配 Git、配 Maven 构建clean package -DskipTests。

第五步,配构建后操作,Source files 填target/demo-service-*.jar,Remove prefix 填target/,Remote directory 填app/current,Exec command 填:

cd /data/deploy/scripts && ./restart.sh

第六步,保存,点"立即构建"。看控制台输出,Maven 构建成功后会看到 SSH 传输日志,最后是执行命令的输出。

构建完成后去目标服务器验证:

ps -ef | grep demo-service tail -f /data/deploy/app/logs/app.log

看到进程在跑、日志正常输出,就成功了。

这里有个实测经验:第一次部署时,restart.sh 里的sleep 5可能不够,因为 Spring Boot 启动慢的话,旧进程还没完全退出新进程就起来了,端口冲突。解决办法是把 sleep 时间调长,或者在启动脚本里加一个端口检测循环,等端口释放了再启动。我后来改成检测端口的方式,比固定 sleep 靠谱。

5. 常见问题排查与避坑经验实录

5.1 构建阶段的高频问题

构建阶段最容易出问题的是依赖拉取和 JDK 版本。

依赖拉取失败,报错通常是Could not resolve dependencies。原因无非几个:网络不通、私服地址配错、settings.xml 没配。排查顺序是先ping私服地址,再检查 Jenkins 里 Maven 用的 settings.xml 是不是你配的那个。Jenkins 自动安装的 Maven 默认用的是它自带的 settings.xml,不是你系统里的那个,这点特别容易踩。

JDK 版本不匹配,报错像invalid target release: 1.8或者Unsupported class file major version。这说明编译用的 JDK 和项目要求的对不上。去全局工具配置里确认 JDK 版本,然后在任务里指定用哪个 JDK。如果任务里没得选,说明全局工具配置里没配好。

内存不足导致构建被杀,日志里会出现Killed或者OutOfMemoryError。这是 Jenkins 或 Maven 内存不够。可以在 Maven 的MAVEN_OPTS里加-Xmx1024m,或者给服务器加内存。

5.2 传输与远程执行阶段的典型故障

传输阶段的问题,十有八九是 SSH 连接和权限。

连接超时,Test Configuration 就失败。检查目标服务器 IP、端口、防火墙。CentOS 7 的 firewalld 默认可能没放行 SSH 端口(虽然 22 通常默认开),确认一下firewall-cmd --list-all。

认证失败,报Auth fail。密钥方式的话,检查公钥有没有正确加到目标服务器的 authorized_keys,权限对不对(.ssh目录 700,authorized_keys600)。权限不对 SSH 会拒绝使用密钥,这个坑很隐蔽。

传输成功但命令执行失败,报command not found。这就是前面说的环境变量问题,脚本里用绝对路径解决。

文件传过去了但权限不对,jar 包没有执行权限或者属主不对。可以在 Exec command 里加chmod和chown。

5.3 服务启动与运行阶段的排查

服务启动阶段的问题最影响业务,得重点盯。

端口被占用,报Address already in use。说明旧进程没停干净。检查ps -ef | grep java,手动 kill 掉残留进程,然后优化 stop.sh 的等待逻辑。

启动后立刻退出,日志里可能有配置错误。常见的是数据库连不上、配置文件路径不对、profile 没指定。看app.log里的异常堆栈,一般能直接定位。

启动成功但访问不了,检查防火墙有没有放行应用端口,以及应用是不是绑定了127.0.0.1而不是0.0.0.0。Spring Boot 默认绑所有网卡,但如果配置里写了server.address=127.0.0.1,外部就访问不了。

下面这张表是我整理的高频问题速查,遇到问题先对照着看:

阶段现象最可能原因处理方向
构建依赖拉取失败私服地址或 settings.xml 问题检查 Maven 配置
构建JDK 版本报错编译 JDK 与项目不匹配全局工具配置指定 JDK
传输连接超时防火墙或端口不对检查 firewalld 和端口
传输认证失败密钥权限或配置错误检查 .ssh 权限
执行command not found环境变量缺失脚本用绝对路径
启动端口占用旧进程未退出优化停止脚本
启动启动即退出配置或依赖问题看应用日志堆栈
运行外部访问不了防火墙或绑定地址检查端口和 server.address

5.4 几条踩坑换来的实操心得

第一条,永远保留可回滚的备份。我吃过一次亏,新版本上线有 bug,想回滚发现旧包被覆盖了,只能重新构建,耽误了半小时。从那以后 backup 目录至少留 5 个版本,回滚就是复制粘贴的事。

第二条,部署脚本要幂等。意思是同一个脚本重复执行结果一致,不会因为执行两次就出问题。比如启动前先检查有没有同名进程,有就先停掉。这样即使部署中断重来,也不会起一堆重复进程。

第三条,构建和部署分离。构建失败就不该触发部署,部署失败要能单独重试而不用重新构建。自由风格任务里这两步是串的,如果部署失败想重试,得整个重跑。如果构建很慢,这就很痛苦。可以考虑把构建产物归档,部署任务单独从归档里取包。

第四条,日志一定要能追溯。Jenkins 的构建日志、应用的运行日志、部署脚本的执行日志,三份日志对得上,出问题才能快速定位。我习惯在部署脚本里把每次执行的时间、操作、结果都 echo 出来,重定向到一个部署日志文件里。

第五条,别在周五下午部署。这是血泪教训,不是技术问题,是人的问题。出了问题大家都在过周末,响应慢。要部署就选大家都有空盯着的时候。

6. 关于这套方案的一些延伸想法

这套 CentOS 7 + Jenkins + SSH 的方案,胜在简单直接,但它也有明显的边界。当你的服务数量多起来、部署频率高起来之后,SSH 逐个推包的方式会变得笨重,这时候就该考虑容器化和更现代的编排方式了。但那是另一个话题,对于大多数还在用传统方式部署 Spring Boot 的团队来说,把眼下这套跑稳、跑顺,收益已经足够大。

我个人在实际操作中的体会是,自动化部署这件事,第一步不是追求多先进,而是先让流程跑通、让团队用起来。哪怕一开始只是自动构建加自动传包,也比纯手工强太多。等大家习惯了这种节奏,再逐步优化,比如加自动化测试、加健康检查、加回滚机制。技术方案是长出来的,不是一步到位的。

最后再分享一个小技巧:如果你想让部署更稳,可以在 restart.sh 里加一个启动后的健康检查,用curl请求应用的健康端点,连续几次成功才算部署成功,否则自动回滚到上一个版本。这个逻辑不复杂,但能挡掉很多"部署看起来成功、实际服务没起来"的尴尬情况。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 14:55:10

纯方位无源定位:从数学建模到最小二乘与GDOP优化实战

简介:本资源为2022年全国大学生数学建模竞赛本科组B题的完整论文PDF,聚焦无人机纯方位无源定位问题,适合备战数模竞赛的本科生及指导教师参考。论文围绕圆形与锥形两种编队,系统建立被动接收信号无人机的定位模型与位置调整模型&a…

作者头像 李华
网站建设 2026/10/15 14:53:31

嵌入式SHA256轻量实现:裸机/RTOS下的固件完整性校验方案

简介:这是一份面向C初学者与密码学实践者的SHA256哈希算法轻量级实现资源,聚焦数据完整性校验、密码安全处理等典型应用场景。资源以标准C11语法编写,不依赖外部构建系统,便于快速集成与教学演示。压缩包共3个文件(3KB…

作者头像 李华
网站建设 2026/10/15 14:53:26

最近邻算法(KNN)从入门到精通:距离度量、K值选择与实战优化

1. 最近邻算法到底在解决什么问题第一次接触最近邻算法的人,脑子里冒出来的问题往往很朴素:机器怎么判断一个东西属于哪一类?它又没有见过这个东西。这个问题如果往深了想,其实触及了机器学习最核心的命题——如何让机器从已有经验…

作者头像 李华
网站建设 2026/10/15 14:53:24

JDK 1.7.0_03 部署指南:老旧系统、国产化迁移与工控环境实战

简介:本资源为Java 7u3(JDK 1.7.0_03)完整开发套件,面向Java初学者、教学环境搭建者及需离线/免安装部署的开发者,解决旧项目兼容性调试、教学演示环境快速初始化等实际需求。压缩包为RAR格式,大小70.23MB&…

作者头像 李华
网站建设 2026/10/15 14:53:02

STM32F103驱动BH1750光照传感器:I2C通信、量程选择与低功耗实战

简介:这份资源面向嵌入式开发初学者与需要快速搭建光照采集方案的工程师,围绕STM32F103微控制器驱动BH1750光照强度传感器(GY-30模块)展开,解决环境光测量与TFTLCD实时显示的实际问题。压缩包共140个文件,约…

作者头像 李华