网站Logo 时光杂货铺

从 0 搭建 Jenkins + Gitee + Spring Boot 自动部署 CI/CD 实战笔记

admin
35
2026-10-03

本文记录一次完整的 Jenkins CI/CD 搭建过程,从 Jenkins Docker 安装、插件安装、JDK/Maven/Docker 配置,到 Gitee WebHook、Spring Boot 打包、SSH 部署、systemd 管理,最终实现:

本地 git push → Gitee → Jenkins 自动构建 → 自动部署 Spring Boot → 浏览器访问最新版本。

最终实现效果

最终实现下面这条完整链路:

本地开发电脑
    │
    │ git push
    ▼
Gitee
    │
    │ WebHook
    ▼
Jenkins
    │
    ├── 拉取代码
    ├── Maven clean package
    ├── 生成 Spring Boot JAR
    ├── SCP 上传 JAR
    └── SSH 执行 systemctl restart
             │
             ▼
      Ubuntu 宿主机
             │
             ▼
       Spring Boot
          :8081
             │
             ▼
        浏览器访问

最终以后开发只需要:

git add .
git commit -m "xxx"
git push

Jenkins 就会自动完成部署。

环境说明

本次环境:

操作系统:Ubuntu 26.04 LTS

Jenkins:
Docker 部署
镜像:
jenkins/jenkins:lts-jdk21

JDK:
21

Maven:
3.9.10

Spring Boot:测试开放端口
8081

Jenkins:
8080

服务器 IP:

43.142.88.165

所以:

Jenkins:
http://43.142.88.165:8080

Spring Boot:
http://43.142.88.165:8081

本次采用docker部署,docker和docker compose安装方式本次不在概述。

一、安装&启动jenkins,获取初始密码

创建 Jenkins 工作目录:

mkdir -p /opt/jenkins
cd /opt/jenkins

创建 Jenkins 数据目录:
mkdir -p /opt/jenkins/jenkins_home

在jenkins目录创建 Jenkins Docker Compose 需在/opt目录上传apache-maven-3.9.10

cat > docker-compose.yml <<'EOF'
services:
  jenkins:
    image: jenkins/jenkins:lts-jdk21
    container_name: jenkins
    restart: unless-stopped
    user: root
    ports:
      - "8080:8080"
      - "50000:50000"
    volumes:
      - ./jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
      - /usr/bin/docker:/usr/bin/docker:ro
      - /opt/apache-maven-3.9.10:/opt/apache-maven-3.9.10:ro
    environment:
      JAVA_OPTS: >-
        -Dhudson.model.DownloadService.noSignatureCheck=true
EOF

运行docker compose up -d 启动Jenkins容器 会一次性拉取jdk21和jenkins两个容器

docker ps

应该看到:

查看日志:docker logs -f jenkins

正常启动后Ctrl+C退出日志

执行命令 : docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

获取初始密码

浏览器打开:

http://43.142.88.165:8080

输入初始密码。

二、安装jenkins插件&插件安装建议

进入jenkins初始化页面

选择:Install suggested plugins

如果网络原因导致插件安装失败,可以先进入Jenkins

之后进入: Manage Jenkins -> Plugins -> Available plugins

本次测试项目最终需要的主要插件

Git
Git client
Pipeline
Pipeline Stage View
Credentials
Credentials Binding
Docker Pipeline
Docker Commons
Pipeline Maven Integration
JUnit
Config File Provider
SSH Agent
Gitee

其中 Gitee非常重要 它负责后面的: Gitee WebHook -> Jenkins 自动构建

为了避免一次安装太多导致排查困难,可以按照功能来安装。

第一组: Git、GitClient 。 作用: Jenkins -> 拉取 Gitee 代码

第二组: Pipeline、Pipeline Stage View。 作用: 让Jenkins支持pipeline{ .... } 以及之后在控制台看到stage1 stage2

第三组: Pipeline Maven Integration 。 作用: 用于Jenkins Pipeline调用Maven

第四组: Credentials、Credentials Binding、SSH Agent 。 作用: 后续SSH 部署会使用

第五组: Docker Pipeline、Docker Commons。 作用: 之后如果继续扩展Docker部署会用到

第六组: Gitee 作用: 用于Gitee WebHook

每一组安装完成之后点击下方复选框重启一次jenkins。

三、配置Jenkins JDK

进入 Manage Jenkins -> Tools

配置JDK

Name: jdk21

JAVA_HOME : /opt/java/openjdk

这里注意:因为jdk是在docker compose文件中一起被拉取的,因此宿主机并没有这个jdk,它在容器内

可以执行命令: docker exec jenkins printenv JAVA_HOME 来查看位置

四、配置Maven

继续在: Manage Jenkins -> Tools 配置Maven

Name: Maven-3.9.10

Maven Home: /opt/apache-maven-3.9.10

完成上述四步之后进入jenkins容器后验证 docker exec -it jenkins bash

检查Java: java -version

检查Maven: mvn -version

检查Git: git --version

检查Docker: docker --version

全部正常以后执行 exit 退出容器

五、创建SpringBoot项目

本次用来测试的项目名称是 jenkins-demo

SpringBoot端口号设置为:

server:

port: 8081

测试接口: Get /hello

例如 : http://43.142.88.165:8081/hello

到这里后,先不要让 Jenkins 管理 Spring Boot 进程

这次本次最重要的经验之一。

本次测试 最开始采用的架构如下图

flowchart LR subgraph 旧方案[❌ 旧方案:Jenkins直接启动SpringBoot] J1[Jenkins Pipeline] --> S[执行 start.sh 脚本] S --> N[nohup java -jar xxx.jar] N --> B1[SpringBoot进程<br/>归属Jenkins的Shell会话] note1["风险:Pipeline执行结束 → Shell会话退出 → 进程可能被系统回收"] end

这种方式看起来可以运行,但是 Jenkins Pipeline 结束后,后台进程可能被 Jenkins 清理。

所以最终改成:

flowchart LR subgraph 新方案[✅ 新方案:systemd托管SpringBoot] J2[Jenkins Pipeline] --> A[编译打包,上传Jar包] A --> C[执行 systemctl restart springboot.service] C --> Sys[systemd 系统服务管理器] Sys --> B2[SpringBoot进程<br/>独立后台托管,与Jenkins会话解耦] note2["优势:Jenkins任务结束不影响进程,崩溃自动重启、开机自启"] end

这是更加合理的架构。

之后创建SpringBoot生产目录

宿主机:

mkdir -p /opt/apps/jenkins-demo

最终:

/opt/apps/jenkins-demo/
├── jenkins-demo.jar
└── app.log

六、创建 systemd 服务

执行

cat > /etc/systemd/system/jenkins-demo.service <<'EOF'
[Unit]
Description=Jenkins Demo Spring Boot Application
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/apps/jenkins-demo

ExecStart=/usr/bin/java -jar /opt/apps/jenkins-demo/jenkins-demo.jar

Restart=always
RestartSec=5

StandardOutput=append:/opt/apps/jenkins-demo/app.log
StandardError=append:/opt/apps/jenkins-demo/app.log

[Install]
WantedBy=multi-user.target
EOF

执行完上面的命令后,再执行

systemctl daemon-reload

解释这个jenkins-demo.service含义:

cat > xxx <<'EOF' 含义:把 EOF 和 EOF 中间的文本,直接写入到 /etc/systemd/system/jenkins-demo.service 文件 'EOF' 加单引号:里面内容原样写入,不会解析变量。一次性生成服务配置文件,不用 vim 手动编辑。

二、分块解释 service 文件(systemd 的配置,分 3 大块:[Unit] / [Service] / [Install])

① [Unit] 区块:服务的基本描述、启动依赖

[Unit]
Description=Jenkins Demo Spring Boot Application
After=network.target
  • Description:服务描述,systemctl status jenkins-demo 时展示,方便识别这是哪个应用

  • After=network.target:等系统网络就绪之后,再启动这个 SpringBoot 程序

作用:防止网络还没初始化完成,SpringBoot 就启动,导致端口 / 网络访问异常 ⚠️ After 只是先后顺序,不是强依赖(网络挂了它照样尝试启动)

② [Service] 区块【核心!】定义程序怎么运行

[Service]
Type=simple
User=root
WorkingDirectory=/opt/apps/jenkins-demo
ExecStart=/usr/bin/java -jar /opt/apps/jenkins-demo/jenkins-demo.jar
Restart=always
RestartSec=5

StandardOutput=append:/opt/apps/jenkins-demo/app.log
StandardError=append:/opt/apps/jenkins-demo/app.log
  • Type=simple:最常用类型。ExecStart 执行的命令就是主进程,systemd 会监控这个 java 进程。

  • User=root:使用 root 用户运行这个程序(生产环境不推荐 root,测试演示没问题)

  • WorkingDirectory:程序工作目录,相当于cd /opt/apps/jenkins-demo之后再执行 jar,读取相对路径资源、日志输出基准目录。

  • ExecStart:启动命令,等价于 java -jar jenkins-demo.jar,systemd 通过这条命令拉起 SpringBoot。

✅ 重点:这个 java 进程归属 systemd,不再属于 Jenkins 的 shell 会话!也就是解决了之前的痛点:Jenkins 流水线跑完,SpringBoot 不会被杀。

  • Restart=always:只要进程退出,就自动重启 不管是代码抛异常崩溃、OOM、手动 kill,只要进程没了,systemd 自动拉起。

  • RestartSec=5:进程挂掉之后,等待 5 秒再重启,防止无限快速重启把服务器打崩。

  • StandardOutput / StandardError 把程序标准输出 + 错误输出,追加写入app.log。

    等价于旧方案里 nohup ... > app.log 2>&1,但是由 systemd 托管日志。append代表追加,不会覆盖旧日志。

③ [Install] 区块:定义开机自启规则

[Install]
WantedBy=multi-user.target
  • multi-user.target:Linux 多用户模式(正常服务器开机进入的模式)

  • 执行 systemctl enable jenkins-demo 的时候生效:服务器开机,自动启动这个 SpringBoot 服务

三、后面这条命令

systemctl daemon-reload

含义:重新加载 systemd 的配置 当你新增 / 修改 /etc/systemd/system/ 下面的 service 文件,必须执行这条! systemd 不会自动扫描文件变更,不执行这条,systemd 识别不到你新建的 jenkins-demo 服务。

六、启动SpringBoot项目

第一次部署 JAR:

cp /opt/jenkins/jenkins_home/workspace/jenkins-demo/target/*.jar \
   /opt/apps/jenkins-demo/jenkins-demo.jar

启动:

systemctl start jenkins-demo

设置开机启动:

systemctl enable jenkins-demo

查看:

systemctl status jenkins-demo

这里注意,在最初的docker compose文件中

ports:

- "8080:8080"

- "50000:50000"

除了这两个jenkins本身需要的,端口映射不要设置其他和应用实例冲突的端口,或者干脆就不要设置。

比如 -"8081:8081"

我个人想法是我测试的boot实例设置的是8081端口,但是如果写在ports里。那么docker就会占用这个端口,导致实例启动失败(本次踩坑之一)。

之后游览器访问

http://43.142.88.165:8081/hello

如果可以看到 Spring Boot 返回内容:

Hello Jenkins

说明 Spring Boot 部署成功。

六、解决 Jenkins Docker → 宿主机通信

因为 Jenkins 在 Docker 中:
Jenkins Container(容器)
而 systemd 在:
Ubuntu Host
所以需要让 Jenkins 可以 SSH 到宿主机。
首先获取 Jenkins Docker 网络网关:

docker inspect -f '{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}' jenkins

本次得到:
172.18.0.1
所以:
Jenkins Container
       ↓
172.18.0.1
       ↓
Ubuntu Host

确认宿主机SSH

执行 ssh -V V要大写

检查 22 端口: ss -lntp | grep ':22'

应该看到:

0.0.0.0:22
[::]:22

创建 Jenkins 专用 SSH 密钥

在 Ubuntu 宿主机执行:

ssh-keygen -t ed25519 \
  -f /root/.ssh/jenkins_deploy \
  -C "jenkins-deploy"

密码可以留空。
检查:
ls -l /root/.ssh/jenkins_deploy*
应该有:
/root/.ssh/jenkins_deploy
/root/.ssh/jenkins_deploy.pub


配置 SSH 公钥
执行:

cat /root/.ssh/jenkins_deploy.pub >> /root/.ssh/authorized_keys

设置权限:
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys



测试 SSH
宿主机执行:

ssh -i /root/.ssh/jenkins_deploy [email protected]
能够登录:
root@VM-0-6-ubuntu:~#
说明密钥正常。




把私钥复制到 Jenkins 容器
宿主机执行:
docker cp /root/.ssh/jenkins_deploy \
  jenkins:/tmp/jenkins_deploy
进入 Jenkins:
docker exec -it jenkins bash
执行:

mkdir -p /root/.ssh
mv /tmp/jenkins_deploy \
   /root/.ssh/jenkins_deploy

chmod 600 /root/.ssh/jenkins_deploy

测试:
ssh -i /root/.ssh/jenkins_deploy [email protected]
如果能够进入宿主机:
root@VM-0-6-ubuntu:~#
说明:
Jenkins Container
      ↓ SSH
Ubuntu Host
已经打通。

Docker 路径差异(踩坑之一)

宿主机:
/opt/jenkins/jenkins_home
Jenkins 容器:
/var/jenkins_home
因为 Docker Compose 有:
- ./jenkins_home:/var/jenkins_home
所以 Jenkins Pipeline 里面必须使用:
/var/jenkins_home
而不能使用:
/opt/jenkins/jenkins_home
例如:
ls -lh /var/jenkins_home/workspace/jenkins-demo/target/*.jar
可以找到 JAR。



测试SCP
在 Jenkins 容器里:

scp -i /root/.ssh/jenkins_deploy \
  /var/jenkins_home/workspace/jenkins-demo/target/*.jar \
  [email protected]:/opt/apps/jenkins-demo/jenkins-demo.jar

如果成功:
100%
说明 JAR 已经可以从 Jenkins 上传到宿主机。

测试 systemd
继续执行:

ssh -i /root/.ssh/jenkins_deploy \
  [email protected] \
  "systemctl restart jenkins-demo"
检查:
ssh -i /root/.ssh/jenkins_deploy \
  [email protected] \
  "systemctl is-active jenkins-demo"

返回:
active

说明:
Jenkins
 ↓
SSH
 ↓
systemd
 ↓
Spring Boot

已经打通。

七、创建 Jenkins Pipeline

项目根目录创建:

Jenkinsfile

pipeline {
    agent any
    tools {
        maven 'Maven-3.9.10'
        jdk 'jdk21'
    }
    stages {
        stage('1. 拉取Gitee代码') {
            steps {
                checkout scm
                echo "✅ Gitee代码拉取完成"
            }
        }

        stage('2. Maven编译测试打包') {
            steps {
                sh 'mvn clean package'
                echo "📦 Maven构建完成"
                sh '''
                    echo "========== Jar包 =========="
                    ls -lh target/*.jar
                '''
            }
        }

        stage('3. 部署JAR到服务器') {
            steps {
                echo "📦 正在部署新的SpringBoot JAR"
                sh '''
                    scp -i /root/.ssh/jenkins_deploy \
                    target/*.jar \
                    [email protected]:/opt/apps/jenkins-demo/jenkins-demo.jar
                '''
                echo "✅ JAR部署完成"
            }
        }

        stage('4. 重启SpringBoot服务') {
            steps {
                echo "🚀 正在重启SpringBoot服务"
                sh '''
                    ssh -i /root/.ssh/jenkins_deploy \
                    [email protected] \
                    "systemctl restart jenkins-demo"
                '''
                echo "✅ SpringBoot重启完成"
            }
        }

        stage('5. 验证SpringBoot服务') {
            steps {
                sh '''
                    echo "🔍 等待SpringBoot启动..."
                    sleep 5
                    echo "========== 服务状态 =========="
                    ssh -i /root/.ssh/jenkins_deploy \
                    [email protected] \
                    "systemctl is-active jenkins-demo"

                    echo "========== 8081端口 =========="
                    ssh -i /root/.ssh/jenkins_deploy \
                    [email protected] \
                    "ss -lntp | grep 8081"

                    echo "========== 接口测试 =========="
                    curl -f http://172.18.0.1:8081/hello
                    echo ""
                    echo "✅ SpringBoot部署验证成功"
                '''
            }
        }
    }

    post {
        success {
            echo '''
========================================
🎉 Jenkins构建成功 🎉
SpringBoot部署成功
========================================
            '''
        }
        failure {
            echo '''
========================================
❌ Jenkins构建失败 ❌
SpringBoot部署失败
========================================
            '''
        }
        always {
            echo "📝 Pipeline执行结束"
        }
    }
}

第一次手动 Build

先不要急着配置 WebHook。

点击:
Build Now

确认:

1. 拉取Gitee代码
2. Maven编译测试打包
3. 部署JAR到服务器
4. 重启SpringBoot服务
5. 验证SpringBoot服务

全部成功。
如果全部成功:
Jenkins → Spring Boot
就已经打通。

八、配置 Gitee WebHook

进入 Jenkins:

jenkins-demo
    ↓
Configure
    ↓
Triggers

勾选:
Gitee webhook 触发构建
Jenkins 会显示:
http://43.142.88.165:8080/gitee-project/jenkins-demo

保存这个链接。


进入 Gitee 仓库:
管理
    ↓
WebHooks
    ↓
添加 WebHook
URL:
http://43.142.88.165:8080/gitee-project/jenkins-demo
事件:
Push


配置 WebHook 密码
Jenkins:
jenkins-demo
    ↓
Configure
    ↓
Triggers
    ↓
Gitee WebHook 密码

设置一个随机值,例如:
JenkinsGitee-2026-8fK3mP92
保存。
然后 Gitee:
WebHook 密码/签名密钥
填写完全相同的值:
JenkinsGitee-2026-8fK3mP92
保存。

解决 WebHook 403
如果 Gitee 测试出现:

HTTP ERROR 403
anonymous is missing the Job/Build permission
不要直接把 Jenkins 设置成:
Anyone can do anything
这种方式风险太大。
正确方向是使用 Gitee WebHook 的认证机制。
本次通过:
Jenkins Gitee WebHook 密码
Gitee WebHook 密码/签名密钥
解决了 WebHook 请求认证问题。


验证 WebHook
Gitee WebHook 最后一次请求应该看到:

Response:
200

例如:
POST
http://43.142.88.165:8080/gitee-project/jenkins-demo
事件:
Push

Response:
200

说明:
Gitee
 ↓
Jenkins
已经正常通信。

最终测试

不要再点击 Gitee 测试。

直接修改 Spring Boot:

例如:

Hello Jenkins CI/CD

改成:

Hello Jenkins CI/CD V2

然后本地:

git add .
git commit -m "test: gitee webhook auto deploy"
git push

观察 Jenkins。

应该自动出现新的 Build。

然后 Jenkins 自动执行:

拉取代码
 ↓
Maven
 ↓
生成 JAR
 ↓
SCP
 ↓
SSH
 ↓
systemctl restart
 ↓
验证

最后浏览器访问:

http://43.142.88.165:8081/hello

看到:

Hello Jenkins CI/CD V2

说明整个 CI/CD 流程真正完成。

最终架构总结

最终服务器上的结构:

/opt/
├── jenkins/
│   └── jenkins_home/
│
├── apache-maven-3.9.10/
│
└── apps/
    └── jenkins-demo/
        ├── jenkins-demo.jar
        └── app.log

服务:

Jenkins
  Docker
  :8080

Spring Boot
  systemd
  :8081

代码:

Gitee
  ↓
Jenkins

部署:

Jenkins
  ↓ SCP
Ubuntu
  ↓ systemctl
Spring Boot

九、总结

1. Jenkins 不应该负责长期运行 Spring Boot

不要:

Jenkins
 ↓
nohup java -jar

应该:

Jenkins
 ↓
部署 JAR

systemd
 ↓
管理 Spring Boot

2. Docker 容器路径和宿主机路径不一样

宿主机:

/opt/jenkins/jenkins_home

容器:

/var/jenkins_home

Pipeline 中必须使用容器里的路径。

3. Jenkins 8080 和 Spring Boot 8081 要分开

Jenkins:
8080

Spring Boot:
8081

Jenkins Docker 不要再映射:
8081:8081
否则会造成端口冲突。

4. WebHook 403 不一定是网络问题

如果 Gitee 显示:

403 anonymous is missing the Job/Build permission

说明:

Gitee → Jenkins
其实已经访问成功。
真正的问题是 Jenkins 权限/认证。

5. CI/CD 最好一步一步验证

不要一开始就:

Gitee
 ↓
WebHook
 ↓
Jenkins
 ↓
Maven
 ↓
Docker
 ↓
Spring Boot

全部一起搞。

正确排查顺序:

① Jenkins 能启动
        ↓
② Jenkins 能拉代码
        ↓
③ Maven 能打包
        ↓
④ Spring Boot 能手动启动
        ↓
⑤ Jenkins 能上传 JAR
        ↓
⑥ Jenkins 能 SSH
        ↓
⑦ systemd 能重启
        ↓
⑧ Jenkins Pipeline 全流程成功
        ↓
⑨ Gitee WebHook
        ↓
⑩ git push 自动部署

这样每一步出现问题都比较容易定位。

十、最终效果

flowchart LR dev["开发人员<br/><b>修改代码 → git push</b>"] gitee["Gitee代码仓库"] jenkins["Jenkins<br/><b>流水线自动执行</b>"] jar["编译打包生成Jar包<br/>Maven clean package"] host["Ubuntu宿主机<br/><b>执行systemctl</b>"] sb["Spring Boot 应用"] dev --> gitee gitee --<b>WebHook自动触发</b>--> jenkins jenkins --<b>Maven打包</b>--> jar jar --<b>SCP传输Jar到宿主机</b>--> host host --<b>systemctl重启服务</b>--> sb classDef bigfont font-size:18px class dev,gitee,jenkins,jar,host,sb bigfont

开发人员不需要再手动登录服务器:

不用手动上传 JAR
不用手动 kill Java
不用手动启动 Spring Boot
不用手动 Build Jenkins
不用手动刷新部署

最终实现:

一次 git push,自动完成整个 Spring Boot 部署流程。

十一、日后升级准备

当前版本已经完成基础 CI/CD。

后续可以继续升级:

V1
Gitee
 ↓
Jenkins
 ↓
Spring Boot

升级到:

V2
Gitee
 ↓
Jenkins
 ↓
Maven
 ↓
Docker Image
 ↓
Docker Compose
 ↓
Spring Boot

再进一步:

V3
Gitee
 ↓
Jenkins
 ↓
Maven
 ↓
Docker Image
 ↓
私有镜像仓库
 ↓
生产服务器
 ↓
Docker Compose

还可以加入:

Git 分支策略
测试环境
生产环境
审批
回滚
版本号
Docker 镜像版本
日志
健康检查
Nginx
HTTPS

这样就可以逐步从现在的个人项目 CI/CD,升级成更加完整的企业级部署流程。