Gitlab CI 与 Docker 的配置与整合流程

什么是Gitlab CI

  Gitlab CI是一个持续集成的工具,目前已经与Gitlab完美整合在一起。它提供一个虚拟的代码测试、打包的环境,这个环境与Gitlab Repo勾连在一起,让每一次的push或者merge request触发这个虚拟环境的创建,自动运行测试与打包的脚本,得出脚本的运行结果呈现在Gitlab 的web页面上,然后再销毁这个虚拟的环境。并且,它能与我们的真正的产品部署环境连接起来,让我们能在Gitlab的web页面上看到部署环境的一些结果,让整个代码的生产线变得完整。通过Gitlab CI与Gitlab的整合,源代码开发、自动测试、自动打包、自动部署成为一条流水线,能极大地加快产品的迭代速度。

Gitlab Runner 与 Docker

  Gitlab Runner是一个为我们创建上述Gitlab CI虚拟环境的一个程序,我们选择把这个程序所需的环境用Docker来安装,作为一个Docker Daemon Container来配置好Gitlab Runner运行的环境。每一个Gitlab project都可以注册一个或多个runner,这些runner就是负责跑我们上述的虚拟环境的,它们的注册,创建以及运行都是由Gitlab Runner这个程序来控制的。这些runner实际上都是docker container,由Gitlab Runner这个程序自动创建,它们与Gitlab Runner所处的container是同等关系而不是从属关系。使用docker来建立runner,使得每一个虚拟环境都干净、轻量,相互隔离,互不影响。

  下面我主要讲一下在服务器上安装docker,通过docker安装Gitlab Runner,以及通过Gitlab Runner注册runner的步骤。

安装与配置

Docker

  第一步先要在服务器上把Docker装起来,服务器环境是Centos,安装流程非常简单,完全参照Docker官网即可:https://docs.docker.com/engine/installation/linux/centos/#/install-docker-engine

  这样安装好以后,每次运行docker指令都要加sudo,十分不方便,可以把用户加到docker group里面,便可以省去sudo了,参考:https://docs.docker.com/engine/installation/linux/centos/#/create-a-docker-group

Gitlab Runner的安装与注册

安装

  我们用docker来配置一个container给Gitlab Runner程序运行。安装指令如下:

1
2
3
4
docker run -d --name gitlab-runner --add-host gitlab.local:10.106.128.234 --restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest

  上述指令主要是根据gitlab/gitlab-runner这个docker镜像运行一个docker container,这个镜像里安装好了gitlab-runner这个程序来为我们创建测试用的虚拟环境。我们把这个container用–name参数命名为gitlab-runner。-v指令代表挂载,我们将host服务器主机的/var/run/docker.sock和/srv/gitlab-runner/config这两个文件或文件夹与gitlab-runner这个container内部的/var/run/docker.sock和/etc/gitlab-runner这两个文件或文件夹挂载在一起。/var/run/docker.sock是用来创建docker container的,都过挂载host的/var/run/docker.sock,使得gitlab-runner container可以利用host服务器的docker来创建container,而不需要使用docker-in-docker。/etc/gitlab-runner/里面则有一个config.toml文件,我们通过这个文件来设置一些runner的参数,挂载在host的/srv/gitlab-runner/config里使得我们可以通过修改host主机的这个文件来设置runner的参数。

  还有一个比较重要的参数是–add-host。这个参数在Gitlab CI官网上是找不到的,但对于我们是必须的。因为gitlab.local是一个内网的gitlab服务器,不能通过公共的DNS来查找到,这个参数会把gitlab.local加到gitlab-runner container的host文件里。这个是docker提供的一个参数,当然我想如果host服务器的host文件里如果有gitlab.local的信息,通过上面说的挂载host主机上的/etc/hosts的方法也可以实现我们的需求。所以,如果gitlab服务器建在内网(我觉得是大多数情况),都需要通过这种方法解决。

注册

  下面是为我们需要测试的Gitlab项目注册runner:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker exec -it gitlab-runner gitlab-runner register
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
https://gitlab.com
Please enter the gitlab-ci token for this runner
xxx
Please enter the gitlab-ci description for this runner
my-runner
INFO[0034] fcf5c619 Registering runner... succeeded
Please enter the executor: shell, docker, docker-ssh, ssh?
docker
Please enter the Docker image (eg. ruby:2.1):
ruby:2.1
INFO[0037] Runner registered successfully. Feel free to start it, but if it's
running already the config should be automatically reloaded!

  其中url填写gitlab服务器的域名,gitlab-ci token是每个project特有的,填写你需要测试的project的token即可。关于runner的更详尽的信息可以参照:https://docs.gitlab.com/ce/ci/runners/README.html

  注册成功以后,我们还需要修改一个配置。因为我们执行测试的runner本身是一个docker container,执行测试时候runner需要从gitlab服务器里fetch gitlab repo,所以我们也需要确保它们也可以对gitlab.local这个域名进行解析。它们的配置信息通过挂载可以在host服务器主机的/srv/gitlab-runner/config/config.toml里查看和修改,下面是一个runner的配置例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
[[runners]]
name = "simple uasg"
url = "http://gitlab.local/ci"
token = "f37c2425b506aeb658e91eea4219f4"
executor = "docker"
[runners.docker]
tls_verify = false
image = "centos:6.8"
privileged = false
disable_cache = false
volumes = ["/cache"]
extra_hosts = ["gitlab.local:10.106.128.234"]
[runners.cache]

  这段配置信息除了extra_hosts这一项,其他都是注册成功后自动生成的。我们需要加上extra_hosts来确保gitlab.local可以得到正确的解析。

简述.gitlab-ci.yml

  最后简述一下.gitlab-ci.yml这个文件。

  当我们把代码push到gitlab上,或者提交了merge request,会自动触发gitlab ci进行测试。gitlab ci控制测试是通过project repo里面根目录的.gitlab-ci.yml这个文件来进行的,开发者在开发的同时也需要撰写这个文件并放到到repo的根目录下,才能触发自动测试。

  下面以一个我自己开发的仿微博的java web项目的.gitlab-ci.yml为例,简单说明一下整个结构,可以对照这个文件来看。下面附上代码:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
image: java:8
services:
- postgres:9.5
stages:
- build
- test
- package
- deploy
variables:
POSTGRES_DB: weibo
POSTGRES_USER: postgres
POSTGRES_PASSWORD: "gdzqzxwjs95"
compile:
stage: build
script:
- mkdir classes
- javac -d classes -cp "web/WEB-INF/lib/*:classes" src/weibo/tool/*
- javac -d classes -cp "web/WEB-INF/lib/*:classes" src/weibo/filter/*
- javac -d classes -cp "web/WEB-INF/lib/*:classes" src/weibo/servlet/*
artifacts:
name: classes-files
paths:
- classes/
connect:
stage: test
script:
- apt-get update && apt-get install -y postgresql-client
- export PGPASSWORD=$POSTGRES_PASSWORD
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f database/status.sql
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f database/subscriber.sql
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f database/the_insertion_of_status.sql
- java -cp "classes:web/WEB-INF/lib/*" org.junit.runner.JUnitCore weibo.servlet.SignUpAndLoginTest
package:
stage: package
script:
- cp -r classes web/WEB-INF/classes
- cd web
- jar -cvf weibo.war *
- cd ../
- mkdir binaries
- cp web/weibo.war binaries/
artifacts:
name: weibo-war
paths:
- binaries/
deploy:
image: tomcat:7
stage: deploy
script:
- cp binaries/weibo.war /usr/local/tomcat/webapps
- apt-get update && apt-get install -y postgresql-client
- export PGPASSWORD=$POSTGRES_PASSWORD
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f database/status.sql
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f database/subscriber.sql
- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -f database/the_insertion_of_status.sql
- wget -P /opt/ http://www.gtlib.gatech.edu/pub/apache//jmeter/binaries/apache-jmeter-3.0.tgz
- cp test\ plan/simple-test.jmx /tmp/
- cd /opt
- tar -zxvf apache-jmeter-3.0.tgz
- catalina.sh start
- sleep 30
- cd /opt/apache-jmeter-3.0/bin
- ./jmeter -n -t /tmp/simple-test.jmx -l report.xml
- cat report.xml

主要参数

  • image代表虚拟环境的基础镜像,这个镜像是docker镜像,我们可以根据项目需求在docker hub里挑选想要的镜像,作为基础的环境。因为weibo项目是用java开发的,因此这里选了java:8这个镜像。

  • services也是一些镜像,它们和我们主要的这个虚拟测试环境是同等的,都运行在一个独立的docker container中。这些service其实主要是用来运行数据库的,它们与主测试环境,即这里的java:8镜像建立的container,是通过docker link建立通信的。

  • variables是一些gitlab ci的变量,通过它们可以配置一些测试需要用到的参数,相当于是docker内的环境变量,并且可以给services共享。这里主要是配置postgres的用户,密码以及数据库,根据我们项目需求配置即可。后面我们就可以用这些参数来连接service了。这些参数的名字一般docker hub的image介绍里都会提及,我们可以根据具体的应用去查阅,看看我们需要用到哪些参数,极大地方便了我们的配置。

  • stage是测试的阶段,是人为划分的,只有在上一个stage正确通过以后才会运行后面的测试。

  • compile,connect,package,deploy都是job的名字,每个job都是独立运行在一个干净的docker container里面的,在运行结束时候自动销毁环境。其中同一个stage的job可以并行运行。每个job必须含有script,里面写我们执行测试的脚本即可。

  • artifacts保存我们需要在job和stage间共享的文件,如果测试成功,这些文件同时可以在gitlab的web页面上下载。

测试流程

  1. compile这个job主要用javac这个指令来把.java源文件编译成.class文件,这些.class文件放在artifacts里给下一个stage使用。
  2. connect这个job是进行单元测试的,我们要先下一个postgres的客户端去连接postgres这个service,进行一些数据库的初始化工作,再用junit这个库运行单元测试文件。
  3. package这个job是把weibo这个工程打成war包,放在artifacts里给后面测试部署环境使用。
  4. deploy这个job是基于tomcat:7这个基础镜像的,我们把上面的war包部署在tomcat里,把tomcat启动起来,然后下载安装jmeter来进行压力测试,并在屏幕上打印测试结果。

  其实.gitlab-ci.yml还有很多配置的知识,上面所述只不过冰山一角,要针对自己的项目编写这个文件,必须阅读gitlab ci的文档。