Jenkins是一款开源自动化服务器,旨在自动化连续集成和交付(CICD)软件所涉及的重复技术任务。在Jenkins入门教程-2:Ubuntu下Jenkins安装指南及详细配置中我们已经搭建好了Jenkins服务,本文在此基础上实现使用 jenkins 服务器控制 dev环境 和 线上release环境来完成CICD的流程演示。
1、CICD流程
1.1 传统打包部署模式
在实际开发中,我们经常要一边开发一边测试,这里的测试是指同组程序员将代码提交后,由测试人员测试(而不是程序员对自己代码的单元测试);或者前后端分离后,经常会修改接口,然后重新部署;这些情况都会涉及到频繁的打包部署;
手动打包常规步骤:
- 1.个人提交代码到代码仓库,如gitee/github/svn
- 2.问一下同组小伙伴有没有要提交的代码
- 3.拉取代码并打包(war包,或者jar包)
- 4.上传部署包到Linux服务器
- 5.查看当前程序是否在运行
- 6.关闭当前程序
- 7.启动新的jar包
- 8.观察日志看是否启动成功
- 9.如果有小伙伴的代码还没有提交…重复1到8的步骤!(半天时间没了)
那么,有没有一种工具能够实现将代码提交到git后就自动打包部署,答案是肯定的:那就是Jenkins。
1.2 持续集成自动部署流程
CICD的步骤为:自动编译、打包、构建镜像、容器部署等,流程图如下:
具体步骤:开发人员 push 代码到 gitee/github 仓库,触发 Jenkins 的自动构建流程(是由Jenkins编排文件设置的),这时候 Jenkins 会自动 pull 代码、进行 maven编译、打包,并通过自动化执行shell脚本使 docker构建镜像并 push 到私服(或者阿里云)仓库,此操作完成后 Jenkins 服务器上再执行SSH命令登录到部署服务器,docker 从仓库(私服)拉取镜像,启动容器。整个操作流程完成。
2、服务器环境及基础配置
2.1 需准备的服务器
生产环境下,Jenkins服务器和测试服务器位于不同的服务器,由于条件所限,我们采用同一台服务器。
jenkins服务器: 172.19.82.206
测试服务器: 172.19.82.206
这里的Jenkins服务器使用我们上个教程搭建的环境。需要确认下基础配置是否完成。
2.2 Jenkins安装插件
本例中我们使用的代码仓库为gitee。所以插件也需要安装 gitee 相关插件。
在 系统管理 -> 插件管理
中,查看已安装插件,确认 Jenkins 是否安装了 Gitee Plugin
、Publish Over SSH
这2个插件,如果没有在 可选插件
中搜索对应插件并安装。
Gitee Plugin
插件通过伪造一个 Gitee CI 服务器将Gitee集成到 Jenkins。此插件允许 Gitee 在提交代码或打开/更新拉取请求时触发 Jenkins 中的构建。它还可以将构建状态发送回 Gitee。Publish Over SSH
插件通过 SSH 发送构建工件,即通过SSH发送部署文件到远程机器上。SSH Plugin
插件使用 SSH 协议远程执行 shell 命令。Mask Passwords
此插件允许屏蔽可能出现在控制台中的密码。该插件还提供了一个非存储密码参数。
2.3 Jenkins添加gitee凭据
在 系统管理 -> 凭据管理(Manage Credentials)
中,查看 凭据
列表,如果没有添加过你的gitee凭据,可以点击下方全局凭据右侧的小箭头,会弹出 添加凭据
的菜单,点击进入添加凭据页面。
在添加凭据页面,类型选择 Username with password
, 范围选择 全局
, 填入你的gitee的账号和密码,然后点击 Create
创建即可。 如下图所示。
添加完的凭据如图所示
3、Jenkins服务器上创建项目和构建配置
新建任务
点击左侧新建任务,输入你的任务名称,如 demo
,选择构建自由风格的项目。
接着会跳转至Jenkins项目配置区。
大体步骤:General(基础配置)–》源码管理–》构建触发器–》构建环境–》构建–》构建后操作
3.1 配置 general 项
该配置项下的所有配置都可以不用配置,本例中我们不做任何配置,下面对各配置项做简要说明。
描述:就是注释,给我们这个job一个更便于其他查看的详解
GitHub 项目:里面配置响应的url和需要显示的名称就可以了
Gitee 链接:用于 Gitee Pull Request Webhook 触发的构建结果评论到的 Pull Request中,以及自动合并 Pull Request 等。
This build requires lockable resources:此构建需要可锁定的资源
Throttle builds:节流构建,通过设置时间段内允许并发的次数来实现构建的控制
丢弃旧的构建:设置构建历史的保存策略。
- log Rotation 日志循环
- 保持构建的天数 意思就是根据你所填写的天数来保存构建记录
- 保持构建的最大个数 意思就是有几条构建记录就保存几条
- 发布包保留天数 例如我们发布的war包等的保存天数
- 发布包最大保留#个构建 例如我们发布了几个war包,就保存几个
参数化构建过程:参数允许您提示用户输入一个或多个将传递到构建中的输入。例如,您可能有一个项目通过允许用户上传包含要测试的二进制文件的 zip 文件来按需运行测试。这可以通过在此处添加文件参数来完成。
关闭构建:选中此选项后,将不会执行此项目的新构建。当您想暂时阻止构建项目时,这会很有帮助。
在必要的时候并发构建:选中此选项时,可以并行执行该项目的多个构建。默认情况下,一次只执行一个项目的构建——任何其他开始构建该项目的请求都将保留在构建队列中,直到第一个构建完成。这是一个安全的默认设置,因为项目通常需要独占访问某些资源,例如数据库或硬件。
静默期:如果设置此选项,一个计划中的构建在开始之前需要等待选项中设置的秒数。
重试次数:拉取源码重试的次数
该项目的上游项目正在构建时阻止该项目构建:用于上游项目有关联的构建策略
该项目的下游项目正在构建时阻止该项目构建:用于下游项目有关联的构建策略
使用自定义的工作空间:使该项目独立于系统的工作空间,默认jenkins的工作空间是在 .jenkins/workspace/task
中,可自定义工作空间。
显示名称:如果设置,则在整个 Jenkins Web GUI 中显示项目的可选显示名称。由于它仅用于显示目的,因此显示名称在项目中不需要是唯一的。如果未设置显示名称,Jenkins Web GUI 将默认显示项目名称。
保留构建的依赖日志:如果启用此选项,则从该项目 的构建中引用的所有构建(通过指纹)都将受到保护免受日志轮换。
3.2 配置 源码管理 项
选择源码管理项,Git选项,输入你Git仓库的地址,然后在Credentials处选择你之前添加过的gitee凭据,你也可以直接在这里添加你Git仓库的用户名和密码;并且选择监听master分支(默认就是)。
3.3 配置 构建触发器 项
我们需要的效果是一旦git仓库发生变化就要自动构建镜像,并且部署新的镜像容器,所以在构建触发器项下选择轮询SCM,使用corn表达式控制Jenkins监听git仓库的频率为每分钟一次。
- 当您输入 “* * * * *” 时,意思为每分钟轮询一次, “H * * * *” 意思为每小时轮询一次
其他工程构建后触发:在其他项目构建完成后再进行构建。这里又分4种情况:
- 只有构建稳定时触发
- 即使构建不稳定时也会触发
- 即使构建失败时也会触发
- 始终触发,即使构建被中止
定时构建:隔一段时间build一次,不管版本库代码是否发生变化,通常不会采用此种方式。例如: 0 2 * * * (每天2:00 必须build一次源码)
轮询 SCM(Poll SCM):隔一段时间比较一次源代码如果发生变更,那么就build。否则,不进行build,通常采用这种方式; Jenkins会定时检查源码变更(根据SCM软件的版本号),如果有更新就checkout最新code下来,然后执行构建动作。配置如下: */5 * * * *
(每5分钟检查一次源码变化,如果有更新才build)
在 Schedule中填入* * * * *
,代表一分钟监听一次
- 第一个参数代表的是分钟 minute,取值 0~59;
- 第二个参数代表的是小时 hour,取值 0~23;
- 第三个参数代表的是天 day,取值 1~31;
- 第四个参数代表的是月 month,取值 1~12;
- 最后一个参数代表的是星期 week,取值 0~7,0 和 7 都是表示星期天。
所以 0 * * * *
表示的就是每个小时的第 0 分钟执行一次构建。
五个*
依次标识 分钟
、小时
、每月几号
、哪个月
、周几
。
例:
H/15 * * * *
每15分钟H(0-29)/10 * * * *
每小时的0到29分钟每10分钟H 2-19/2 * * 1-5
每周1到周五(工作日)2点到19点每2小时执行H H 1,15 1-11 *
1到11月1号和15号各执行一次
3.4 配置 构建环境 项
选中 Mask passwords and regexes (and enable global passwords)
,在 Regular Expressions
下方点击 新增
按钮,然后在 Regex
下方的输入框中写入正则表达式 (?<=--password).*(?=\s)
。
(?<=)
表示断言以某某开始,即以--password
开始(?=)
表示断言以某某结束,\s
即以空格结束- 两个组合的效果就是,匹配出中间的部分,
.*
即--password
和空格中间的任意字符,也就是密码。
配置界面截图:
构建后控制台输出信息截图,如果不配置这个,密码将会以明文形式显示。
3.5 配置 构建 项
3.5.1 Jenkins服务器构建镜像并上传至镜像仓库
1)maven打包
方式一:调用顶层Maven目标方式构建
进入 构建
配置项, 选择 增加构建步骤
右侧的下拉箭头,选择 调用顶层Maven目标
。
在展开的对话框中,选择 Maven版本
并填写目标指令 clean install
(也可以填写为clean package
)。
该步骤配置完成后,保存配置,我们可以先执行 立即构建
,测试下配置是否正确。构建过程中,可以点击左侧构建历史列表中的当前构建记录进入构建详情页面,点击左侧控制台输出可以看到构建的执行情况。
在页面最下方我们可以看到个构建失败的原因,The goal you specified requires a project to execute but there is no POM in this directory (/opt/jenkins/workspace/demo). Please verify you invoked Maven from the correct directory. -> [Help 1]
, 这是因为在构建目录下没有pom文件,我这里使用的demo项目只是一个文件夹,test工程目录是demo的子目录(test目录下才有pom文件),所以构建失败了。
方式二:执行shell方式构建
这里我们换个方式,通过 执行shell
的方式来构建。删除掉当前配置(调用顶层 Maven 目标
右侧的 X
按钮),选择 增加构建步骤
右侧的下拉箭头,选择 执行shell
。在展开的shell窗口中,填写构建脚本如下。
# 因为需要构建的test项目是demo的子目录,所以先要cd到test目录
cd /opt/jenkins/workspace/demo/test
mvn clean install
保存配置后我们再次执行 立即构建
,测试下配置是否正确。
可以看到构建成功,通过 Build History下方的记录链接
进入到构建详情页面,点击详情页面左侧的 控制台输出
可以进入查看构建的详细过程。
2)执行shell方式构建镜像
选择 增加构建步骤
右侧的下拉箭头,选择 执行shell
。在展开的shell窗口中,填写构建脚本如下。(如果上一步骤已经创建了shell脚本,可以直接加到之前的shell脚本后面)
- 因为要执行docker命令,所以我们的Jenkins服务器需要安装docker环境。
echo "显示当前目录"
pwd
echo "显示当前目录下的文件目录"
ls -l
# Dockerfile文件所在的目录为/opt/jenkins/workspace/demo/test/deploy
docker build -t kongzid/java:test1.0.0 -f ./deploy/Dockerfile .
其中前4行代码都是打印了一些提示信息,方便我们查看执行情况,完全可以去掉。
保存配置后我们再次执行 立即构建
,测试下配置是否正确。
可以看到,docker镜像已经构建成功。登录Jenkins服务器使用命令 docker images
可以查看到生成的docker镜像 java/test
。
Dockerfile文件存放在与src同级的deploy目录下。
附上Dockerfile文件的内容:
# 该镜像需要依赖的基础镜像
FROM java:8
# 维护者信息
MAINTAINER kongzi<153957433@qq.com>
# 将targer目录下的jar包复制到docker容器/home/jar目录下面
COPY ./target/*.jar /home/jar/test.jar
# 声明服务运行在8080端口
EXPOSE 8080
# 执行命令
CMD ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/home/jar/test.jar"]
3)将构建好的镜像推送到Docker Hub的Registry
选择 增加构建步骤
右侧的下拉箭头,选择 执行shell
。在展开的shell窗口中,填写构建脚本如下。(也可以加在上一步构建镜像shell脚本的后面)
# docker hub 登录
docker login --username=用户名 --password=密码
# 上传镜像到DockerHub仓库
docker push kongzid/java:test1.0.0
# 删除本地镜像
docker image rm kongzid/java:test1.0.0
# 删除本地因构建失败而生成的名称为none的镜像,这句脚本可能会失败,慎用
# docker images|grep none|awk '{print $3}' | xargs docker rmi
保存配置后我们再次执行 立即构建
,测试下配置是否正确。查看构建结果,可以看到控制台最终输出Finished: SUCCESS
, 构建状态也显示为绿色对勾。再登录DockerHub仓库也可以看到新上传的镜像。
3.5.2 从docker hub仓库下载镜像到目标服务器部署
Jenkins通过 SSH plugin 远程在目标服务器上(本示例中为测试服务器)执行 shell 命令,进行镜像的下载和容器的运行。
1)配置SSH凭证
Jenkins中打开系统管理->Manage Credentials
,点击Stores scoped to Jenkins
下方的全局域后面的三角箭头,在弹出的下拉框中点击 添加凭据
。
在新建凭据页面,选择 Username with password
类型,填入所要连接服务器的用户名和密码,输入该凭据的唯一标识id(可不填写,会自动生成,填写的这个值方便选择时候识别),填写描述信息(可为空)。
2)配置SSH
Jenkins中打开系统管理——>系统配置
,下拉找到SSH remote hosts
区域。点击 SSH sites 下方的新增
按钮,配置选项如图所示:
配置说明:
- Hostname:为linux服务器的ip地址;因为我们的jenkins服务器和测试服务器是一台机器,所以填写127.0.0.1。
- Port:连接端口,默认22即可;
- Credentials:凭证,即为前面步骤(配置SSH凭证)所加的凭据;
- serverAliveInterval:所需的 serverAliveInterval 以毫秒为单位(0 = 关闭)。
- timeout:等待连接的超时时间(以毫秒为单位)(0 = 默认超时),此处写5000。
配置完成后点击 Check connection
按钮,左侧显示 Successfull connection
即为连接成功;最后点页面下方的 保存
按钮完成配置。
3)测试服务器下载镜像并部署
在项目的构建部分,选择 增加构建步骤
右侧的下拉箭头,选择 Excute shell script on remote host using ssh
。在展开的对话框中,SSH site下会自动填入已经创建好的SSH配置信息 root@127.0.0.1:22
, 在 Command
命令行里填写我们要执行的shell命令。下面我们可以先填入一个测试命令 echo "test" > /root/hello.txt
,该命令的作用是在远程主机上root目录下创建1个 hello.txt
文件,内容为test
。
- 该插件的作用是在远程主机上执行 shell 命令。
保存配置后,我们点击页面左侧的 立即构建
按钮,当构建成功后,登录到远程主机上查看,/root/hello.txt
的文件内容和我们的预期是否一致。
测试成功,Jenkins中配置的shell命令在远程主机上正确的执行了。现在我们可以配置docker镜像的下载及安装部署了。
现在我们改下上述配置中的Command
命令行,把之前写的测试命令 echo "test" > /root/hello.txt
删掉,填入以下的命令(拉取镜像并启动容器):
# 拉取镜像
docker pull kongzid/java:test1.0.0
# 根据容器名称停止容器
docker stop test
# 根据容器名称删除容器
docker rm test
# 运行镜像,假设java项目端口为8081
docker run -p 8083:8080 -d --name test kongzid/java:test1.0.0
保存配置后我们再次执行 立即构建
,查看构建结果,可以看到控制台最终输出Finished: SUCCESS
, 构建状态也显示为绿色对勾。
更改代码后重新构建
这一步骤的作用是验证我们的CICD构建流程,和之前的操作完全一致,这里只简单叙述一下步骤:
本地更改代码 -> 提交到gitee仓库 -> giteee上合并代码到master分支 -> 登录Jenkins -> 找到对应的项目demo执行立即构建 -> 查看构建结果是否正确。
从图中可以看到,修改结果已经被部署到测试服务器。
4、Jenkins配置 构建后操作 项
本例中, 构建后操作
主要包括以下几点:
- 发送打包文件到目标服务器
- 重新启动服务
推荐的方式是将镜像上传到私服或公共仓库,目标服务器(本例中指测试服务器)再通过docker仓库下载镜像进行部署。上述步骤已经实现了将镜像推送到Docker Hub的Registry。还有一种方式就是接下来要介绍的:通过ssh publish发送打包文件(本例是jar包)到目标服务器。
- 建议使用前面方式,即通过镜像上传到私服或公共仓库,再通过测试服务器直接下载使用。
ssh publish发送打包文件到目标服务器并启动服务
此方式是将jar包通过ssh发送到测试部署服务器,然后通过java命令启动或tomcat部署,不需要前面的构建镜像步骤。
4.1 生成Jenkins的私钥
要先在jenkins所在的机器上生成秘钥。生成方式为:
ssh-keygen -t rsa #产生公钥与私钥对
在shell窗口中输入命令后回车,当提示Enter passphrase (empty for no passphrase)
时输入密码 111111
,然后会提示Enter same passphrase again:(意思再次输入确认密码)
, 再次输入确认密码 111111
。回车直到生成了秘钥文件。此时在 用户名文件夹/.ssh/
文件夹下生成两个文件:
is_rsa
为密钥文件id_rsa.pub
为公钥文件
查看所生成私钥的格式:
root@xnzysq20210118001:~/.ssh# more /root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCqnCPf7t
UqxH4YccGGWXb4AAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQDMfGtHN0UI
......
可以看到密钥的首行是:
-----BEGIN OPENSSH PRIVATE KEY——
生成密钥的openssh的版本过高,而jenkins现有的版本在检验密钥时还不支持这种格式(可以通过 cat /opt/jenkins/config.xml
命令查看Jenkins版本,可以在登录Jenkins首页的右下角看到版本号)。所以我们需要指定生成秘钥的格式:
ssh-keygen -m PEM -t rsa -b 4096
- -m 参数指定密钥的格式,PEM是rsa之前使用的旧格式
- -b 指定密钥长度。对于RSA密钥,最小要求768位,默认是2048位。
按上述语句生成秘钥后,查看所生成私钥的格式:
root@xnzysq20210118001:~/.ssh# more /root/.ssh/id_rsa
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,533BE3103BDFEAD081AB3C36FAA93078
cQnurtw7ITuz9X/f7fOhO2LWdrrJy5AN0aIXh53hNOExvCDKH4xcdR2JCv8Dlhbm
EoRWEyiiXu6vYh+ASnwpVqOaoE7RvK7cT3hxbe3yVGcROKwtru19OpG1dvjclqF3
aogS0XwEL7PNAFvHXHKn2nQgBJ8nTthtT64F2X/pgFxipRNkkfm7oS1Af5h+3QTw
......
可以看到密钥的首行是:
-----BEGIN RSA PRIVATE KEY-----
这样改动后可以通过jenkins对密钥格式的验证。
4.2 将公钥文件复制到目标服务器
通过以下语句将公钥文件复制到目标服务器,因为我们的Jenkins和测试机是一台服务器,所以语句最后的ip填写为127.0.0.1:
- ssh-copy-id命令可以把本地主机的公钥复制到远程主机的authorized_keys文件上,ssh-copy-id命令也会给远程主机的用户主目录(home)和
/.ssh
, 和/.ssh/authorized_keys
设置合适的权限。
ssh-copy-id -i /root/.ssh/id_rsa.pub root@127.0.0.1
执行此命令后会提示是否确认和目标机器建立连接,输入 yes
后回车,接着会提示你输入目标服务器的密码,输入之后回车,若密码正确就会建立连接。
4.3 配置Publish Over SSH
Jenkins中打开系统管理——>系统配置
,下拉到底部的Publish over SSH
区域。配置选项如下:
- Passphrase:填写生成秘钥时的密码,如果生成秘钥没有加密码,直接enter到底生成的,则这里不用填写。我们这里填写
111111
。- Path to key:填写私钥的路径,没尝试,因为在下面填写了私钥key。
- Key:私钥的值。要将前后的
-----BEGIN RSA PRIVATE KEY-----
和-----END RSA PRIVATE KEY-----
带着。- 点击SSH Servers的“增加”,新添目标服务器。
- Name:你起的可以分辨服务器的名称。
- Hostname:目标服务器的ip,一般服务器之间交互的话用内网ip就行。因为我这里jenkins服务器和所部署的服务器位于同一台服务器中,故写的是127.0.0.1
- Username:登录目标服务器的账号,需要写通过
ssh-copy-id
命令建立连接所用的账号。- Remote Directory:将jenkins构建的文件发送到目标服务器的哪个文件夹下。
配置完成后,点击右下角的 测试配置
按钮,在左下角如果提示 成功
,表示配置正确。最后点保存按钮完成配置。
4.4 发送打包文件到目标服务器并启动服务
配置 构建后操作 项,点击 增加构建后操作步骤
右侧的下拉箭头,选择 Send build artifacts over SSH
, 进行通过SSH发送部署包的相关配置。
在 Send build artifacts over SSH
配置页面,主要包括以下配置参数:
- SSH Server Name: 会默认选中前面配置的SSH服务器
test-server
。- Transfers:这里输入的是你希望传到服务器的文件夹和文件,路径是相对jenkins的workspace的项目名称的,这里我们写
target/*.jar
。- Remove prefix:如果Source files为
target/*.jar
,但是不想把target目录创建到服务器端,这个时候就可以在Remove prefix里填入target
。- Remote directory: 这里指定文件将被拷贝到服务器的路径,注意,被拷贝文件在服务器上的完整路径=登录服务器账号的默认路径+Remote directory,这里填写
/usr/local/demo
。- Exec command: 启动命令,执行脚本
cd /usr/local/demo
java -jar test.jar --server.port=8083
评论区