Jenkins+Docker+Gitlab+Maven搭建持续集成环境

前言

在应用还处于单体架构的时候,交付周期通常都以周、月为单位,在这种情况下项目的部署一般都是由开发人员手动将程序代码打成一个大的war包然后通过SSH工具拖拽到服务器上,执行shell脚本进行部署,整个过程虽然缺乏一个完善、自动化的流程,但是基本可以满足企业的需求,然而随着业务的发展,用户需求的个性化,原先的单体应用逐步演进为微服务的架构,项目面临着的大量构建和部署工作,运维成本较高,我们希望通过docker等容器化技术加快项目的迭代并借助于jenkins的持续集成,可以快速把应用打包成docker镜像,实现自动部署,来降低运维的成本.

持续集成

持续集成(Continuous Integration简称CI)是利用一系列的工具、方法与规则,做到快速的构建代码,并自动的进行测试,从而提高代码的效率和质量.
image.png大致的步骤如下:

  • 开发人员通过IDE工具将代码推送到gitlab.
  • jenkins从gitlab中获取到源码,并使用maven编译、打包、自动构建镜像.
  • jenkins在构建脚本中调用docker命令将构建好的镜像push到本地Docker Registry.并启动相应的容器.
  • jenkins构建失败或者成功,可以及时将结果推送给相关人员,比如测试人员,安排测试.
  • 运维人员只需获取相应的镜像就可以快速发布到生产环境.

环境搭建

本地开发环境为windows环境,开发工具为IntelliJ IDEA.
1.本地下载并安装配置git客户端Git-2.14.1-64-bit
2.在虚拟机上搭建gitlab服务
拉取gitlab镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@bogon /]# docker pull gitlab/gitlab-ce
Using default tag: latest
latest: Pulling from gitlab/gitlab-ce
8ee29e426c26: Pull complete
6e83b260b73b: Pull complete
e26b65fd1143: Pull complete
40dca07f8222: Pull complete
b420ae9e10b3: Pull complete
a218309dd589: Pull complete
5c60fd7ba0ce: Pull complete
659c2144b5a3: Pull complete
8289bbac0d0e: Pull complete
31bbd150e8a7: Pull complete
9114e78243fa: Pull complete
Digest: sha256:3fcabe86e077db438240b214048a1561c6b89c259dbffdaa191f8f3d3119b4cb
Status: Downloaded newer image for gitlab/gitlab-ce:latest

启动gitlab容器

1
2
3
4
5
6
7
8
9
10
[root@bogon /]# docker run --detach \
> --hostname gitlab.william.com \
> --publish 8443:443 --publish 48080:80 --publish 8022:22 \
> --name gitlab \
> --restart always \
> --volume /opt/gitlab/config:/etc/gitlab \
> --volume /opt/gitlab/logs:/var/log/gitlab \
> --volume /opt/gitlab/data:/var/opt/gitlab \
> gitlab/gitlab-ce:latest
e19375a8f2b2a76e6dae2ecd5e7b0d5864c7a2e9975bfc4274cb166cd5091b7b

注意上面的docker run命令指定了域名为gitlab.william.com,必须通过该域名才能访问gitlab,所以在gitlab容器启动前要先在虚拟机和本地配置ip与域名的映射关系.

1
2
3
4
#在linux下:
vi /etc/hosts
#在windows下:
cd C:\Windows\System32\drivers\etc\hosts

修改host文件在最下面添加

1
192.168.56.101 gitlab.william.com

另外我们指定了三个端口,22表示ssh端口,80表示http端口,443表示https端口,分别映射到宿主机上的8022、48080和8443端口,我们还通过–volume指定目录映射,其中

  • /etc/gitlab表示gitlab的配置目录,映射到宿主机的/opt/gitlab/config目录.
  • /var/log/gitlab表示gitlab的日志目录,映射到宿主机的/opt/gitlab/logs目录.
  • /var/opt/gitlab表示gitlab的数据目录,映射到宿主机的/opt/gitlab/data目录.

启动gitlab容器后打开浏览器输入地址http://gitlab.william.com:48080发现访问不了
先查看48080端口是否开启

1
2
[root@bogon /]# firewall-cmd --zone=public --query-port=48080/tcp
no

很明显需要开启48080端口

1
2
3
4
[root@bogon /]# firewall-cmd --zone=public --add-port=48080/tcp --permanent
success
[root@bogon /]# firewall-cmd --reload
success

再次打开浏览器输入地址http://gitlab.william.com:48080后重定向进入修改管理员密码页面,我们将密码修改为12345678.
修改完密码后接着进入管理登录页面,输入登录用户名密码root/12345678登录进去.
image.png接下来新建一个空的项目hello,其中hello为项目名称,可设置可见性级别:

  • Private表示私有项目,授予具体权限的用户才能访问.
  • Internal表示内部项目,可登陆的用户都能克隆项目.
  • Public表示公开项目,没有任何权限的用户都能克隆项目.

项目创建完毕之后,开始进入主页面:image.png接着我们需要做的就是将本地java代码推送至gitlab中.
在推送之前先全局设置git

1
2
[root@bogon /]# git config --global user.name "william.zhang"
[root@bogon /]# git config --global user.email "952408421@qq.com"

3.使用IntelliJ IDEA 将项目发布(提交)到GitLab
首先需要启用项目的版本管理功能,点击VCS 启用版本控制.
image.png然后右击项目,选择Git->add,将项目中的文件加入到git add中等待本地提交,add 后的文件都会由橙色变成绿色.
下一步本地提交代码,右击项目选择Git->Commit Directory…这时本地提交成功,可以看见文件颜色从绿色变成了黑色.
最后就是将代码push到远程git仓库了,右击项目 Git->Repository ->Push
image.png在Define Remote窗口里面的URL中填入http://gitlab.william.com:48080/root/hello.git
点击ok按钮弹出下面窗口
image.png输入用户名密码:root/12345678
然后开始push…
image.pngpush成功之后刷新http://gitlab.william.com:48080/root/hello可以看到本地代码已经推送至gitlab了.
image.png
4.安装jenkins
拉取jenkins镜像

1
2
3
[root@bogon ~]# docker pull jenkinsci/jenkins
Using default tag: latest
latest: Pulling from jenkinsci/jenkins

然后开启38080端口,并启动jenkins容器.

1
2
3
4
5
6
7
8
9
10
[root@bogon ~]# docker run \
> -d \
> -p 38080:8080 \
> -p 50000:50000 \
> --name jenkins \
> --link gitlab:gitlab.william.com \
> -u root \
> -v /opt/jenkins:/var/jenkins_home \
> jenkinsci/jenkins:latest
f7fa42d4e010833da5d9e5258855ac53d7d1c4855549e12992ceac236f3d583a

其中8080端口是jenkins的端口,38080是映射宿主机的端口,50000端口是master和slave通信端口.以root用户来启动容器,同时通过配置–link连接gitlab,因为要与gitlab容器通讯下载代码.
打开浏览器http://192.168.56.101:38080/访问跳转到解锁jenkins页面.
image.png在服务器上以下执行命令查看密码

1
2
[root@bogon ~]# cat /var/jenkins_home/secrets/initialAdminPassword
cat: /var/jenkins_home/secrets/initialAdminPassword: 没有那个文件或目录

由于之前启动jenkins容器时我们做了目录映射 -v /opt/jenkins:/var/jenkins_home,所以要将执行命令改为:

1
2
3
[root@bogon ~]# cat /opt/jenkins/secrets/initialAdminPassword
aa42541900b146f59007de7c0cdc4d3a
[root@bogon ~]#

在解锁页面上输入上述密码,继续进入以下页面:image.png建议选择安装推荐的插件,基本上一些常用的插件都会被安装,安装过程如图:image.png安装完后进入以下页面:
image.png单击开始使用Jenkins按钮进入jenkins系统,点击左侧菜单中的系统管理进入Jenkins管理模块,查看系统设置:

  • 主目录是存放Jenkins所有的文件的,工作空间根目录和构建记录目录默认都是在Jenkins主目录下,这个设置一般不用进行变更.image.png
  • 执行者数量:可以并发构建的数量.
    标记:用来记录这个机器的名称(为了分配节点使用,后面子节点会详细介绍).
    用法:设置这个节点的执行策略(包括尽可能使用这个节点和只允许绑定到这台机器的job.
    生成前等待时间:这个时间为构建开始前的等待时间.
    scm签出重试次数:使用svn或者git拉取代码失败重试的次数.
    Restrict project naming:限制项目命名,勾选后可以看到具体设置,可以设置为默认或者使用正则表达式进行限制.image.png
  • 全局属性
    environment variables:设置全局变量,在这里定义的全局变量可以在构建或者发送邮件时引用.
    tool locations:设置全局工具,可以把需要的工具都在这里进行配置,比如maven,ant,jdk等.image.png
  • 设置时间格式image.png
  • 管理监控默认选中所有image.png
  • jenkins location
    jenkins url :设置jenkins的url(发送邮件引用jenkins的地址会取这个值,如果设错了,邮件的连接就会打不开)
    系统管理员邮件地址:管理员的邮件地址(在构建需要发送邮件时,会用到这个邮件地址)
    image.png

接着进入插件管理,选择可选插件板块,安装并配置maven插件,如果缺少Maven Integration Plugin插件,在创建job时,不会有新建一个maven项目选项.
image.pngimage.png接下来使用jenkins创建一个构建任务.
image.png输入项目名称hello,选中构建一个maven项目点击确定按钮进入项目配置页面:
image.png在源码管理板块中我们选择git并在repository url填写git仓库名称,但此时会报错:
image.png从报错提示中发现jenkins访问gitlab.william.com时出现连接拒绝,可以断定是由于jenkins容器与gitlab容器无法通讯导致的.
解决方法是在启动jenkins容器时添加–link选项,并将其指定到需要连接的gitlab容器,重启jenkins容器后重新填写仓库URL出现另一个错误:
image.png通过错误提示可知:我们所填入的URL是需要身份认证的,可单击Credentials下拉框右侧的Add按钮,并选择Jenkins选项,弹出身份认证信息对话框:
image.png上面填写的是gitlab的用户名密码验证,添加完后需要选中它,这个时候可以看到已经不报错了.
image.png紧接着我们在build板块中配置maven:
image.png在Goals and options使用clean package -q可以加快maven构建速度.
添加构建后操作
image.png点击增加构建后操作步骤,选择归档成品,在文本框中输入需要存档的文件路径:**/target/*.jar.
单击保存回到主界面
image.png单击左侧菜单的立即构建并在控制台输出中查看构建日志:
image.png由日志可看出构建过程报错了提示找不到pom文件,根据提示回到build板块中修改maven配置Root POM为hello/pom.xml,再重新手工构建即可.
构建完成后,回到jenkins主界面可以看到构建任务列表
image.png小球图标表示构建状态,蓝色表示构建成功,但我们的目的是搭建一个持续集成的环境,在开发阶段我们会不断的推送代码到gitlab,所以我们需要利用jenkins来帮我们实现自动构建发布.
在Post step板块中填写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
#定义变量
API_NAME="hello"
API_VERSION="1.0.0"
API_PORT=58080
IMAGE_NAME="192.168.56.101:5000/hello/$API_NAME:$BUILD_NUMBER"
CONTAINER_NAME=$API_NAME-$API_VERSION

#进入target目录复制Dockerfile文件
cd $WORKSPACE/$API_NAME/target
cp classes/Dockerfile .

#构建docker镜像
docker build -t $IMAGE_NAME .

#推送docker镜像
docker push $IMAGE_NAME

#删除docker容器
cid=$(docker ps | grep "$CONTAINER_NAME" | awk '{print $1}')
if [ "$cid" != "" ]; then
docker rm -f $cid
fi

#启动docker容器
docker run -d -p $API_PORT:8080 --name $CONTAINER_NAME $IMAGE_NAME

#删除Dockerfile文件
rm -f Dockerfile

再次点击立即构建按钮image.png出现构建失败错误docker not found,说明jenkins容器中无法执行docker命令,网上搜了一下,主要解决方案有以下几种:
1.不使用Jenkins镜像,直接在宿主机上安装Jenkins服务,可以调用宿主机上的docker命令.
2.使用dood方案:表示在docker容器中使用宿主机上的docker服务.
3.使用dind方案:在docker镜像中要再安装docker服务,此时在容器中的docker和宿主机的docker是两个不同的程序,相互没有关联.
4.使用https与docker后台程序通讯,通过https暴露socket,并且可以使用宿主的镜像,但因为打开了端口增加了攻击面,可以说是最不安全的.
下面我们使用第2种方案即dood来解决这个问题.
1.先删掉之前建好的jenkins容器和镜像,然后在虚拟机上创建一个空的Dockerfile文件.

1
[root@bogon jenkins-dood]# touch Dockerfile

2.编写Dockerfile脚本

1
[root@bogon jenkins-dood]# vi Dockerfile

1
2
3
4
5
6
7
FROM jenkins:latest
USER root
ARG dockerGid=999

RUN echo "docker:x:${dockerGid}:jenkins" >> /etc/group

USER jenkins

3.重新构建jenkins镜像

1
[root@bogon jenkins-dood]# docker build -t jenkins .

4.在启动Jenkins容器时,我们需要先创建一个Jenkins的配置目录,并且挂载到docker 里的Jenkins目录下.

1
[root@bogon /]# mkdir -p /opt/jenkins-dood

5.修改目录权限.

1
[root@bogon /]# chown -R 1000 /opt/jenkins-dood

6.开始运行jenkins容器

1
2
3
4
5
6
7
8
9
10
11
12
[root@bogon /]# docker run \
> -d \
> -p 38080:8080 \
> -p 50000:50000 \
> --name jenkins \
> -v /var/run/docker.sock:/var/run/docker.sock \
> -v $(which docker):/bin/docker \
> -v /opt/jenkins-dood:/var/jenkins_home \
> -u root \
> --link gitlab:gitlab.william.com \
> jenkins:latest
a5904b8c7f50234b52c3772ab13cca4d61f02cb887e0524ff4f6cf53bb63b501

注意这两个-v参数(将jenkins容器内的docker命令指向了宿主机):
-v /var/run/docker.sock:/var/run/docker.sock
-v $(which docker):/bin/docker
之后直接在jenkins里面就可以使用docker命令了.同样的启动jenkins容器后找回管理员密码:

1
2
 cat /opt/jenkins-dood/secrets/initialAdminPassword
d1291723c28848cda642d8ba8351e439

进去系统后先修改jenkins时间设置:打开【系统管理】->【脚本命令行】运行下面的命令:

1
System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone', 'Asia/Shanghai')

其他的配置步骤跟我们之前配置的一样.编写完自动部署脚本之后,我们同样执行立即构建,这个时候报错了:image.png根据错误日志提示可知:找不到libltdl.so.7这个文件,网上搜了一下,在jenkins容器启动命令增加文件映射-v /usr/lib/x86_64-linux-gnu/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7.重新构建,这次报另一个错误:image.png原因是centos7上没有装libltdl.so.7这个library,我们通过下面这个命令来安装:

1
[root@bogon /]# yum install libltdl.so.7

安装完之后找到安装的位置:/usr/lib64/libltdl.so.7,接着再重启jenkins容器:

1
2
3
4
5
6
7
8
9
10
11
12
[root@bogon /]# docker run \
> -d \
> -p 38080:8080 \
> -p 50000:50000 \
> --name jenkins \
> -v /var/run/docker.sock:/var/run/docker.sock \
> -v $(which docker):/bin/docker \
> -v /opt/jenkins-dood:/var/jenkins_home \
> -v /usr/lib64/libltdl.so.7:/usr/lib/x86_64-linux-gnu/libltdl.so.7
> -u root \
> --link gitlab:gitlab.william.com \
> jenkins:latest

再次执行jenkins构建,这回构建成功了,执行docker images查看一下jenkins自动部署的镜像.

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@bogon bin]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.56.101:5000/hello/hello 9 231daf17317c 18 hours ago 673MB
192.168.56.101:5000/hello/hello 7 c9b2c721e7e1 18 hours ago 673MB
192.168.56.101:5000/hello/hello 6 29b676b8d273 19 hours ago 673MB
192.168.56.101:5000/hello/hello 5 f1bdfbf7977e 20 hours ago 673MB
jenkins latest cfa26ddfdfaa 24 hours ago 696MB
gitlab/gitlab-ce latest d87e1ba8aa5f 7 days ago 1.46GB
192.168.56.101:5000/hello/hello-api 1.0.0 68bbcea2575f 7 days ago 673MB
nginx latest c82521676580 13 days ago 109MB
jenkins <none> cd14cecfdb3a 2 weeks ago 696MB
registry latest b2b03e9146e1 4 weeks ago 33.3MB
java 8 d23bdf5b1b1b 18 months ago 643MB

测试jenkins自动发布的容器是否正常运行

1
2
[root@bogon bin]# curl http://192.168.56.101:58080/hello/hi
hi[root@bogon bin]#

返回结果为”hi”,说明jenkins已经可以正常从gitlab中获取源码来构建生成docker镜像并以docker容器的方式进行发布,供生产环境使用了.但目前还是以手动的方式来执行构建任务,我们真正需要的是一款自动化的发布平台,在开发人员将源代码推送至gitlab后自动触发jenkins构建任务,再次打开构建配置,回到构建触发器配置:image.png选中Poll SCM,Poll SCM表示定时检查源码变更(根据SCM软件的版本号),如果有更新就checkout最新code下来,然后执行构建动作.如果需要每隔30分钟检查一次源码变化,有变化就执行则可以在日程表中输入一个基于CRON表达式,比如:H/30 * * * *表示每30分钟执行一次构建.我们来测试一下,在idea中修改测试代码将原来的返回值”hi”改为”hello world”,然后push到gitlab中,观察jenkins是否会自动构建.image.png从上图中我们可以看到jenkins已经开始自动构建了.
最后再来验证下自动化发布的结果.

1
2
[root@bogon bin]# curl http://192.168.56.101:58080/hello/hi
hello world[root@bogon bin]#

返回结果为”hello world”说明jenkins已经成功自动构建并部署了.

参考资料:
https://blog.csdn.net/bingoxubin/article/details/78720976
https://www.cnblogs.com/leolztang/p/6934694.html
https://www.cnblogs.com/stulzq/p/8627360.html
http://www.dockone.io/article/431
https://blog.csdn.net/kikajack/article/details/79806520
https://www.cnblogs.com/caoj/p/7815820.html