前言

CI/CD 是持续化继承和持续化部署的简称。目的是在开发的过程中,尽可能的降低人工成本,来完成部署操作。通过 Jenkins 自动流水线工作可以省去很多时间,对于程序开发者而言,将部署 jar 包、修改环境、日志记录等一系列操作集成到 Jenkins 任务内,省时又省力,本次将以 Jenkins 为核心进行从 0 到 1 的搭建与使用,并提供了 Java 单体项目与微服务的通用 shell 脚本供于使用。

环境说明

本文通过两种方式进行演示,第一种是基于 Docker 完成 Jenkins 的安装与应用,Docker 使用版本为 25.0.3;第二种是直接在本机上安装 Jenkins(本人主机为 Mac,所以使用 Mac 来安装,Linux 基本也是一样的)

基于 Docker 的 Jenkins 更多适合训练,因为所有的操作都在容器内、方便后续转移以及迁移。但缺点是 Jenkins 与宿主机的环境不互通,如在容器打完的 jar 包只能放在挂载的目录里。而且 Java 项目需要连接的中间件也非常多、且因为 jar 包是跑在 Jenkins 容器内的,所以配置容器间通信又是一个复杂点。(推荐初上手的同学使用,先用 docker 搭建一个完成的任务进行测试)

而直接部署在本机上更适合生产环境,更加的灵活方便,shell 脚本的操作空间更大,并且可以直接访问各种中间件,省去了容器间通信的配置调试时间

安装 Jenkins(Docker)

基于 Docker 安装 Jenkins 大致分为拉取镜像与脚本编辑

拉取镜像

这里拉取镜像的时候我推荐直接去官网指定版本下载,我一开始通过后缀跟 latest 下载最新版导致很多插件都下不下来。最后去官网指定了一个版本才行 Jenkins-Docker 镜像官网

1
2
3
4
# 修改前
docker pull jenkins/jenkins:latest
# 修改后(因为我本地开发环境用的是17,所以直接下载了一个内置17的jenkins,当然也可以不指定,后通过jenkins内部下载对应的Java版本,不过还是更推荐前者,而且在内部下载Java也比较麻烦。本文以前者进行!!)
docker pull jenkins/jenkins:2.448-jdk17

编写安装脚本

1
vim startJenkins.sh

这里简单介绍一下每个参数,其中 **-u** 需要特别注意!

-d:后台运行

-p:将容器的 8080 端口映射到宿主机的 8081 端口上

-u:使用 UID 为 0 的用户身份运行 Jenkins 容器(这里如果不指定 - u 后面如果想要通过 jenkins 操作 SHELL 脚本会导致没有权限,所以需要使用 root 的身份运行容器)

一般情况下 0 为管理员角色,也可以使用 id 0 来检测是否是 root 用户

image-20240311155842435

-v:挂载目录,将 jenkins 的工作目录挂在到宿主机内,这样使用 jenkins 操作目录时,我们本地也会有相同的反馈

–restart:在容器退出时自动重启容器

–name:配置容器名字

1
2
3
4
5
6
7
docker run -d \
-p 8081:8080 \
-u 0 \
-v /Users/sora33/docker/jenkins/data:/var/jenkins_home \
--restart=always \
--name jenkins \
jenkins/jenkins:2.448-jdk17

创建完成后,使用 chmod -R 赋值脚本权限并执行

1
2
3
4
# 赋值权限
chmod -R 755 startJenkins.sh
# 执行脚本
./startJenkins.sh

输入 IP:8081 进入 Jenkins 主页面,首次进入页面会要求我们输入密码

image-20240311162221114

密码会在容器内部打印出来

1
2
# docker logs 容器名
docker logs jenkins

将密码复制并进入下一级页面

image-20240311162328443

安装 Jenkins(非 Docker)

Mac 平台:

使用 homebrew 安装 Jenkins,如未安装 homebrew 请先安装 homebrew!!!

1
2
3
4
# 安装jenkins
brew install jenkins
# 注意,mac端安装完成后最后去/opt/homebrew/Cellar/jenkins/2.448/bin目录下看一下jenkins的环境变量是否正确!这里注意替换版本号!该目录下有jenkins
sudo vim jenkins

如果 JAVA_HOME 不是如图完整的路径而是 @@HOMEBREW…… 什么的请使用下面命令重新安装

1
brew reinstall jenkins

image-20240313095649414

之后去 /opt/homebrew/Cellar/jenkins/2.448 目录下修改局域网连接与端口号,默认只允许本机连接,改为 0.0.0.0 所有人可连接,下面端口号默认是 8080,这里我改为了 8081

image-20240313095949249

修改完成后,我们需要重启 Jenkins

1
brew services restart jenkins

Linux 平台:

Linux 平台本人暂时未实践,不过只要成功安装 Jenkins 就没问题,区别在于文件的存放路径位置的不同,配置本质是一样的

1
2
3
sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
sudo rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
sudo yum install jenkins

这里放出 Linux 下的 Jenkins 默认目录

/usr/lib/jenkins/:jenkins 安装目录

/etc/sysconfig/jenkins:Jenkins 配置文件目录

/var/lib/jenkins/:工作目录

/var/log/jenkins/jenkins.log:日志等

Jenkins 配置

输入 127.0.0.1:8081 进入 Jenkins 主页面,按照提示去对应目录拿到密码,下面我们需要安装常用的插件

安装插件

进入到该页面后,我们选择安装推荐的插件

PS:这里每个人的网络环境都不同,所以有可能导致一些插件无法正确下载下来

可以尝试改为清华大学的下载站,具体方法是进入刚刚我们挂载的工作目录内,也就是刚刚启动脚本 - v 所指定的目录,找到 hudson.model.UpdateCenter.xml 文件。将里面的 url 改为 https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

非 Docker 端可以先不配置镜像,一会直接通过页面配置

image-20240311162849418

image-20240311162519763

创建用户

这个账号相当于管理员账户,创建完成后后面 URL 一般也不需要改动

image-20240311163706839

下面是配置 Java 与 Maven,我们需要用到 toolsplugins 这两个模块

image-20240311163854811

配置 Maven

首先点击 plugins,继续点击 Available plugins,输入 maven,下载 Maven Integration,安装完成后我们

image-20240311164026093

接着我们回到管理页面点击 tools

Docker 版配置

如果是 Docker 安装的话,我们直接在 Jenkins 容器内新装一个 maven。翻到最底下找到 Maven,新增 Maven,输入 Maven 的名字和版本,选择版本最好跟本地一致,然后点击保存

image-20240311164542834

Linux 与 Mac 配置

一般 mac 与 linux 本机都已经安装完成 maven 了,如果没有安装请自行安装;安装完成后配置一下别名和路径即可

image-20240313100833460

之后翻到最顶上,配置一下使用的 maven 配置文件,路径为 setting.xml 的地址,这样就可以使用本地的仓库

image-20240313100928792

配置 Java

Docker 版配置

在同一个页面找到 JDK 安装部分,因为我下载的 jenkins 内置 jdk17,所以直接进入 jenkins 容器内查看 Java 所在的目录

进入容器可以使用:docker exec -it c7c(容器 id 前 3 位) bash

image-20240311165659880

配置别名,将路径输入,取消自动安装安装,点击保存

image-20240311165748076

Linux 与 Mac 配置

在同一个页面找到 JDK 安装部分,与 maven 一样,我们本机大概率是已经安装好 Java 的,同样只需配置一下别名和路径

image-20240313101056470

页面上配置 Jenkins 下载源

除了通过该文件,我们也可以在插件管理中直接配置

1
https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

image-20240313134732638

Jenkins 的主目录

jenkins 的工作目录可以在系统管理下的 system 中看到,默认是在用户目录下的.jenkins 文件夹内,我们项目的缓存、打包文件都在该目录下的 workspace 内。

image-20241126103521168

Jenkins 使用

Maven 项目自动化构建

  1. 在主页面点击新建任务,选择构建一个 maven 项目,输入任务名称image-20240312091326780

  2. 下面我们输入这个任务的描述、最好选择丢弃旧的构建,在下面选择对应的策略,这里我选择的是只保留最近的 3 次构建,方便发生意外后直接回滚

image-20240312091601261

  1. 下面我们来配置项目的 Git 地址,首先需要添加一个 Git 用户

    image-20240312091844841

以 GitHub 举例,像这样添加一个用户,用户名和密码就是你登陆 GitHub 的账号和密码

image-20240312092050180

继续输入我们的 Git 地址以及所需要构建的分支

image-20240312094410900

  1. 下面是构建的参数,这里的意思为执行 clean package,清除再打包,跳过 test 阶段

    image-20240312095013918

  2. 最后需要配置构建完成后的操作,一般是通过执行 shell 脚本,将刚刚构建完成的项目跑起来并记录日志

image-20240312133619142

  1. 脚本完成后我们点击保存并进行手动构建,尝试执行一次

boot 项目通用 shell 脚本

boot 脚本需要配置如下 5 个地方,通过序号对应

1:LOG_DIR 与 LOG_FILE,前者为日志所需要存储的路径,后者为日志文件名

3:将 buildSoraProject 改为 jenkins 的任务名称,这里我的任务名称即为 buildSoraProject

4:配置 jar 包的名字,如果不知道 jar 包的名字,可以通过进入容器内该路径查看,或者直接在 pom 文件的 build 标签内提前指定好

image-20240312134300799

6:配置 buildId,只要唯一即可,一般为任务名称

7:目前只配置了字符集与时间,可以根据需求自己追加,例如常用的 - Dspring.profiles.active=test

shell 脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash

# 1.定义日志目录和日志文件的路径
LOG_DIR="$JENKINS_HOME/projectLog/buildSoraProject"
LOG_FILE="$LOG_DIR/sora_project_log.log"

# 2.确保日志目录存在,如果不存在则创建
mkdir -p $LOG_DIR

# 3.进入 Jenkins 的 workspace 目录下的 buildSoraProject/target 文件夹
cd $JENKINS_HOME/workspace/buildSoraProject/target

# 4.配置 jar包名称 sora33-0.0.1-SNAPSHOT.jar
JAR_FILE=sora33-project.jar

# 5.尝试停止已运行的 JAR 文件进程
PID=$(ps -ef | grep $JAR_FILE | awk '{print $2}')
if [ ! -z "$PID" ]; then
echo "Stopping existing Java process: $PID"
kill -9 $PID
fi

# 6.分配打包id
BUILD_ID=buildSoraProject

# 7.执行 jar 包 将日志输出到之前定义的日志文件中 如果时间异常再追加GMT+8,第一次可以先注释
nohup java -Dfile.encoding=UTF-8 \
-Duser.timezone=GMT+08 \
-jar $JAR_FILE > $LOG_FILE 2>&1 &

echo "Application is running, and logs are being written to $LOG_FILE"

cloud 项目通用 shell 脚本

cloud 脚本需要配置如下 4 个地方,通过序号对应

1:LOG_DIR_BASE 配置日志的路径,这里我放在 Jenkins 的工作目录下的 projectLog 文件夹里

2:打包 id,这里请务必将打包 id 与任务名称对应上,因为 Jenkins 会把该任务的结果输出在任务名称下的文件夹里

3:服务列表定义,这里需要配置项目所有的启动类,如下图是我的项目结构,可以参考 shell 脚本的格式对应你自己的项目结构

image-20240313112208745

6:配置 Java 模块的启动命令,目前只配置了字符集与时间,可以根据需求自己追加,例如常用的 - Dspring.profiles.active=test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/bin/bash

# 1.定义日志目录的基本路径
LOG_DIR_BASE="$JENKINS_HOME/projectLog"

# 2. 打包id
BUILD_ID=sora

# 3.服务列表定义
SERVICES=("sora-gateway" "sora-auth" "sora-modules/sora-user/user-server" "sora-modules/sora-system/system-server" "sora-modules/sora-system/spider-server")

# 4.停止所有服务
for SERVICE in ${SERVICES[@]}; do
JAR_FILE=$(find $JENKINS_HOME/workspace/$BUILD_ID/$SERVICE/target -name "*.jar" -print -quit) # 假设每个服务目录下只有一个jar包
if [[ ! -z "$JAR_FILE" ]]; then
PID=$(ps -ef | grep $JAR_FILE | grep -v grep | awk '{print $2}')
if [[ ! -z "$PID" ]]; then
echo "Stopping $SERVICE (PID: $PID)"
kill -9 $PID
fi
fi
done

# 5.启动所有服务
for SERVICE in ${SERVICES[@]}; do
JAR_FILE=$(find $JENKINS_HOME/workspace/$BUILD_ID/$SERVICE/target -name "*.jar" -print -quit) # 假设每个服务目录下只有一个jar包
if [[ ! -z "$JAR_FILE" ]]; then
# 为每个服务创建一个专门的日志目录
LOG_DIR="$LOG_DIR_BASE/$(basename $SERVICE)"
mkdir -p $LOG_DIR
LOG_FILE="$LOG_DIR/$(basename $SERVICE)_log.log"

# 6.Java启动命令行配置
nohup java -Dfile.encoding=UTF-8 -Duser.timezone=GMT+08 \
-jar $JAR_FILE > $LOG_FILE 2>&1 &

echo "$SERVICE is running, logs are being written to $LOG_FILE"
else
echo "Jar file for $SERVICE not found."
fi
done

配置 Aliyun 的 maven 镜像源(Docker 版)

PS:非 Docker 安装如果没有配置 aliyun 仓库地址,把下面的仓库地址加入到 maven 的 conf 目录下的 settings 的 mirrors 标签内即可。

因为 Docker 的 maven 还没配置 aliyun 仓库地址,并且是第一次使用 maven,会在 jenkins_home 下生成一个 tools 文件夹,我们可以到挂载的 data 文件夹下查看,这里我们需要在 maven 内加入 aliyun 的仓库地址,具体路径为你挂载的路径 /tools/hudson.tasks.Maven_MavenInstallation/Maven-3.9.1/conf

修改 settings.xml 文件夹,将 aliyun 的仓库地址配置到 mirrors 标签内

1
2
3
4
5
6
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>

结果测试

Docker

以 boot 项目举例,打包完成后我们可以在挂载目录中看到项目的日志输出,那我们要如何访问到项目呢?

image-20240313132900782

因为我们启动 jenkins 容器时只指定了 Jenkins 的端口并没有把 Jenkins 内部的项目端口暴露出来,所以我们需要删除 Jenkins 容器,再次创建,这次加上我们的项目端口,注意因为我们删除的只是容器,挂载的 data 文件夹并没有删除,所以 Jenkins 的数据缓存还会留在我们宿主机上,当我们再次安装 Jenkins 时,历史数据都还在,所以千万注意不要删除挂载的目录!!

1
2
# 删除容器
docker rm -rf 容器id/容器名

修改我们的安装脚本,这次多暴露一个项目的端口 1234

1
2
3
4
5
6
7
8
docker run -d \
-p 8081:8080 \
-p 1234:1234 \
-u 0 \
-v /Users/sora33/docker/jenkins/data:/var/jenkins_home \
--restart=always \
--name jenkins \
jenkins/jenkins:2.448-jdk17
1
2
# 再次启动安装脚本
./startJenkins.sh

通过查看 1234 端口,可以发现是 docker 在占用

image-20240313132628391

调用接口也可以正常返回

image-20240313132645759

非 Docker

如果我们直接在宿主机部署项目,那么我们直接访问接口就可以。

直接放个日志图,项目启动成功并且接口也成功访问到

image-20240313133515417

结束语

Jenkins 的初步用法到这里就结束了,当然 Jenkins 也还有非常多的用法,大家也可以摸索一下,如果在搭建过程中遇到问题也欢迎留言,如果觉得有用可以分享给他人~