个人使用的自动化部署方案
起因
博主最近刷抖音,老是能刷到 vivo 浏览器的阅片能力非常的无敌,于是好奇心驱使下,我抛弃了夸克,尝试了一些关键词,这一搜不要紧,简直令我大开眼界,我直接一个坚持访问。
后续就是,手机中了病毒,莫名其妙地发热,病毒还会跟我抢屏幕的控制权,一度让我以为是手机进了水。当我发现是病毒后,对重要资料进行了备份然后恢复了出厂设置。
然鹅千算万算漏算了 github 的令牌忘记弄出来了。我使用了四年的 github 账号就这样无了,恢复步骤也是麻烦的要死,还好代码啥的电脑上还有。一气之下我要来个狠的,我不要用 github 了,反正自己有服务器,我自己搭自己玩,我还要弄自动化部署,于是就有了这篇文章
开始
远程仓库
在 1Panel 面板里逛了半天,发现 gitea 非常适合我这种小服务器搭建代码仓库的需求。于是直接一个下载安装
安装完后新建一个仓库,我们要实现自动化部署,就需要 web钩子,gitea 也贴心的给出了他们的 web 钩子的配置,此图在仓库的设置里,新建一个 web 钩子既能看到

配置好钩子后,我们在自己的代码中添加这个 webhook 的处理,以 java 为例,这个钩子没有设置授权标头,大家可以自行设置,
那这里的 triggerJenkinsJob 方法是做什么的呢,这就要说到我们的自动化部署的第二个工具了:Jenkins
@PostMapping("/hook")
public void webhook(@RequestBody Object o){
try {
triggerJenkinsJob();
} catch (Exception e) {
e.printStackTrace();
}
}Jenkins
在 1Panel 中找到 Jenkins 并下载安装
使用前需要下载一些必须的插件,系统管理->插件管理 即可下载
这里需要的插件如下

在主页新建任务,新建软件项目

需要我们配置的地方有
源码管理

triggers

maven

ssh

这里我们需要在宿主机的 /var/code/bot/file 处新增一段 sh 脚本,我的脚本具体内容如下,不想看的我大致介绍一下,我们的 jenkins 运行在 docker 容器中,我们前面的源码配置和 maven 配置都是在这个 docker 容器中的配置,gitea webhook 被触发后,我们在webhook 处理中加入的触发 trigger 的代码(这段触发代码写在文章的后面)开始运行,jenkins trigger 被触发,从我们配置的源码仓库 git clone 最新的代码(因为先更新完才会有 webhook 发送),通过 maven 对源码进行打包,但是这个打包好的 jar 包是在这个 jenkins 的容器中的,我们为了避免在同一个容器中使用不同的功能,需要通过这个 sh 脚本给 jar 包复制到我们的宿主机上来,由于我是宿主机直接运行 jar 包,大家可以参考,如果需要一个独立的容器来专门跑 项目,只需要把对应 jar 包上传到对应的 docker 容器中即可。剩下的一些处理无非就是端口的使用和进程的杀死
#!/bin/bash
# 设置端口号
PORT=8089
JAR_PATH="/var/code/bot/target/demoBot-0.0.1-SNAPSHOT.jar"
SOURCE_JAR="1Panel-jenkins-2W2J:/var/jenkins_home/workspace/qq-bot/target/new-qq-bot-0.0.1-SNAPSHOT.jar"
echo "正在查找并kill占用端口 $PORT 的进程..."
# 查找并kill占用指定端口的进程
PID=$(lsof -ti:$PORT)
if [ -z "$PID" ]; then
echo "没有找到占用端口 $PORT 的进程"
else
echo "找到进程PID: $PID"
echo "正在kill进程..."
kill -9 $PID
# 检查是否成功
sleep 1
CHECK_PID=$(lsof -ti:$PORT)
if [ -z "$CHECK_PID" ]; then
echo "✅ 成功释放端口 $PORT"
else
echo "❌ 释放端口失败,仍有进程占用"
exit 1
fi
fi
# 从Docker容器复制JAR文件
echo "正在从Docker容器复制JAR文件..."
echo "复制命令: docker cp $SOURCE_JAR $JAR_PATH"
docker cp $SOURCE_JAR $JAR_PATH
# 检查复制是否成功
if [ $? -eq 0 ]; then
echo "✅ JAR文件复制成功"
else
echo "❌ JAR文件复制失败"
exit 1
fi
# 检查JAR文件是否存在
if [ ! -f "$JAR_PATH" ]; then
echo "❌ JAR文件不存在: $JAR_PATH"
exit 1
fi
echo "JAR文件大小: $(ls -lh $JAR_PATH | awk '{print $5}')"
# 启动Java应用
echo "正在启动Java应用..."
nohup /usr/lib/openlogic-openjdk-jre-21.0.5+11-linux-x64/bin/java -jar /var/code/bot/target/demoBot-0.0.1-SNAPSHOT.jar > /var/code/bot/app.log 2>&1 &
# 获取启动的进程PID
APP_PID=$!
# 等待一段时间检查应用是否启动成功
sleep 3
# 检查应用是否在运行
if ps -p $APP_PID > /dev/null; then
echo "✅ Java应用启动成功,PID: $APP_PID"
echo "✅ 应用日志输出到: /var/code/bot/app.log"
else
echo "❌ Java应用启动失败,请检查日志: /var/code/bot/app.log"
exit 1
fi
# 检查应用是否在指定端口上监听
echo "检查应用是否在端口 $PORT 上监听..."
sleep 2
if lsof -ti:$PORT > /dev/null; then
echo "✅ 应用已在端口 $PORT 上成功启动"
else
echo "⚠️ 应用进程存在但未在端口 $PORT 上监听,请检查应用状态"
fi最后我们在 webhook 处理中补充完 triggerJenkinsJob 方法就完成了我们的自动化部署,由于 jenkins 设置了跨域的配置,这里触发 trigger 的方法还是有点长的,不过逻辑很简单,介绍一下这里涉及的常量
JENKINS_URL = "jenkins 的地址";
JENKINS_USERNAME = "你的 jenkins 用户名";
JENKINS_API_TOKEN = "这个需要在 jenkins security 中添加一个";
JOB_NAME = "运行的这个任务的名称";
JOB_TOKEN = "我们配置的身份验证令牌,在配置的 triggers 里";
public static void triggerJenkinsJob() throws Exception {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
// 1. 获取 CSRF crumb
String crumb = getCrumb(client);
System.out.println("获取到的 Crumb: " + crumb);
// 2. 触发 Jenkins 任务
triggerJob(client, crumb);
}
private static String getCrumb(HttpClient client) throws Exception {
// 对查询参数进行编码
String xpath = URLEncoder.encode("concat(//crumbRequestField,\":\",//crumb)", StandardCharsets.UTF_8);
String crumbUrl = JENKINS_URL + "crumbIssuer/api/xml?xpath=" + xpath;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(crumbUrl))
.header("Authorization", getBasicAuthHeader())
.timeout(Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return response.body().split(":")[1];
}
throw new RuntimeException("获取 Crumb 失败,状态码: " + response.statusCode() + ", 响应: " + response.body());
}
private static void triggerJob(HttpClient client, String crumb) throws Exception {
String buildUrl = JENKINS_URL + "job/" + JOB_NAME + "/build?token=" + JOB_TOKEN;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(buildUrl))
.header("Authorization", getBasicAuthHeader())
.header("Jenkins-Crumb", crumb)
.POST(HttpRequest.BodyPublishers.noBody())
.timeout(Duration.ofSeconds(30))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 201) {
System.out.println("Jenkins 任务触发成功!");
} else {
System.out.println("Jenkins 任务触发失败,状态码: " + response.statusCode());
}
}
private static String getBasicAuthHeader() {
String auth = JENKINS_USERNAME + ":" + JENKINS_API_TOKEN;
return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes());
}git push 一下,jenkins 顺利完成了自动构建
