一、前提条件
- 环境准备 :
- Jenkins 已安装 Publish Over SSH 插件(进入 Manage Jenkins → Plugins → 搜索并安装
Publish Over SSH)。 - 目标部署服务器(需部署项目的服务器)已开启 SSH 服务(默认端口 22,若为其他端口需单独配置)。
- Jenkins 服务器(或容器)与目标服务器网络互通(可通过
ping或telnet <目标IP> 22验证)。
- Jenkins 已安装 Publish Over SSH 插件(进入 Manage Jenkins → Plugins → 搜索并安装
二、配置 Jenkins 与目标服务器的 SSH 免密登录
1. 在 Jenkins 容器内生成 SSH 密钥对
由于 Jenkins 通常通过 Docker 容器运行,需进入容器内生成密钥(确保密钥由 Jenkins 服务用户持有):
bash
# 进入 Jenkins 容器(替换容器名为实际容器名,如 "jenkins")
docker exec -it -u root jenkins /bin/bash
# 生成 RSA 密钥对(一路回车,可选设置密钥密码 Passphrase,生产环境建议设置)
ssh-keygen -t rsa -b 2048 -C "jenkins-ssh-key"
# 生成后密钥路径:私钥 /root/.ssh/id_rsa,公钥 /root/.ssh/id_rsa.pub
2. 将公钥复制到目标部署服务器
方式 1:使用 ssh-copy-id 自动复制(推荐,需目标服务器支持)
bash
# 首次连接目标服务器,需手动确认指纹(输入 "yes")并输入目标服务器密码
ssh <目标服务器用户名>@<目标服务器IP> # 例如:ssh appuser@172.20.113.93
# 复制公钥到目标服务器(替换用户名和IP)
ssh-copy-id -i ~/.ssh/id_rsa.pub <目标服务器用户名>@<目标服务器IP>
方式 2:手动复制公钥(若 ssh-copy-id 不可用)
-
查看 Jenkins 容器内的公钥内容:
bashcat /root/.ssh/id_rsa.pub复制输出的公钥文本(以
ssh-rsa开头)。 -
登录目标服务器,粘贴公钥到
authorized_keys:bash# 登录目标服务器 ssh <目标服务器用户名>@<目标服务器IP> # 创建 .ssh 目录(若不存在) mkdir -p ~/.ssh && chmod 700 ~/.ssh # 编辑 authorized_keys 文件,粘贴 Jenkins 公钥 vi ~/.ssh/authorized_keys # 粘贴公钥后保存退出 # 设置权限(必须,否则 SSH 会拒绝免密登录) chmod 600 ~/.ssh/authorized_keys
3. 验证 SSH 免密连接
在 Jenkins 容器内执行以下命令,确认无需密码即可登录目标服务器:
bash
ssh <目标服务器用户名>@<目标服务器IP> # 若直接进入目标服务器终端,说明免密配置成功
三、Jenkins 全局配置 Publish over SSH
1. 进入配置页面
Jenkins 首页 → Manage Jenkins → System (系统配置)→ 找到 Publish over SSH 模块。
2. 配置 SSH 服务器(目标部署服务器)
(1)配置 SSH 密钥
| 字段 | 填写说明 |
|---|---|
| Passphrase | 若生成密钥时设置了"密钥密码",则填写该密码;未设置则留空。 |
| Path to key | 私钥在 Jenkins 容器内的绝对路径(如 /root/.ssh/id_rsa)。 |
| Key | 直接粘贴私钥文件(id_rsa)的完整文本内容(若填写此项,Path to key 会被忽略)。 |
推荐配置:直接粘贴私钥内容(避免容器路径变更导致失效):
- 在 Jenkins 容器内执行
cat /root/.ssh/id_rsa,复制完整私钥文本(从-----BEGIN RSA PRIVATE KEY-----到-----END RSA PRIVATE KEY-----)。 - 粘贴到 Key 字段中。
(2)添加目标服务器信息
点击 SSH Servers → Add,配置目标部署服务器参数:
| 字段 | 填写说明 |
|---|---|
| Name | 自定义服务器名称(如"deploy-server-172.20.113.93",后续构建时选择)。 |
| Hostname | 目标服务器 IP 或域名(如 172.20.113.93)。 |
| Username | 目标服务器登录用户名(如 appuser)。 |
| Remote Directory | (可选)默认远程目录(若构建后步骤中未指定,会使用此目录,一般留空)。 |
| Port | SSH 端口(默认 22,若目标服务器 SSH 端口非 22,需填写实际端口,如 2222)。 |
高级选项(按需配置):
- 勾选 Use password authentication, or use a different key:若不使用全局密钥,可单独指定该服务器的密钥。
- Timeout (ms):设置 SSH 连接超时时间(默认 30000 ms,网络差时可延长)。
3. 测试 SSH 连接
点击 Test Configuration ,若显示 Success,说明目标服务器配置正确;否则检查密钥、用户名、IP 或端口是否错误。
4. 保存配置
点击页面底部 Save,完成 Publish over SSH 全局配置。
四、在项目中添加构建后部署步骤(以 Spring Boot 项目为例)
以之前的 Spring Boot 项目任务(如 project-springboot-admin)为例,添加"通过 SSH 发送构建产物并部署"的步骤。
1. 进入项目配置页面
项目任务页面 → Configure (配置)→ Post-build Actions (构建后操作)→ 点击 Add post-build action → 选择 Send build artifacts over SSH。
2. 配置 SSH 部署参数
(1)选择目标服务器
- SSH Server:从下拉列表选择在全局配置中添加的目标服务器(如"deploy-server-172.20.113.93")。
(2)配置文件传输(Transfer Set)
| 字段 | 填写说明 |
|---|---|
| Source files | 需传输到目标服务器的文件路径(相对于 Jenkins 项目工作区的相对路径)。 示例:dsrm-server/target/dsrm-server.jar(Spring Boot 项目打包后的 JAR 包路径)。 |
| Remove prefix | (可选)移除源文件路径的前缀(如填写 dsrm-server/target,则传输到目标服务器的文件名为 dsrm-server.jar,而非带路径的文件)。 |
| Remote directory | 目标服务器接收文件的目录(绝对路径)。 示例:/home/etc/xxxxxx/target(根据实际部署路径填写)。 |
| Exec command | 文件传输完成后在目标服务器执行的部署命令(如备份旧版本、启动新服务)。 |
(3)填写部署命令(Exec command)
用户提供的示例命令(需根据实际路径调整):
bash
# 1. 备份旧版本 JAR 包(以当前时间戳命名,保留历史版本)
mv /data/app/xxxxx.jar /data/app/xxxxx.jar-$(date +%Y%m%d%H%M%S)
# 2. 将传输到目标服务器的新 JAR 包移动到部署目录
mv /home/etc/xxxxxx/target/dsrm-server.jar /data/app/xxxx.jar
# 3. 执行重启脚本(后台运行,避免 Jenkins 构建进程阻塞)
sh -c "cd /data/app && ./restart.sh & disown"
3. 保存项目配置
点击 Save,完成构建后部署步骤配置。
五、部署脚本(restart.sh)说明
目标服务器 /data/app/restart.sh 脚本用于停止旧服务、启动新服务,内容如下(需提前创建并赋予执行权限):
bash
#!/bin/bash
# 停止旧服务
rm -f nohup.out # 清理旧日志
app="xxxx.jar" # 替换为实际 JAR 包名称(如 dsrm-server.jar)
pid=$(ps -ef | grep "$app" | grep -v grep | awk '{print $2}') # 获取进程 ID
if [ -n "$pid" ]; then
echo "Stopping old process: $pid"
kill -9 "$pid" # 强制停止进程
fi
# 启动新服务(使用指定 JDK 路径,后台运行并输出日志)
jdk11_path="/data/software/jdk-17/bin/java" # 替换为目标服务器 JDK 实际路径
nohup setsid "$jdk11_path" -jar "$app" > nohup.out 2>&1 < /dev/null &
echo "New process started successfully"
脚本权限设置(目标服务器执行):
bash
chmod +x /data/app/restart.sh # 赋予执行权限
六、验证自动部署
1. 手动触发构建
进入项目任务页面 → Build Now,等待构建完成。
2. 查看部署日志
- Jenkins 构建日志 :构建后点击 Console Output ,搜索
SSH: Transferred 1 file(s)(文件传输成功)和Exec command complete(部署命令执行完成)。 - 目标服务器日志 :登录目标服务器,查看
/data/app/nohup.out,确认新服务启动日志(如Started XxxApplication in x.x seconds)。
七、常见问题排查
1. SSH 连接失败(日志显示 Connection refused 或 Authentication failed)
- 原因:目标服务器 SSH 端口错误、公钥未正确复制、Jenkins 私钥配置错误。
- 解决 :
- 确认目标服务器 SSH 端口(默认 22),全局配置中
Port字段是否正确。 - 重新执行公钥复制步骤,确保目标服务器
~/.ssh/authorized_keys包含 Jenkins 公钥,且权限为700(.ssh 目录)和600(authorized_keys)。 - 检查 Jenkins 全局配置的私钥是否完整(无多余空格或换行)。
- 确认目标服务器 SSH 端口(默认 22),全局配置中
2. 文件传输失败(日志显示 No such file)
- 原因 :
Source files路径错误(相对于 Jenkins 项目工作区)。 - 解决 :在 Jenkins 项目工作区(如
/var/jenkins_home/workspace/project-springboot-admin)确认 JAR 包实际路径,调整Source files(如dsrm-server/target/dsrm-server.jar)。
3. 部署命令执行失败(日志显示 mv: cannot stat ...)
- 原因 :
Remote directory路径错误或目标服务器目录权限不足。 - 解决 :
- 登录目标服务器,确认
Remote directory(如/home/etc/xxxxxx/target)是否存在,若不存在则创建:mkdir -p /home/etc/xxxxxx/target。 - 确保 Jenkins 登录用户(如
appuser)对目标目录有读写权限:chown -R appuser:appuser /home/etc/xxxxxx/target。
- 登录目标服务器,确认
4. 服务启动失败(nohup.out 日志报错)
- 原因:JDK 路径错误、JAR 包损坏或配置文件缺失。
- 解决 :
- 检查
restart.sh中的jdk11_path是否指向目标服务器实际 JDK 路径(执行which java确认)。 - 手动在目标服务器执行
java -jar /data/app/xxxx.jar,查看启动错误(如依赖缺失、端口占用)。
- 检查
八、总结
通过以上步骤,Jenkins 可在构建完成后自动将 Spring Boot 项目的 JAR 包通过 SSH 传输到目标服务器,并执行部署脚本完成服务重启。该流程可扩展到多服务器部署(添加多个 SSH Server)或其他项目(如 Vue 项目传输前端包到 Nginx 目录),实现全自动化部署。
