Jenkins教程
Jenkins 是一款流行的开源持续集成(Continuous Integration)工具,广泛用于项目开发,具有自动化构建、测试和部署等功能。
Jenkins的特征:
- 开源的Java语言开发持续集成工具,支持持续集成,持续部署。
- 易于安装部署配置:可通过
yum安装,或下载war包以及通过docker容器等快速实现安装部署,可方便web界面配置管理。 - 消息通知及测试报告:集成
RSS/E-mail通过RSS发布构建结果或当构建完成时通过e-mail通知,生成JUnit/TestNG测试报告。 - 分布式构建:支持
Jenkins能够让多台计算机一起构建/测试。 - 文件识别:
Jenkins能够跟踪哪次构建生成哪些jar,哪次构建使用哪个版本的jar等。 - 丰富的插件支持:支持扩展插件,你可以开发适合自己团队使用的工具,如
git,svn,maven,docker等。
2. Jenkins安装和持续集成环境配置
2.1 Gitlab代码托管服务器安装
2.1.1 Gitlab简介
GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务。
GitLab和GitHub一样属于第三方基于Git开发的作品,免费且开源(基于MIT协议),与Github类似,可以注册用户,任意提交你的代码,添加SSHKey等等。不同的是,GitLab是可以部署到自己的服务器上,数据库等一切信息都掌握在自己手上,适合团队内部协作开发,你总不可能把团队内部的智慧总放在别人的服务器上吧?简单来说可把GitLab看作个人版的GitHub。
2.1.2 Gitlab安装
官网步骤:



[!tip]
按照上诉步骤走即可。
安装相关依赖
sudo yum install -y curl policycoreutils-python openssh-server perl启动ssh服务&设置为开机启动
sudo systemctl enable sshdsudo systemctl start sshd开放ssh以及http服务,然后重新加载防火墙列表
sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo systemctl reload firewalld[!tip]
如果关闭防火墙就不需要做以上配置。
安装 Postfix
接下来,安装 Postfix(或 Sendmail)来发送通知电子邮件。如果您想使用其他解决方案发送电子邮件,请跳过此步骤并在安装 GitLab 后配置外部 SMTP 服务器。
安装之前,先查看Linux Centos查看postfix已经安装,如果安装过,则无需安装,使用命令:
rpm -qa | grep postfix[!note]
笔者不推荐使用postfix发送邮件,postfix相对来说比较复杂一些。官网有很丰富的邮箱服务配置,进行邮件通知。
如果没有安装,那么就可以按照下面步骤走了。
sudo yum install postfix sudo systemctl enable postfix sudo systemctl start postfix如果安装过程中出现如下错误:

可以通过如下命令解决:
wget https://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql-community-libs-compat-5.7.32-1.el7.x86_64.rpmrpm -vih mysql-community-libs-compat-5.7.32-1.el7.x86_64.rpm之后就可以正常安装了。
下载gitlab包,并且安装
关于gitlab-ee和gitlab-ce,二者是基于同样的核心代码进行开发,只是gitlab-ee(企业版)功能更强大,但需要付费使用,有30天试用期。但试用期过后如果不付费,它就跟gitlab-ce(社区版)功能是完全一样的,只是需要付费的功能无法再继续使用而已,所以这两个版本可以随意选择安装,但如果将来有付费的打算,直接安装gitlab-ee版本是个有远见的选择。当然,即使不付费,gitlab-ee使用上和gitlab-ce没有任何区别。
所以接下来的操作我们就以安装gitlab-ee为例进行。
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.rpm.sh | sudo bash这里默认下载的最新版本。
出现下图所示,标识gitlab reposity镜像下载成功:
curl命令执行完成后,会在
/etc/yum.repos.d目录下生成gitlab_gitlab-ee.repo或gitlab_gitlab-ce.repo文件。
接下来的步骤:

最重要的一点就是:可以安装的同时,配置git仓库访问的域名,要求域名是有效的。这里由于我暂时没有域名,所以就用IP代替。
sudo EXTERNAL_URL="https://gitlab.example.com" yum install -y gitlab-ee[!tip]
这里的
EXTERNAL_URL替换为你自己的域名或者IP即可。这里你也可以暂时不用配置,直接安装即可:
yum install -y gitlab-ee
修改GitLab配置文件
指定服务器ip和自定义端口,使用命令:
vim /etc/gitlab/gitlab.rb
重载配置
gitlab-ctl reconfigure这个过程有点慢,因为会启动部分核心服务,请耐心等待哈。
出现下图所示,说明reconfigure finished:

把端口添加到防火墙
firewall-cmd --zone=public --add-port=82/tcp --permanentfirewall-cmd --reload启动GitLab
gitlab-ctl start启动成功如图所示:


[!important]
GitLab搭建(初始化)好了之后,其实系统会自动的给你生成一个管理员的账号。
账号的用户名为root;密码存放在文件:
/etc/gitlab/initial_root_password中,密码不会含空格,且会在 24 小时后自动被删除。实际上,在第一次使用命令
gitlab-ctl reconfigure初始化 GitLab 配置时,GitLab 其实已经提示过这些信息,只是很多人对英文不敏感,尤其是一长段英文中夹杂着一个重要信息时。
2.1.3 Gitlab添加组、创建用户、创建项目
创建组
使用管理员 root 创建组,一个组里面可以有多个项目分支,可以将开发添加到组里面进行设置权限,不同的组就是公司不同的开发项目或者服务模块,不同的组添加不同的开发即可实现对开发设置权限的管理。


创建用户



用户创建好了之后,管理员可以更改所有用户的密码,用户本人,第一次登陆的时候,也是需要设置密码的。


将用户添加到组中
选择某个用户组,进行Members管理组的成员


具体权限说明:
- Guest:可以创建issue、发表评论,不能读写版本库
- Reporter:可以克隆代码,不能提交,QA、PM可以赋予这个权限
- Developer:可以克隆代码、开发、提交、push,普通开发可以赋予这个权限
- Maintainer:可以创建项目、添加tag、保护分支、添加项目成员、编辑项目,核心开发可以赋予这个权限
- Owner:可以设置项目访问权限 - Visibility Level、删除项目、迁移项目、管理组成员,开发组组长可以赋予这个权限
在用户组中创建项目
以刚才创建的新用户身份登录到GitLab,然后在用户组中创建新的项目。



这样项目就创建好了。
2.1.4 源码上传到GitLab仓库
[!tip]
这部分是开发者都会的,不多讲。
2.2 Jenkins安装
在安装Jenkins之前,需要安装JDK,JDK版本不同,对应的Jenkins版本也是有区别的

下载JDK这里就不赘述了。
获取jenkins安装包
下载页面:https://jenkins.io/zh/download/

这里我选择的是CentOS,按照他的按照步骤走即可:


[!important]
这一步安装好了之后,启动Jenkins,可能很多人在启动Jenkins的时候会失败,一定要注意JDK和Jenkins的版本比对。比如我这里我的JDK版本是17,我这里Jenkins的版本是2.462.1-1.1,查看版本比对图,可见我的JDK版本是不支持的:
所以,这里我最高只能使用LTS版本的2.426.1版本。
安装方式1:通过war包安装和启动
由于目前官网直接用他的下载命令,会导致下载的最新的版本,从而导致和我们JDK的版本不匹配,所以,我要去找历史的版本:


下载了war包到服务器上之后,使用命令启动即可:
java –jar war包文件 --httpPort=9000httpPort表示配置指定Jenkins端口,由于Jenkins默认端口是8080,通常8080端口可能会有占用情况,所以换成自己定义的端口9000。
[!caution]
这里的端口防火墙要放过,不然无法访问。
#列出已允许的服务 firewall-cmd --zone=public --list-services #允许 指定TCP 端口 通过防火墙: firewall-cmd --zone=public --add-port=要放过的端口/tcp --permanent之后访问启动并且Jenkins:
IP:端口

安装方式2:通过RPM包安装和启动(推荐)
访问地址:https://archives.jenkins.io/redhat-stable/

将对应的包下载到服务器上之后,使用命令安装:
rpm -ivh jenkins-2.426.1-1.1.noarch.rpm找到 Jenkins重要的配置文件:
cd /usr/lib/systemd/system这个目录下有个
jenkins.service文件,这个文件就是新版Jenkins的配置文件,其余配置文件不用管。之后修改配置文件:

之后重新加载一下配置:
systemctl daemon-reload启动命令:
systemctl start jenkins查看Jenkins服务状态:
systemctl status jenkins跳过插件安装
因为Jenkins插件需要连接默认官网下载,速度非常慢,而且经过会失败,所以我们暂时先跳过插件安装。


添加一个管理员账户,并进入Jenkins后台



这样就配置OK了。
2.3 Jenkins插件管理

Jenkins国外官方插件地址下载速度非常慢,所以可以修改为国内插件地址:
在Jenkins中,修改升级站点的配置

修改之后,在浏览器的输入栏中输入restart重启Jenkins服务,即可生效:http://192.168.254.141:9000/restart
2.4 Jenkins用户权限管理
我们可以利用Role-based Authorization Strategy插件来管理Jenkins用户权限:

当插件安装好了之后,进入安全设置:


当你点击保存之后,Jenkins管理界面就会多出一个角色管理的入口:

点击进去进行具体的角色管理:

这里的管理员角色,最好是赋予所有权限:
这里的项目角色,也可以赋予所有权限:
当角色管理设置好了之后,现在我们创建一些用户,并且赋予对应的角色:





现在用户创建好了,给用户分配角色:
绑定规则如下:
- test1用户分别绑定common全局角色和test-one项目角色
- test2用户分别绑定common全局角色和test-two项目角色

现在角色也分配给用户了,现在我们来创建项目来测试权限:



现在我分别登陆test1和test2查看:


2.5 Jenkins凭据管理
凭据可以用来存储需要密文保护的数据库密码、Gitlab密码信息、Docker私有仓库密码等,以便Jenkins可以和这些第三方的应用进行交互。
要在Jenkins使用凭证管理功能,需要安装Credentials Binding插件:


现在我们来添加凭据,点击凭据管理,之后点击全局


Username with password:用户名和密码SSH Username with private key: 使用SSH用户和密钥Secret file:需要保密的文本文件,使用时Jenkins会将文件复制到一个临时目录中,再将文件路径设置到一个变量中,等构建结束后,所复制的Secret file就会被删除。Secret text:需要保存的一个加密的文本串,如钉钉机器人或Github的api tokenCertificate:通过上传证书文件的方式
常用的凭证类型有:Username with password(用户密码)和SSH Username with private key(SSH密钥)
接下来以使用Git工具到Gitlab拉取项目源码为例,演示Jenkins的如何管理Gitlab的凭证。
[!note]
由于要拉取代码,肯定要用到Git服务,所以Jenkins的服务器以及对应的服务需要安装Git。
服务器安装git服务:
yum install -y git
添加凭据:

测试凭据是否可用,我们需要修改一些之前创建的项目信息:


修改之后,点击构建,看看代码是否能够拉取下来:



查看工作区有两种方式:
Jenkins查看

服务器查看

上诉的凭据管理是基于账号密码的方式,现在再来看看基于SSH密钥的方式:
使用一个账户生成公钥和私钥,一般选择项目的创建者,或者管理员,这里我就用root
ssh-keygen -t rsa在/root/.ssh/目录保存了公钥和使用

把生成的公钥放到GitLab中

在Jenkins中添加凭证,配置私钥

使用项目来测试凭据是否可用

[!note]
如果自己确定公钥已经设置到了GitLab中,私钥也设置到了Jenkins的凭据中,并且是没问题的,但是在配置Jenkins项目中的源码管理仍然报错:
则需要看一下GitLab的相关文档:https://docs.gitlab.com/17.2/ee/user/ssh.html
执行如下命令:
#1. 确认你的SSH key已经成功添加 ssh -T git@192.168.254.141 #ssh -T git@gitlab.example.com
这要出现了这个,之后,你再去Jenkins上项目配置,问题就解决了。
2.6 Jenkins整合Maven
我们知道,用Jenkins可以用来持续集成和构建Java代码,在Java项目中, 包管理工具一般都是Maven,所以,这里也需要在Jenkins服务器上安装Maven环境。
2.6.1 Maven下载以及配置
下载以及解压Maven命令此处省略….
配置Maven环境变量:
vim /etc/profile
export JAVA_HOME=/usr/local/src/JDK/jdk-17.0.11
export MAVEN_HOME=/software/maven/apache-maven-3.9.4
export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin
重置配置:
source /etc/profile
检验环境变量是否配置成功:
mvn -v
修改Maven的settings.xml:
#创建本地仓库目录
mkdir /software/maven/repo
vim /software/maven/apache-maven-3.9.4/conf/settings.xml
修改里面的本地仓库地址以及镜像地址:


2.6.2 全局工具关联JDK以及Maven



2.6.3 添加Jenkins全局变量


测试Maven是否配置成功:


保存之后构建一次项目:

2.7 Jenkins整合Tomcat
[!tip]
由于现在Java项目基本上都是采用的SpringBoot项目,SpringBoot项目里面又内置又tomcat,所以本小节就不展开讲解。
3. Jenkins构建Maven项目
3.1 Jenkins构建项目类型介绍
自由风格项目(Freestyle Project)
这是 Jenkins 中最基础、最常用的项目类型。它提供了基本的配置选项,用于构建、测试和部署应用程序。
特点:
- 配置简单,适合小型或中型项目。
- 支持源代码管理(SCM)集成,如 Git、SVN 等。
- 可以添加多个构建步骤,如执行 shell 脚本、调用批处理命令等。
- 通过 Jenkins 插件,能扩展各种功能,如邮件通知、打包、发布等。
Maven 项目(Maven Project)
专门用于 Maven 构建工具的项目类型,Maven 是一个项目管理和构建自动化工具,特别适合 Java 项目。
特点:
- 直接集成 Maven 构建步骤,支持 Maven 生命周期(如
clean,install,deploy等)。 - 自动检测和解析
pom.xml文件。 - 支持标准的 Maven 插件和报告生成。
- 直接集成 Maven 构建步骤,支持 Maven 生命周期(如
Pipeline 项目(重点🌟🌟🌟)
这是 Jenkins 中最强大、最灵活的项目类型,允许你通过代码定义整个构建过程。Pipeline 是用 Groovy 语言编写的脚本,定义从代码拉取到部署的整个过程。
特点:
- 支持
Declarative Pipeline(声明式)和Scripted Pipeline(脚本式)两种语法:- Declarative Pipeline: 语法更简洁,适合大多数用例,易于阅读和维护。
- Scripted Pipeline: 灵活性更高,可以编写复杂的 Groovy 逻辑。
- 支持多阶段构建(如构建、测试、部署阶段)。
- 支持并行执行和条件分支。
- 与现代 CI/CD 流程高度集成,如 GitOps、容器化部署等。
- 支持
每种类型的构建其实都可以完成一样的构建过程与结果,只是在操作方式、灵活度等方面有所区别,在实际开发中可以根据自己的需求和习惯来选择。
[!tip]
个人推荐使用**流水线类型(Pipeline )**,因为灵活度非常高
3.2 自由风格项目(Freestyle Project)
下面演示创建一个自由风格项目来完成项目的集成过程:拉取代码->编译->打包->部署

拉取代码

编译打包部署
编译和打包之前都使用过maven命令实现过,这里的部署,由于现在的项目都是jar包了,所以直接
java -jar来执行项目
[!note]
这里保存之后,你出去构建,可能会出现这样的提示:
这里暂时不用担心,我们这里只是模拟部署,并不是符合要求的部署,一般是不允许直接
java -jar的,这个后面会讲到。
3.3 Maven 项目(Maven Project)
安装
Maven Integration插件
创建Maven项目

配置项目
拉取代码和远程部署的过程和自由风格项目一样,只是”构建”部分不同

最后测试,测试通过。
3.4 Pipeline流水线项目构建
Jenkins Pipeline 是一种用于定义和自动化持续集成/持续交付 (CI/CD) 流程的强大工具。通过 Pipeline,开发者可以使用代码来描述整个构建、测试、部署过程,使得流程更加灵活和可复用。Pipeline 提供了两种主要的语法:Declarative Pipeline(声明式)和 Scripted Pipeline(脚本式)。
3.4.1 Pipeline 的基本概念
- Pipeline: Jenkins 中的 Pipeline 是一系列自动化步骤的组合。每个 Pipeline 都描述了从源代码的拉取、编译、测试到部署的整个过程。
- Node: Jenkins 中的 Pipeline 通常运行在一个或多个 “Node” 上,这些 Node 可以是 Jenkins 主机或从机。
- Stage: Pipeline 的核心组成部分,用于表示构建过程中的不同阶段,如 “Build”、”Test”、”Deploy” 等。
- Step: 在每个 Stage 中具体执行的动作,如执行脚本、调用命令行工具、发送通知等。
3.4.2 Declarative Pipeline(声明式 Pipeline)
Declarative Pipeline 是一种更简单和结构化的方式来定义 Jenkins Pipeline,适合大多数用例。它使用了更易读的语法,强制执行某些最佳实践。
示例:
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building...'
// 示例:执行构建命令,如 `mvn clean install`
}
}
stage('Test') {
steps {
echo 'Testing...'
// 示例:执行测试命令,如 `mvn test`
}
}
stage('Deploy') {
steps {
echo 'Deploying...'
// 示例:执行部署命令,如将构建产物部署到服务器
}
}
}
}
关键组件:
- pipeline: Pipeline 块是声明式 Pipeline 的根元素,所有配置都在其中定义。
- agent: 定义 Jenkins 运行 Pipeline 的位置。
agent any表示可以在任何可用的 Node 上运行。 - stages: 定义了一系列的构建阶段,每个阶段由一个
stage块表示。 - steps: 每个
stage中包含实际执行的步骤,使用steps块定义。
高级特性:
post:在 Pipeline 结束后执行的一些操作,如
always(无论成功与否都会执行)、success(仅在成功时执行)、failure(仅在失败时执行)等。post { always { echo 'This will always run' } success { echo 'This will run only if the pipeline succeeds' } failure { echo 'This will run only if the pipeline fails' } }options: 可以为 Pipeline 设置一些全局选项,比如
timeout,retry,timestamps等。options { timeout(time: 1, unit: 'HOURS') retry(3) timestamps() }
3.4.3 Scripted Pipeline(脚本式 Pipeline)
Scripted Pipeline 提供了更多的灵活性,可以使用 Groovy 语言的全部功能,适合需要复杂逻辑的 CI/CD 流程。
示例:
node {
stage('Build') {
echo 'Building...'
// 示例:执行构建命令
}
stage('Test') {
echo 'Testing...'
// 示例:执行测试命令
}
stage('Deploy') {
echo 'Deploying...'
// 示例:执行部署命令
}
}
关键组件:
- node:在脚本式 Pipeline 中,
node是顶层的概念,用于指定在哪个 Node 上运行 Pipeline。 - stage:和声明式 Pipeline 一样,
stage用于定义构建的不同阶段。 - steps:直接在
node块中定义的具体执行步骤。
高级特性:
- 条件逻辑:由于 Scripted Pipeline 是基于 Groovy 语言的,可以使用条件语句、循环等控制流。
node {
stage('Build') {
if (env.BRANCH_NAME == 'master') {
echo 'Building for master branch'
} else {
echo 'Building for feature branch'
}
}
}
- 并行执行:Scripted Pipeline 可以通过
parallel块并行执行多个任务。
node {
stage('Test') {
parallel(
unitTests: {
echo 'Running unit tests...'
},
integrationTests: {
echo 'Running integration tests...'
}
)
}
}
3.4.4 Pipeline 的优势
- 版本控制:Pipeline 脚本可以存储在代码库中,与项目代码一同版本控制,确保构建和部署流程的一致性。
- 可移植性:通过定义 Pipeline,开发者可以轻松地将 CI/CD 流程从一个 Jenkins 实例迁移到另一个实例,甚至可以在本地调试 Pipeline。
- 可视化:Jenkins 提供了直观的 Pipeline 可视化界面,可以实时监控每个阶段的执行情况。
3.4.5 如何开始使用 Pipeline
[!tip]
- Declarative Pipeline:是新手的首选,因为它提供了更多的结构化引导和错误检查。
- Scripted Pipeline:适合需要复杂控制流和逻辑的高级用户。
下载Pipeline插件

安装插件后,创建项目的时候多了“流水线”类型

编写一个简单声明式Pipeline

编译看看


[!note]
这里如果没有编译视图,需要安装插件:
Pipeline Stage View:
插件安装好了之后,再回去看看:

可见编译视图就有了,而且名称都是在声明式Pipeline脚本中写的名称。
之前的Pipeline语法是我们自己手动写的,在Jenkins中,其实也是可以自动生成目标Pipeline语句的:

目标步骤选择:




构建一下试试:

3.4.6 Pipeline Script from SCM
[!tip]
SCM 是 Source Code Management(源代码管理)的缩写,是用于管理软件项目源代码的系统或工具。SCM 系统的主要功能包括代码的版本控制、变更管理、分支和合并、协作开发、历史记录追踪等。例如我们常说的GitLab、GitHub、Gitee等拥有Git功能的都是SCM。
刚才我们都是直接在Jenkins的UI界面编写Pipeline代码,这样不方便脚本维护,建议把Pipeline脚本放在项目中(一起进行版本控制)
在项目根目录建立Jenkinsfile文件,把内容复制到该文件中

把Jenkinsfile上传到Gitlab
在项目中引用该文件


构建测试成功。
3.5 Jenkins常用的构建触发器
Jenkins常见的构建触发器有如下几种:
Build after other projects are built(在其他项目构建后触发):
当某个项目构建完成后,自动触发当前项目的构建。可以用来设置项目之间的依赖关系,例如,当A项目完成后触发B项目构建。
Build periodically(定时构建):
通过定时任务在特定的时间间隔触发构建。
使用Cron表达式来设定构建时间。定时字符串从左往右分别为: 分 时 日 月 周;例如,
H/15 * * * *表示每15分钟触发一次构建,这里的H其实是一个变量,例如现在是10:16分,则下一次构建就是10:31分,并不是整点构建的,当然,如果你想整点构建,则H就用0代替。常用于周期性任务,例如每晚定时触发构建,进行夜间测试。
[!note]
一些定时表达式的例子:
- 每30分钟构建一次:H代表形参 H/30 * * * * 10:02 10:32
- 每2个小时构建一次: H H/2 * * *
- 每天的8点,12点,22点,一天构建3次: (多个时间点中间用逗号隔开) 0 8,12,22 * * *
- 每天中午12点定时构建一次 H 12 * * *
- 每天下午18点定时构建一次 H 18 * * *
- 在每个小时的前半个小时内的每10分钟 H(0-29)/10 * * * *
- 每两小时一次,每个工作日上午9点到下午5点(也许是上午10:38,下午12:38,下午2:38,下午4:38) H H(9-16)/2 * * 1-5
Poll SCM(轮询SCM):
[!caution]
注意:这种构建触发器,Jenkins会定时扫描本地整个项目的代码,增大系统的开销,不建议使用。
定期轮询代码库(如Git、SVN),检查是否有代码变化,如果有变化,则触发构建。同样使用Cron表达式设置轮询的频率,例如每5分钟检查一次代码库是否有新提交。注意轮询并不会每次都触发构建,只有当检测到代码发生变化时才会触发。
Quiet period(静默期):
触发构建后,会等待一段设定的“静默期”后再开始构建。静默期可以防止频繁的构建触发,例如在持续提交的过程中减少不必要的构建次数。可以在项目配置中设置静默期的时长,单位是秒。
[!caution]
这里的触发构建需要排除手动触发构建这种情况。
**Trigger builds remotely (e.g., with scripts)**(触发远程构建 (例如, 使用脚本)):
通过HTTP请求来远程触发构建任务。常用于外部系统或脚本触发构建。
需要设置一个触发令牌(token),并通过
Jenkins_URL/job/项目名/build?token=TOKEN_NAME来触发。常用于与其他系统集成,例如GitLab、GitHub或自定义脚本触发构建。
Git hook自动触发构建
[!tip]
这种方式较为推荐。
当代码托管在GitLab、GitHub、Gitee等平台上时,可以配置Webhooks来触发Jenkins构建。每当有代码提交到仓库时,对应平台会通知Jenkins触发构建。
这里需要安装插件,这里我使用的是GitLab,所以需要安装一个GitLab的插件

3.5.1 Build after other projects are built(在其他项目构建后触发)
这种构建,会在前一个项目构建后才会触发,所以这里我们这里新建一个前置项目:pre-demo

现在我们先手动构建一下pre-demo,然后回来看看pipeline-demo是否触发自动构建:

测试成功。
3.5.2 Build periodically(定时构建)
这相当于是一种定时任务,通过写cron表达式来触发构建,现在我们测试每2分钟触发构建:


可见确实是如期构建的。
3.5.3 轮询SCM(Poll SCM)

现在我们改一下代码,然后上传Git,看看他是否会构建:

我代码上传的时间是11:34,由于我轮询SCM的时间设置的是每3分钟,所以下一次自动触发构建大概时间就在11:37,我们看看:

可见确实是这个时间。
3.5.4 静默期(Quiet period)
触发构建后,会等待一段设定的“静默期”后再开始构建。

现在我在54分的时候手动构建了一下pre-demo

此时在返回去看pipeline-demo,可见确实还没构建,而是要等静默期过了,再触发构建。

[!caution]
注意,静默期构建手动触发以及与定时构建想结合起来是没有用的,仍然会在手动触发构建的时候构建,以及到达定时构建配置的时候定时触发构建。
3.5.5 触发远程构建 (例如, 使用脚本)(Trigger builds remotely (e.g., with scripts))

触发远程构建是通过HTTP请求来触发的,所以,现在我们访问:JENKINS_URL/job/pipeline-demo/build?token=token,然后回来看看是否触发了自动构建:


测试成功。
3.5.6 Git hook自动触发构建
刚才我们看到在Jenkins的内置构建触发器中,轮询SCM可以实现Gitlab代码更新,项目自动构建,但是该方案的性能不佳。那有没有更好的方案呢? 有的。就是利用Gitlab的webhook实现代码push到仓库,立即触发项目自动构建。


之后来到GitLab中设置:
[!tip]
如下GitLab的设置需要管理员权限。
来到Admin area –>Settings–>Network–>Outbound requests

之后来到你需要集成的项目中:


点击测试:

如果出现这个报错,表示没有权限,这里其实是Jenkins服务阻止了请求,去Jenkins中设置修改一下:

再次点击测试查看:


可见自动构建成功。
3.6 Jenkins的参数化构建
在Jenkins中,”This project is parameterized” 是一个非常有用的选项,允许你为构建任务定义参数化的构建。这意味着在构建开始之前,用户可以提供一些输入值,这些值会作为构建过程中的参数使用。这使得构建任务更加灵活,可以根据不同的输入条件进行不同的操作。
参数化构建的作用:
灵活构建:通过参数化,用户可以在每次构建时提供不同的输入,控制构建流程或输出的内容。比如,你可以让用户选择不同的环境(开发、测试、生产)来部署应用,或选择不同的分支进行构建。
减少项目数量:通过参数化,可以避免为每种构建场景创建单独的项目。一个项目可以通过不同的参数值来执行不同的构建任务。
提高可重用性:参数化使得构建脚本和流程可以在多个场景中重复使用,提高了项目的可维护性和可扩展性。

常见的参数类型:
- String Parameter:允许用户输入一个字符串,通常用作文件名、版本号等。
- Boolean Parameter:提供一个布尔值选择框(勾选或不勾选),可用于控制构建中的某些开关。
- Choice Parameter:提供一组选项供用户选择,常用于选择环境或部署策略。
- File Parameter:允许用户上传一个文件,构建过程中可以使用该文件。
- Run Parameter:允许用户选择某个已执行的构建结果作为参数。
- Password Parameter:用于输入密码,密码会被隐藏。
- Multi-line String Parameter:允许用户输入多行字符串,常用于输入复杂的配置或脚本。
在构建脚本(如Shell脚本、批处理命令、Groovy脚本)中,你可以通过 ${PARAMETER_NAME} 这样的方式引用参数值。
例如,如果你定义了一个名为 ENV 的字符串参数,你可以在Shell脚本中使用 echo "Deploying to ${ENV} environment"。
接下来演示通过输入GitLab项目的分支名称来部署不同分支项目。
创建新分支dev:

通过参数来构建不同的分支

对于流水线的类型,如果你是寻找的SCM的方式,如下指定分支需要更改:


改动项目中的Pipeline脚本

这里的参数名称要与在Jenkins中配置的参数名称一致。
选择不同分支编译构建

测试构建

还有一种也能够选择不同分支的构建方式:
安装插件

选择Git参数

配置Git参数

流水线定义

这样才能够正常运行。
3.7 Jenkins邮件整合
[!tip]
这里暂时不准备讲解,因为我们公司目前是用的企业微信通知,所以这里我只讲解企业微信。
3.9 Jenkins+SonarQube代码审查
3.9.1 安装SonarQube
SonarQube是一个用于管理代码质量的开放平台,可以快速的定位代码中潜在的或者明显的错误。目前支持java、C#、C/C++、Python、PL/SQL、Cobol、JavaScrip、Groovy等二十几种编程语言的代码质量管理与检测。
环境要求:

我这里选择安装10.6的版本,这里面有一些环境的要求需要提前阅读一下。
Java版本需求:

数据库版本需求:

这里后面我将使用PG数据库。
[!tip]
数据库以及JDK的安装这里就不展开讲解了。
PG数据库安装教程可以参考:https://juejin.cn/post/7108728363472617486
我这里已经有PostgreSQL数据库了,所以我们直接使用PostgreSQL,创建一个数据库:sonar

之后参照官网说明:

在之前创建了sonar数据库的基础上,继续执行如下命令:
#切换数据库
\c sonar
create schema sonar;
CREATE USER sonarqube WITH PASSWORD 'sonar';
GRANT USAGE ON SCHEMA sonar TO sonarqube;
GRANT CREATE, CONNECT, TEMPORARY ON DATABASE sonar TO sonarqube;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA sonar TO sonarqube;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA sonar TO sonarqube;
ALTER DEFAULT PRIVILEGES IN SCHEMA sonar GRANT ALL ON TABLES TO sonarqube;
ALTER DEFAULT PRIVILEGES IN SCHEMA sonar GRANT ALL ON SEQUENCES TO sonarqube;
GRANT ALL PRIVILEGES ON SCHEMA sonar TO sonarqube;
之后下载sonar压缩包,准备安装sonar。
sonar下载地址:https://www.sonarsource.com/products/sonarqube/downloads/
这里有多个版本可以选择,我们直接选择社区版,他是免费的:

这里下载的是一个ZIP的压缩包,需要将下载好的文件上传到Linux中,然后使用解压文件:
unzip sonarqube-10.6.0.92116.zip
现在来修改配置:



这里我由于我使用的是9998端口,如果有防火墙,需要放行这个端口:
firewall-cmd --zone=public --add-port=9998/tcp --permanent
firewall-cmd --reload
从这里开始,就是配置的重点了,很多坑:
你必须使用创建一个sonar用户来启动sonar,否则启动会报错
useradd sonar更改sonar相关文件夹的权限,这里直接更改用户组和所属组,即可
chown -R sonar sonar安装目录启动命令

sh sonar.sh start #启动 sh sonar.sh status #查看状态 sh sonar.sh stop #停止如果启动过程中报错
如果启动过程中报错,可以先看看
sonar.log这个日志文件,然后结合es.log来看,看是不是es启动失败,导致sonar启动失败。比如如下sonar.log报错:

此时可知是ES报错了,我们再来看看es.log:

从上诉报错可知,不能使用root用户来启动sonar。
再比如如下sonar.log报错:

此时又是ES报错了,我们再来看看es.log:

可见是ES需要的配置比较高,当前服务器的配置不能满足,所以我们查看官网的解决办法:

也就是以root用户执行如下命令:
sysctl -w vm.max_map_count=524288 sysctl -w fs.file-max=131072 ulimit -n 131072 ulimit -u 8192然后切换到非系统用户,比如我这里的sonar,执行查看:
sysctl vm.max_map_count sysctl fs.file-max ulimit -n ulimit -u这个就能够解决ES配置不足的问题。
最后再比如如下sonar.log报错:

此时也是ES报错了,我们再来看看es.log,再看了一圈之后,也没有发现ERROR级别的错误,这时候,我发现logs目录下多了几个日志文件,其中比较重要的是:
web.log,他表示以及再执行web相关的配置了,我们再来看看这个日志:
可见报错了,再往下找找,找到根本的报错原因:

看到这个基本上能够断定你的数据库配置相关有问题了,当时我是只创建了sonar数据库,没有创建对应的模式,所以这里报错了。
再比如如下报错:

表示你新建的数据库角色对于数据库这个模式的权限不够,我这里执行的权限参考我本小节的数据库配置那段,基本上不会出现问题。
这是我目前搭建sonar发现的坑,后续发现可再补充。
现在访问IP:端口即可使用SonarQube了:

[!tip]
初始化账号密码均为admin。
3.9.2 Jenkins中SonarQube基本配置
Jenkins使用SonarQube步骤如下:
- Jenkins调用SonarScanner
- SonarScanner提交审查结果到SonarQube
- SonarQube保存审查结果到数据库
从上面步骤可知,我们需要一个SonarScanner,这个其实不用特别去安装,只需要在Jenkins中安装插件即可:

插件安装好了之后,我们需要在来到凭证管理中添加一个SonarQube凭证,这个凭证需要在SonarQube创建:

[!important]
这个凭证需要保存下来,他只显示一次,后面就不显示了。sqa_949de1c87d2d472e48cd4fc05cee93a8a12d5ad5

之后来到系统配置配置SonarQube:

之后来到工具配置中配置SonarQube Scanner:

之后回到SonarQube中关闭审查结果上传SCM功能:

3.9.3 在项目添加SonaQube代码审查(非流水线项目)
先创建一个非流水线的项目:sonar-porject


配置文件:
#项目的唯一标识,不能重复
sonar.projectKey=test-project
#项目在SonarQube上的显示名称
sonar.projectName=test-project
#版本,这有助于跟踪不同版本的代码质量
sonar.projectVersion=1.0
#扫描哪个项目的源码
sonar.source=.
#排除那些文件扫描
sonar.exclusions=**/test/**,**/target/**
#设置为 UTF-8,以确保正确解析和分析代码文件的内容。
sonar.sourceEncoding=UTF-8
# 指定编译后的 .class 文件的路径
sonar.java.binaries=target/classes
之后构建你会看见如下内容:

最终构建成功了,去SonarQube上看看:

3.9.4 在项目添加SonaQube代码审查(流水线项目)
在之前的项目中添加一个sonar相关的配置文件:
#项目的唯一标识,不能重复
sonar.projectKey=test-project
#项目在SonarQube上的显示名称
sonar.projectName=test-project
#版本,这有助于跟踪不同版本的代码质量
sonar.projectVersion=1.0
#扫描哪个项目的源码
sonar.source=.
#排除那些文件扫描
sonar.exclusions=**/test/**,**/target/**
#设置为 UTF-8,以确保正确解析和分析代码文件的内容。
sonar.sourceEncoding=UTF-8
# 指定编译后的 .class 文件的路径
sonar.java.binaries=target/classes

之后修改Jenkins文件,加入代码审查的脚本:
pipeline {
agent any
stages {
stage('pull code') {
steps {
checkout scmGit(branches: [[name: '*/${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: '7c2082d3-6825-48ce-81a1-04941585a40c', url: 'http://192.168.3.46:82/test-group/test-project.git']])
}
}
stage('SonarQube代码审查') {
steps{
script {
scannerHome = tool 'sonarqube-scanner'
}
withSonarQubeEnv('SonarQube10.6') {
sh "${scannerHome}/bin/sonar-scanner"
}
}
}
stage('master分支编译构建') {
steps {
sh 'mvn clean package -Dmaven.test.skip=true'
}
}
stage('project deploy') {
steps {
echo 'master项目部署'
}
}
}
}
这里有几个注意点:
SonarQube代码审查一般放在拉取代码之后,部署之前,强烈推荐放在maven编译构建之后。
在script代码块中,执行了语句:
tool 'sonarqube-scanner',其含义是 Jenkins 中配置的一个工具,它返回 SonarQube Scanner 的安装路径,并将路径存储在 scannerHome 变量中。这里tool 后面要的名称要与之前在Jenkins中tool配置的一致:

在script之后的语句中:
withSonarQubeEnv('SonarQube10.6'),这个命令是在 SonarQube 的环境中执行接下来的脚本。withSonarQubeEnv接受一个参数,这个参数是 SonarQube 服务器的名字,你在 Jenkins 中配置的 SonarQube 服务器名为:
之后执行脚本:
sh "${scannerHome}/bin/sonar-scanner",通过 Shell 执行 SonarQube Scanner 命令。${scannerHome}/bin/sonar-scanner指向 SonarQube Scanner 的可执行文件路径,并使用该路径运行代码审查。
这些都准备好了之后,就可以去Jenkins创建一个流水线项目然后构建了。
4. Jenkins+Docker+SpringCloud微服务持续集成
Jenkins+Docker+SpringCloud持续集成大致流程说明:
- 开发人员每天把代码提交到Gitlab代码仓库
- Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后构建成Docker镜像,将镜像上传到Harbor私有仓库。
- Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器。
- 最后,用户可以访问到容器
[!tip]
这里需要额外安装Docker以及Harbor,Docker以及Harbor的安装就不多讲了,可以去看我往期Docker专栏(https://blog.cqwulyj.cn/posts/bd770737.html)。

[!note]
默认账户:admin/你harbor.yml配置的密码。
Harbor的项目分为公开和私有的:
- 公开项目:所有用户都可以访问,通常存放公共的镜像,默认有一个library公开项目。
- 私有项目:只有授权用户才可以访问,通常存放项目本身的镜像。
我们可以为微服务项目创建一个新的项目:



权限说明:
以下是 Harbor 项目成员的权限表格:
| 角色/权限 | 创建项目 | 删除项目 | 拉取镜像 | 推送镜像 | 删除镜像 | 管理成员 | 管理Webhook | 查看日志 |
|---|---|---|---|---|---|---|---|---|
| 项目管理员 | 是 | 是 | 是 | 是 | 是 | 是 | 是 | 是 |
| 开发者 | 否 | 否 | 是 | 是 | 是 | 否 | 否 | 是 |
| 访客 | 否 | 否 | 是 | 否 | 否 | 否 | 否 | 是 |
| 受限访客 | 否 | 否 | 是 | 否 | 否 | 否 | 否 | 否 |
表格中展示了 Harbor 中不同角色的项目权限分配情况,具体权限如下:
- 创建项目:是否有权限创建新项目。
- 删除项目:是否有权限删除现有项目。
- 拉取镜像:是否有权限从项目中拉取镜像。
- 推送镜像:是否有权限向项目中推送镜像。
- 删除镜像:是否有权限删除项目中的镜像。
- 管理成员:是否有权限管理项目成员。
- 管理Webhook:是否有权限管理项目的 Webhook 配置。
- 查看日志:是否有权限查看项目的操作日志。
之后用新创建的用户登陆。
将微服务的前后端代码上传GitLab:

4.1 Jenkins从GitLab上拉取源码
在Jenkins上新建一个pipeline项目:

在微服务的根目录下创建Jenkinsfile文件
pipeline {
agent any
stages {
stage('pull code') {
steps {
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: '7c2082d3-6825-48ce-81a1-04941585a40c', url: 'http://192.168.3.50:82/test-springcloud/test-springcloud.git']])
}
}
}
}


4.2 微服务提交到SonarQube审查
为每一个真正能启动的微服务添加sonar-project.properties文件:
[!tip]
这里的真正能启动的意思是有启动类的微服务,那些公共模块等没有启动类的微服务无需添加。
sonar-project.properties文件模版:
#项目的唯一标识,不能重复
sonar.projectKey=
#项目在SonarQube上的显示名称
sonar.projectName=
#版本,这有助于跟踪不同版本的代码质量
sonar.projectVersion=1.0
#扫描哪个项目的源码
sonar.source=.
#排除那些文件扫描
sonar.exclusions=**/test/**,**/target/**
#设置为 UTF-8,以确保正确解析和分析代码文件的内容。
sonar.sourceEncoding=UTF-8
# 指定编译后的 .class 文件的路径
sonar.java.binaries=target/classes

之后,返回Jenkins项目中进行修改:

[!important]
可见我这里加了一个参数化配置,为什么呢?因为SonarQube的审查执行只能从sonar-project.properties所在的目录运行sonar- scanner;
如果你不这样做,就会出现如下报错:
并且你仔细观察我的参数配置,有部分是这样的:
这是因为我的项目结构如下:
根据自己的项目结构动态调整即可。
修改Jenkinsfile脚本文件:
pipeline {
agent any
stages {
stage('pull code') {
steps {
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: '7c2082d3-6825-48ce-81a1-04941585a40c', url: 'http://192.168.3.50:82/test-springcloud/test-springcloud.git']])
}
}
stage('master分支编译构建') {
steps {
sh 'mvn clean package -Dmaven.test.skip=true'
}
}
stage('SonarQube代码审查') {
steps{
script {
scannerHome = tool 'sonarqube-scanner'
}
withSonarQubeEnv('SonarQube10.6') {
sh """
cd ${project_module}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
}
}
[!important]
同理,因为SonarQube的审查执行只能从sonar-project.properties所在的目录运行sonar- scanner;所以,这里的脚本和之前略有不同:
之后我们来简单构建一下各个模块:


可见基本上都是通过了的。
4.3 使用Dockerfile编译生成镜像
4.3.1 Dockerfile文件提供
在Docker中,应用都是部署在容器中的,而容器又由镜像生成,镜像则通常是通过Dockerfile文件构建的,所以微服务与Docker整合的第一步就是要提供Dockerfile文件。
之前我们说了项目的整体结构:参考4.1小节,我们针对每一个真正能启动的微服务的根目录下提供Dockerfile文件:
#FROM java:8
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 23121
ENTRYPOINT ["java","-jar","/app.jar"]
文件解释:
#FROM java:8
这一行被注释掉了,表示它不会被执行。最初可能打算使用 java:8 作为基础镜像,但后来改用了 openjdk:8-jdk-alpine。
FROM openjdk:8-jdk-alpine
这行指令指定了基础镜像。openjdk:8-jdk-alpine 是一个基于 Alpine Linux 的 OpenJDK 8 镜像。Alpine 是一个非常轻量级的 Linux 发行版,所以这个镜像比较小,适合用来运行 Java 应用。
ARG JAR_FILE
这行定义了一个构建时的参数 JAR_FILE,它可以在 docker build 命令中通过
--build-arg JAR_FILE=your-jar-file.jar来传递。在构建时,这个参数会被替换为你指定的值。COPY ${JAR_FILE} app.jar
这行指令将构建上下文中的
${JAR_FILE}文件复制到镜像的/app.jar中。${JAR_FILE}会被替换为你在构建时指定的 JAR 文件的名称。EXPOSE 23121
这行指令声明容器将暴露的端口 23121。这个端口通常是应用程序监听的端口。需要注意的是,EXPOSE 只是在 Dockerfile 中标记端口,而不会实际地将它暴露给外部网络。实际访问需要在运行容器时使用 -p 选项映射端口。
[!caution]
这里不同的微服务的端口要不同。
ENTRYPOINT [“java”,”-jar”,”/app.jar”]
这行定义了容器启动时的入口命令。当容器启动时,
java -jar /app.jar会被执行,用来运行app.jar文件。
[!tip]
具体的Dockerfile文件解读,可以参考我往期Docker的专栏:https://blog.cqwulyj.cn/posts/55d16a1a.html#
例如:

4.3.2 dockerfile-maven-plugin插件提供
在每个真正能够启动的微服务项目的pom.xml加入dockerfile-maven-plugin插件:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<repository>${project.artifactId}</repository>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
[!tip]
这里的如图:
4.3.3 修改Jenkinsfile构建脚本
添加构建步骤:
stage('编译,构建镜像') {
steps {
script {
// 编译并安装公共模块
sh """
mvn clean install -pl personal-blog-commons,personal-blog-model,personal_blog-feign -am -Dmaven.test.skip=true
"""
// 编译目标模块并构建 Docker 镜像
sh """
mvn -f ${project_module}/pom.xml clean package -Dmaven.test.skip=true dockerfile:build
"""
}
}
}
完整如下:
pipeline {
agent any
stages {
stage('pull code') {
steps {
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: '7c2082d3-6825-48ce-81a1-04941585a40c', url: 'http://192.168.3.50:82/test-springcloud/test-springcloud.git']])
}
}
stage('master分支编译构建') {
steps {
sh 'mvn clean package -Dmaven.test.skip=true'
}
}
stage('SonarQube代码审查') {
steps{
script {
scannerHome = tool 'sonarqube-scanner'
}
withSonarQubeEnv('SonarQube10.6') {
sh """
cd ${project_module}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
stage('编译,构建镜像') {
steps {
script {
// 编译并安装公共模块
sh """
mvn clean install -pl personal-blog-commons,personal-blog-model,personal_blog-feign -am -Dmaven.test.skip=true
"""
// 编译目标模块并构建 Docker 镜像
sh """
mvn -f ${project_module}/pom.xml clean package -Dmaven.test.skip
=true dockerfile:build
"""
}
}
}
}
}
这里的编译,构建镜像这步我简单解释一下:
因为微服务有些模块没有启动类,相当于作为一种工具和公共服务的,但是其他有启动类的微服务又是必须依赖这些工具或公共微服务的,所以,在构建真正的微服务之前,需要先将工具或公共等没有启动类的微服务给使用maven来编译打包为一个jar包,所以上部分为先做的编译并安装公共模块:
mvn clean install -pl personal-blog-commons,personal-blog-model,personal_blog-feign -am -Dmaven.test.skip=true-pl:参数用于指定要构建的模块或项目列表(以逗号分隔)。这适用于多模块项目中的情况。
-am:当你使用 -pl(–projects)参数指定了一个或多个模块时,-am 参数会确保这些模块所依赖的其他模块也会一并被构建。这在多模块项目中尤其有用,可以避免手动逐个编译依赖模块的麻烦。
举个例子:
假设你有一个多模块项目,结构如下:
parent-project │ ├── module-a │ └── pom.xml ├── module-b (depends on module-a) │ └── pom.xml └── module-c (depends on module-b) └── pom.xml如果你运行以下命令:
mvn clean install -pl module-c -ammaven 会执行以下操作:
首先编译 module-a,因为 module-b 依赖于它。
然后编译 module-b,因为 module-c 依赖于它。
最后编译你指定的 module-c 模块。
这保证了 module-c 在构建时所有依赖模块都已经编译并安装到本地仓库了。
简单来说就是:**-am 参数确保在构建某个模块时,Maven 会同时构建它所依赖的其他模块,从而避免由于未构建依赖模块而导致的构建失败问题。**
在保证公共模块编译打包为jar包之后,我们就要针对每一个微服务进行docker的镜像构建了,不够在构建之前,也是需要将这个微服务打包为jar包的,所以前面先执行了:
mvn -f ${project_module}/pom.xml clean package -Dmaven.test.skip=true其中mvn 命令中的 -f 参数用于指定一个特定的 POM 文件 (pom.xml) 来执行构建,而不是默认的当前目录下的 pom.xml 文件。这个参数的完整形式是 –file。
使用场景:**当你需要从一个非当前目录的 pom.xml 文件进行构建时,可以使用 -f 参数。 **
例如:
mvn -f /path/to/your/pom.xml clean install之后使用命令:
dockerfile:build这是一个 Maven 插件目标:
- 这里的 dockerfile:build 指的是使用 Maven 的 Dockerfile 插件来构建 Docker 镜像。
- dockerfile 是插件的 artifactId,build 是这个插件的目标(goal)。执行这个目标时,Maven 会根据项目目录中的 Dockerfile 构建 Docker 镜像。
在提供了Dockerfile–>dockerfile-maven-plugin–>Jenkins脚本修改之后,我们去Jenkins分别构建打包:

我们的微服务镜像其实就已经构建好了:

4.4 上传镜像到Harbor仓库
现在我们修改Jenkins脚本:
//定义一些全局变量
def HarborUrl = "192.168.3.50:80" //Harbor服务器的地址,跟上端口
def HarborProjectName = "personal-blog" //Harbor服务上的项目地址
def tag = "latest" //镜像的版本
pipeline {
agent any
stages {
//之前的阶段省略
stage('上传镜像到Harbor'){
steps {
script {
def imageName = "${project_module.split('/').last()}:${tag}"
def fullImageName = "${HarborUrl}/${HarborProjectName}/${imageName}"
sh """
set -e # 当命令失败时,脚本将立即退出
# 给镜像打标签
docker tag ${imageName} ${fullImageName}
# 登录Harbor并且上传镜像
echo "Logging into Harbor..."
echo Harbor123456 | docker login -u nxz --password-stdin ${HarborUrl}
echo "Pushing image to Harbor..."
docker push ${fullImageName}
# 删除本地镜像
echo "Removing local images..."
docker rmi -f ${imageName} || true # 忽略镜像不存在的错误
docker rmi -f ${fullImageName} || true # 忽略镜像不存在的错误
# 清理本地的悬空镜像(none)
echo "Removing dangling images..."
docker rmi \$(docker images -f "dangling=true" -q) || true # 忽略没有悬空镜像的错误
"""
}
}
}
}
}
这里有个细节:

因为我的参数project_module有些是多层级的:

但是我的docker构建镜像的时候,不是多层级的,所以,如果这里我不这样处理:project_module.split('/').last(),那么这里在打标签的时候就会出错。
[!note]
总结下来就是这里要根据自己的情况来定,不同的项目,不同的构建方式,这里都会导致不同。
同时,这里登陆Harbor可能会报错:

这是因为Docker没有把Harbor加入受信列表中,只需要编辑文件:/etc/docker/daemon.json,将Harbor服务器的地址加入其中即可:

之后重启Docker服务即可:
systemctl daemon-reload
systemctl restart docker
之后再次使用Jenkins构建,最后我这里是构建成功,查看Harbor仓库:

可见镜像也都上传成功了。
4.5 拉取镜像以及发布应用
[!tip]
有些公司的生产部署服务器和Jenkins服务器不是一个服务器,是分开的,并且部署项目是用的shell脚本来部署的,此时脚本存在于生产服务器,与Jenkins服务器没有在一个服务器,所以,此时就需要Jenkins有远程执行shell脚本的功能,此时需要安装插件:
后续的配置自己可以在网上查询资料。因为我这里是一个服务器,所以我不用远程执行shell。
当前服务器上的镜像:

现在编写部署脚本:
#接收外部参数
harbor_url=$1
harbor_project_name=$2
project_module=$3
tag=$4
port=$5
imageName=$harbor_url/$harbor_project_name/$project_module:$tag
echo "接收到到参数:harbor_url = $harbor_url ; harbor_project_name = $harbor_project_name ; project_module = $project_module ; tag = $tag ; port = $port ; imageName = $imageName"
#查询容器是否存在,存在则删除
containerId=`docker ps -a | grep -w ${project_module}:${tag} | awk '{print $1}'`
if [ "$containerId" != "" ] ; then
#停掉容器
docker stop $containerId
#删除容器
docker rm $containerId
echo "成功删除容器"
fi
#查询镜像是否存在,存在则删除
imageId=`docker images | grep -w $project_module | awk '{print $3}'`
if [ "$imageId" != "" ] ; then
#删除镜像
docker rmi -f $imageId
echo "成功删除镜像"
fi
# 登录Harbor私服
docker login -u nxz -p Harbor123456 $harbor_url
# 下载镜像
docker pull $imageName
# 启动容器
docker run -di -p $port:$port $imageName
echo "容器启动成功"
修改Jenkins文件:
def HarborUrl = "192.168.3.50:80"
def HarborProjectName = "personal-blog"
def tag = "latest"
pipeline {
agent any
stages {
//其他阶段省略
stage('项目部署'){
steps {
script {
sh """
sh deploy.sh ${HarborUrl}
${HarborProjectName} ${project_module.split('/').last()} ${tag} ${port}
"""
}
}
}
}
}

注意:我的部署脚本和Jenkins文件都在项目的根目录下,所以才有上图的执行,如果目录不一样,脚本的执行也是需要自己更改的。

又由于是微服务项目,相当于不同的微服务启动是需要不同的端口的,同时我们上部分也是用的port变量,所以,这里我们要去Jenkins也要配置port参数:


构建之后:

可见确实是先拉取的镜像,也可以去Harbor上看下载次数有没有新增:

我们的脚本中还包括运行镜像的命令,看看这个微服务是否启动了:

可见没有启动起来,我们输入命令:
docker logs 镜像ID

可见确实是去启动了,只是nacos没有启动,导致程序报错,所以,只是我们的流程是跑通了,这些报错自行解决即可。
4.6 部署前端Web网站
[!tip]
部署前端,首先需要nginx,关于nginx的安装,可以查看我的niginx专栏:https://blog.cqwulyj.cn/posts/34e2af00.html#
看见如下界面就代表nginx安装成功了:

在Jenkins安装NodeJS插件,因为前端项目的包都是靠NodeJS来管理的:

之后去Jenkins的工具配置中配置NodeJS:

[!caution]
这里我选择的NodeJS的版本为22,有可能你都服务器的环境并不支持这么高的版本。
例如可能会报如下错误:
错误参考:https://blog.csdn.net/qq_38225558/article/details/128641561
不过最稳当的方法还是降低NodeJS版本。
之后前端也去创建一个流水线项目:springcloud-web-demo:

[!tip]
剩下的内容基本上就是k8s相关的了,这里暂时不讨论k8s。