Jenkins + Spring Boot + GitHub 实现自动化部署
环境:
- Jenkins 部署在 docker 中
- 本地开发 Spring Boot 的 Demo (配合 Dockerfile 进行构建)
- 服务器:腾讯云 Debian GNU/Linux 11 (bullseye)
步骤:
Docker 安装 Jenkins
ps:没安装的看这里有安装教程
在 Jenkins 容器中配置需要的环境
ps:也可以在 Jenkins 客户端配置 JDK 和 Maven ,但是第一次构建的时候会导致速度很慢,我就在本地安装了
(也可以把本机的 JDK 和 Maven 环境挂载到 Jenkins 容器中)
首先进入Jenkins
docker exec -it --user root <container-id> /bin/bash
安装 JDK
ps:安装前先检查(
java -version
),Jenkins 有的自带的有 JDK,如果有了就跳过这一步
- 安装
apt install default-jdk
ps:默认 openjdk:17 版本
- 检查安装:
java -version
- 查看环境变量
echo $PATH
ps:列出所有环境变量,保存 openjdk 的即可
示例输出:/root/.nvm/versions/node/v20.14.0/bin:/usr/java/jdk-17.0.11+9/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
jdk 就是/usr/java/jdk-17.0.11+9/bin
安装 Maven
- 执行命令
apt update && apt install maven
- 检查版本
mvn -v
输出如下:
root@9ae2b288441d:/# mvn -v
Apache Maven 3.8.7
Maven home: /usr/share/maven
Java version: 17.0.12, vendor: Eclipse Adoptium, runtime: /opt/java/openjdk
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "5.10.0-26-amd64", arch: "amd64", family: "unix"
- 保存 Maven 路径
/usr/share/maven
- 配置阿里云镜像(一定要配!不然以我们自己使用的这种 2 核 2G 的服务器,下载速度会很慢!!!)
在/usr/share/maven/conf/settings.xml
文件中,<mirrors></mirrors>
标签中间添加下面的代码
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
配置 ssh 密钥
为了方便 Jenkins 从 GitHub 使用 SSH 拉取代码,如果不需要则可以不配置(如果 HTTPS 可以拉取就不用)
- 获取并添加 GitHub 主机密钥
防止出现
Host key verification failed
错误
# 切换到 Jenkins 用户(如果不能切换,退出容器,重新以普通角色进入,去掉 --user root 的命令即可)
su - jenkins
# 创建 .ssh 目录
mkdir -p ~/.ssh
# 设置 .ssh 目录权限
chmod 700 ~/.ssh
# 获取 GitHub 主机密钥并添加到 known_hosts
ssh-keyscan github.com >> ~/.ssh/known_hosts
# 设置 known_hosts 文件权限
chmod 644 ~/.ssh/known_hosts
# 测试连接 GitHub 仓库
ssh -T git@github.com
- 生成 SSH 密钥对
如果没有 SSH 密钥对,而直接以 SSH 方式拉取会报错:
Permission denied (publickey)
如果有密钥对,可以跳过这一步。直接复制
.ssh/id_rsa.pub
的内容
# 生成密钥对,后面一直按空格即可
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# 获取公钥内容,并复制
cat ~/.ssh/id_rsa.pub
然后在github中添加 SSH key
- 进入
Settings > SSH and GPG keys > New SSH key
。 - 将公钥粘贴到 SSH key 中并保存。
注意:
确认 SSH 密钥文件权限:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
测试 SSH 连接:
ssh -T git@github.com
如果配置正确,你应该看到类似以下的信息:
Hi <username>! You've successfully authenticated, but GitHub does not provide shell access.
到此,Jenkins 就可以使用 ssh 拉取 github 的代码了
到 Jenkins 客户端配置
安装插件
ps:除了刚安装 Jenkins 时候的推荐安装插件,其余需要安装的如下:
- Maven Integration (Maven 集成插件,可以执行 Maven 的各种命令)
- SSH (这个是使用 SSH 协议来执行打包后的 shell 命令的,这个很多年没维护了,有漏洞,但是目前没找到平替的,只能委屈使用这个了)
- Publish Over SSH (通过 SSH 发送打包好的 jar 文件)
- SSH Server (这个忘了推荐安装里面有没有了,如果有的话就不用安装了)
全局工具配置
ps:这一步是为了创建 Maven 任务需要添加的配置
- Maven 配置使用默认即可
- Maven 安装,名称随便填即可,设置 Maven 目录,如上述输出
/usr/share/maven
- JDK 安装,名称随便填即可,JAVA_HOME 从上述的环境变量中取出,删掉后面的 /bin 即可
- 最后记得保存
凭据管理
- github 用户凭证
- 类型:Username with password
- 范围:全局(Jenkins,nodes,items,all child items,etc)
- 用户名:github 用户名
- Treat username as secret:勾选
- 密码:github 登录密码
- ID:不填
- 描述:github-login(随意填)
- 服务器登录凭证(参考上面)
- 最后记得保存
系统配置
- SSH Remote Hosts(SSH sites)
Hostname:服务器IP
- Port:22
- Credentials:选择服务器的登录凭证(上面添加的)
- serverAliveInterval:100 (根据个人需求修改)
- timeout:100(根据个人需求修改)
- SSH Server
- Name:自定义即可
- Hostname:服务器IP
- Username:服务器登录用户名
- Remote Directory:远程目录,随便设置即可(但是必须要保证目录存在)
- 最后记得保存
新建任务
这里罗列了两种不同的新建任务方式
- Maven 任务只适用于基于 Maven 构建的项目
- Pipeline 任务包含 Maven 任务,还可以实现更多其他的选项(推荐)
新建Maven项目
- 填写任务名称
- 本次测试是使用 Maven,选择
构建一个Maven项目
- 勾选 Github 项目,填入项目 URL (SSH 即可)
- 源码管理 -> Git
- 输入 Repository URL (SSH 即可)
- 添加凭证(上述 GitHub 用户凭证)
- 指定分支:Branches to build - >
*/master
- 构建触发器
- 勾选
Generic Webhook Trigger
- 添加 Token(所有的触发器前缀 URL 一样,添加 Token 来辨别不同的触发器
- 其余什么都不选(有需求可以自行添加)
- 勾选
- Build -> Goals and options:
clean package
- Post Steps -> Run regardless of build result
- 选择执行 shell 命令
- 输入 shell 脚本(shell 示例内容在下面附录)
- 保存即可
新建流水线项目(推荐)
- 输入任务名称
- 选择“流水线”
- 勾选自己使用的 SCM
- 填写仓库 URL
- 构建触发器参考上面
- 定义流水线(流水线示例代码在下面附录)
- 保存即可
GitHub 配置 Webhook
ps:给仓库配置 webhook,和上面的触发器连接
步骤如下:
- 打开对应 GitHub 存储库
- 点击 settings,左侧 Webhooks
- 右上角 add webhook
- Payload URL:
http://<Jenkins-Url>/generic-webhook-trigger/invoke?token=xxxx
(token填上面的) - 下面可以选择对应想要触发的操作(push,pull Requeest等)
- 点击保存即可。
附录
sh 脚本(基于Maven任务的)
ps:构建完成后的执行的脚本
#!/bin/bash
# 服务名称
SERVER_NAME=jenkins-demo1
# 工程所在路径(根据自己情况进行调整)
APP_HOME=/var/jenkins_home/workspace/$SERVER_NAME
# maven打包后的jar包名
JAR_NAME=demo1-0.0.1-SNAPSHOT.jar
# jar包的目录
JAR_PATH=${APP_HOME}/target
# 杀死之前的进程
PID_FILE="${APP_HOME}"/"${SERVER_NAME}".pid
if [ -f "${PID_FILE}" ];then
PID=`cat "$PID_FILE"` && kill -9 $PID && echo "kill process "${PID}" finished!"
fi
cd $JAR_PATH
# 修改文件权限
chmod 755 $JAR_NAME
echo "当前路径:"
pwd
# 启动服务
BUILD_ID=dontKillMe nohup java -jar $JAR_NAME &
echo "nohup java -jar ${JAR_NAME} >output.log &"
echo "java程序启动!!!"
# 将新进程ID写到文件中
JAVA_PID=$!
echo "${JAVA_PID}" > "${PID_FILE}"
pipeline 脚本
ps:pipeline 脚本实现了以下步骤
- 拉取代码
- 编译构建
- 移除原有的镜像和容器
- 构建新的镜像和容器并运行
pipeline {
agent any
environment {
GIT_URL = 'git@github.com:yannqing/YanOJ-SandBox.git'
APP_NAME = 'sandbox'
APP_PROFILE = 'prod'
APP_IMAGE = 'yanojsandbox:latest'
APP_PORT = '8091:8080'
}
stages {
stage('拉取代码') {
steps {
git branch: 'master', url: GIT_URL
}
}
stage('编译构建') {
steps {
sh "mvn clean package"
}
}
stage('移除镜像和容器') {
steps {
script {
// 检查容器是否存在
def containerExists = sh(script: 'docker ps -a --format "{{.Names}}" | grep -q "^${APP_NAME}"', returnStatus: true) == 0
// 检查镜像是否存在
def imageExists = sh(script: 'docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "^${APP_IMAGE}$"', returnStatus: true) == 0
// 如果容器存在,则停止和移除
if (containerExists) {
echo "容器存在"
def isRunning = sh(script: 'docker ps --format "{{.Names}}" | grep -q "^${APP_NAME}"', returnStatus: true) == 0
if (isRunning) {
sh "docker stop ${APP_NAME}"
}
echo "删除容器"
sh "docker rm ${APP_NAME}"
} else {
echo "容器不存在"
}
// 如果镜像存在,则移除
if (imageExists) {
echo "删除镜像"
sh "docker rmi ${APP_IMAGE}"
} else {
echo "镜像不存在"
}
}
}
}
stage('构建镜像,创建并运行容器') {
steps {
// 基于 Dockerfile 进行构建
sh "docker build -f Dockerfile.dockerfile -t ${APP_IMAGE} ."
sh "docker run -it --name ${APP_NAME} -v /yannqing/${APP_NAME}:/yannqing/${APP_NAME} -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -p ${APP_PORT} -d ${APP_IMAGE}"
}
}
}
}
Dockerfile 文件
# 使用 OpenJDK 17 slim 版本作为基础镜像
FROM openjdk:17-slim
# 设置维护者标签
LABEL maintainer="yannqing <yannqing.com>"
LABEL version="1.0"
LABEL description="YanOJ SandBox"
# 设置工作目录
WORKDIR /yannqing/sandbox/java
# 创建一个挂载点
VOLUME /yannqing/sandbox/logs
# 复制应用程序
COPY ./target/yanoj-code-sandbox-0.0.1-SNAPSHOT.jar /tmp/app.jar
# 暴露端口
EXPOSE 8091
# 启动命令
CMD ["java", "-jar", "/tmp/app.jar"]