gitlab-从零开始实现你的CICD

为什么使用gitlab?

每一位开发工程师都多多少少使用过几款不同的版本管理工具,诸如svn,git,或者项目更小一点的使用简单的存储工具然后再手动合并代码也不是没有,现如今最火的版本管理工具莫过于git了,而在开源世界中,github无疑是最火的项目源码管理仓库,而在我们国家由于访问github速度过慢,也有gitee(码云)等类似github的源码管理仓库,gitlab又是什么?

GitLab最开始是仿照GitHub做的一个面向企业的Git仓库软件,随着其社区的发展和产品的迭代,目前GitLab已经是企业端Git仓库的首选。

使用gitlab有几个好处:

  • 在一个 GitLab 项目上一起工作的最简单方法就是赋予协作者对 git 版本库的直接 push 权限
  • 另外一个让合作更解耦的方法就是使用合并请求。 它的优点在于让任何能够看到这个项目的协作者在被管控的情况下对这个项目作出贡献。
  • 毫无疑问,免费且满足企业级源码管理的权限粒度和私密性是gitlab最大的优点。

接下来我们将会在Centos 7 上使用Docker来教你一步步的完成gitlab服务器的搭建,并且完美的绕过这其中的诸多坑(哈哈,我自己就没少掉进去),对于Docker还不熟悉的朋友,应该考虑学习一下这个说新不新说旧不旧的容器引擎了。

一、安装gitlab

首先,你必须有一台4GB内存并且拥有Docker环境的服务器(因为运行gitlab会占据3G多的内存),如何安装Docker请自行百度,这里不再过多描述。

  • 拉取gitlab镜像
docker pull docker.io/gitlab/gitlab-ce
  • 启动gitlab容器服务
    值得一提的是,在启动容器服务之前,有几件事情需要确认一下:
    1.linux主机默认远程连接端口号是22,而我们的git SSH连接端口号也是22,所以为了确保SSH连接端口正常使用,需要修改我们的远程连接端口号,具体如何修改参考这里centos 修改默认远程端口号
    2.由于gitlab服务后续可能需要重复启动,所以为了保证docker容器的数据不丢失,需要跟我们的宿主机进行挂载,这边我在/usr/local目录下新建gitlab文件夹用来跟容器内部数据卷进行挂载。

以上事情确认完之后,可以启动我们的gitlab 容器;

docker run -d -p 443:443 -p 81:80 -p 22:22 --name gitlab --restart always -v /usr/local/gitlab/config:/etc/gitlab -v /usr/local/gitlab/logs:/var/log/gitlab -v /usrlocal//gitlab/data:/var/opt/gitlab docker.io/gitlab/gitlab-ce

第一次启动时间久一点,需要等待几分钟,最终gitlab可以正确在浏览器中访问如下图:

gitlab-从零开始实现你的CICD_第1张图片
这边我是已经启动并且配置好的gitlab,首次进入gitlab会提示你修改密码,默认用户都是root.

  • 修改配置文件防止项目clone url错误

在成功进入gitlab之后,我们可以新建一个group,group下再创建几个项目,然后在我们满心欢喜的要clone自己的仓库时,发现我们的项目地址是这样子的:
gitlab-从零开始实现你的CICD_第2张图片
gitlab-从零开始实现你的CICD_第3张图片

没有域名、没有ip地址,有的只是一串特殊数字,很显然这样的地址你在本地clone的时候会提示你找不到仓库位置,应该如何解决呢?

很简单,只要修改gitlab的配置文件,将服务器ip地址正确修改即可。而因为我们的gitlab是Docker 容器,由于我们之前启动时挂载了数据卷到宿主机上,所以我们直接修改宿主机上的配置文件:

vi /usr/local/gitlab/config/gitlab.rb

gitlab-从零开始实现你的CICD_第4张图片
加入一行;

external_url 'http://gitlab.xiejr.com'

接下来还需要修改项目clone url的端口号,由于我们启动容器把81端口映射到80,所以还需要:

vi /usr/local/gitlab/data/gitlab-rails/etc/gitlab.yml
# This file is managed by gitlab-ctl. Manual changes will be
# erased! To change the contents below, edit /etc/gitlab/gitlab.rb
# and run `sudo gitlab-ctl reconfigure`.

production: &base
  #
  # 1. GitLab app settings
  # ==========================

  ## GitLab settings
  gitlab:
    ## Web server settings (note: host is the FQDN, do not include http://)
    host: gitlab.xiejr.com
    port: 81 #(端口号修改过来即可)
    https: false

然后保存好之后重启gitlab容器,

docker restart gitlab

重启第一次可能会失败,重复启动即可成功,之后看到我们的gitlab项目cloen url变成如下:

gitlab-从零开始实现你的CICD_第5张图片

最后就可以愉快的clone 和push你的代码啦。

二、安装gitlab-runner

为什么要安装gitlab-runner呢?因为我们要实现标题中提到的CICD呀,那什么是CICD,哈哈忘记介绍了。

  • CI(Continuous Integration):持续集成。持续集成是一个将集成提前至开发周期的早期阶段的实践方式,让构建、测试和集成代码更经常反复地发生。
  • CD(Continuous Delivery):持续交付。持续交付是持续集成的延伸,将集成后的代码部署到类生产环境,确保可以以可持续的方式快速向客户发布新的更改。如果代码没有问题,可以继续手工部署到生产环境中。

以java项目为例,没有CICD之前,传统的做法是在本地开发完成之后,手动编译、打包,然后通过ftp等传输工具传输到生产环境中,通过命令行交互或者脚本的方式发布服务的方式来实现一整套的开发–测试-交付流水线工作,但是根据项目越来越多,手动构建和发布的方式已经不适用了,所以引入CICD的概念来解决这些问题。

之前自己学习扩展的时候,有搭建过jenkins来实现CICD,但是搭建jenkins过程有点复杂,所以这次选择使用gitlab来完成。

gitlab和gitlab-runner有什么关系呢

GitLab-Runner是配合GitLab-CI进行使用的。一般地,GitLab里面的每一个工程都会定义一个属于这个工程的软件集成脚本,用来自动化地完成一些软件集成工作。当这个工程的仓库代码发生变动时,比如有人push了代码,GitLab就会将这个变动通知GitLab-CI。这时GitLab-CI会找出与这个工程相关联的Runner,并通知这些Runner把代码更新到本地并执行预定义好的执行脚本。所以,GitLab-Runner就是一个用来执行软件集成脚本的东西。你可以想象一下:Runner就像一个个的工人,而GitLab-CI就是这些工人的一个管理中心,所有工人都要在GitLab-CI里面登记注册,并且表明自己是为哪个工程服务的。当相应的工程发生变化时,GitLab-CI就会通知相应的工人执行软件集成脚本。如下图所示:
gitlab-从零开始实现你的CICD_第6张图片
Runner可以分布在不同的主机上,同一个主机上也可以有多个Runner。

Runner类型

GitLab-Runner可以分类两种类型:Shared Runner(共享型)和Specific Runner(指定型)。

Shared Runner:这种Runner(工人)是所有工程都能够用的。只有系统管理员能够创建Shared Runner。

Specific Runner:这种Runner(工人)只能为指定的工程服务。拥有该工程访问权限的人都能够为该工程创建Shared Runner。

安装Runner

  • 拉取runner镜像并且启动
docker pull docker.io/gitlab/gitlab-runner:latest
docker run -d --name gitlab-runner --restart always -v /usr/local/gitlab-runner/config:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest

这边同gitlab一样,我也是在/usr/local下新建了一个gitlab-runner文件夹实现数据卷挂载,并且将宿主机docker也挂载到容器中方便runner可以使用宿主机的docker环境。

  • 进入runner容器,注册到gitlab上

runner容器启动之后,我们需要将它注册到gitlab上才可以正常使用,因此第一步要先进入容器;

docker exec -it gitlab-runner bash

运行以下注册命令:

gitlab-runner register

输入Gitlab实例的地址

Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
http://gitlab.xiejr.com:81

输入token

Please enter the gitlab-ci token for this runner
xxx

不知道token如何来的自己去gitlab主设置页里面获取,如下图;
gitlab-从零开始实现你的CICD_第7张图片
然后继续上一步,输入Runner的描述

Please enter the gitlab-ci description for this runner
[hostname] my-runner

输入与Runner关联的标签

Please enter the gitlab-ci tags for this runner (comma separated):
my-tag

输入Ruuner的执行者

Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
docker

如果上面执行者为docker,需要你在后续项目根部的.gitlab-ci.yml中指定docker版本

Please enter the Docker image (eg. ruby:2.1):
alpine:latest

通过以上命令后,就可以在gitlab中查看到了这个刚刚创建的runner

在这里插入图片描述

runner注册完毕之后,我们还需要修改一下runner的配置文件,实现runner与宿主机的数据挂载:

vi /usr/local/gitlab-runner/config/config.toml
concurrent = 1
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "my runner"
  url = "http://gitlab.xiejr.com:81/"
  token = "NJo3MgtzBQoxrZLzxyHv"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.docker]
    tls_verify = false
    image = "alpine:latest"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache","/var/run/docker.sock:/var/run/docker.sock","/usr/local/repos/gradle:/usr/local/repos/gradle"]
    shm_size = 0
    extra_hosts = ["gitlab.xiejr.com:81.68.146.23"]

在上面的volumes数组中添加docker的挂载和gradle本地仓库的挂载,加快项目的构建速度。

最后,只需要再重启runner容器即可:

docker restart gitlab-runner

三、配置gitlab邮件服务

以往在企业项目开发中,我们的每一次构建都会发送构建结果至我们的邮箱或者微信中,所以为了实现gitlab的邮箱构建提醒,必须先对邮箱进行认证。

这边我们使用QQ邮箱为例教学。

1.首先先开启QQ邮箱的SMTP支持:

gitlab-从零开始实现你的CICD_第8张图片
gitlab-从零开始实现你的CICD_第9张图片
开启之后会弹出一个授权码,复制授权码,这在后续的认证中会用到。

2.修改gitlab配置

vi /usr/local/gitlab/config/gitlab.rb
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.qq.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "邮箱@qq.com"
gitlab_rails['smtp_password'] = "开通smtp时返回的授权码"
gitlab_rails['smtp_domain'] = "qq.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
​
user['git_user_email'] = "邮箱@qq.com"
gitlab_rails['gitlab_email_from'] = '邮箱@qq.com'

3.更新设置

先进入容器当中,然后输入:

gitlab-ctl reconfigure

就完成了邮件的配置。

4.客户端校验

来到gitlab网页客户端,
gitlab-从零开始实现你的CICD_第10张图片
由于我这里是已经校验通过,否则应该是手动添加邮箱,然后发送一封确认邮件,在邮箱中点击链接就可以完成邮件的认证了。

四、项目构建、发布

这边我以自己的一个springboot项目为例演示gitlab的CICD流程。

gitlab-从零开始实现你的CICD_第11张图片
如上图,是一个标准的gradle项目。

在editor-service模块下的front-service web工程下新建Dockerfile,作为jar包构建镜像脚本:

FROM openjdk:8-jdk-alpine
MAINTAINER xiejiarong 937890254@qq.com
ADD /editor-service/front-service/build/libs/*.jar  /editor-front-service.jar
EXPOSE 8081
ENTRYPOINT ["java","-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005","-jar","/editor-front-service.jar","--spring.profiles.active=prod"]

在项目根目录新建.gitlab-ci.yml:

# 因为我们Runner执行器设置为docker, 所以这里需要指定docker的版本
image: docker:stable
# 定义三个阶段
stages:
  - compile
  - build
  - deploy
# 定义三个变量,方便后续构建和发布
variables:
  FRONT_SERVICE: "editor-service/front-service"
  IMAGE_FRONT: "odf-editor-sys-front-service"
  CONTAINER: "odf-editor-sys-front-service-container"
  VERSION: "1.0.0"
# 第一阶段
compile:
  # 打包用到了gradle, 所有需要拉取gradle镜像
  image: docker.io/gradle
  # 指定阶段
  stage: compile
  # 运行脚本, 使用变量时要用到 $ 符号
  script:
    - export GRADLE_USER_HOME=/usr/local/repos/gradle
    - gradle clean build
  # 只作用在master分支
  only:
    - master
  # 创建runner时指定的tag
  tags:
    - my-tag
  # 编译后有产物,所以要指定下过期时间和路径, 以供于其他阶段使用
  artifacts:
    expire_in: 10 min
    paths:
      - $FRONT_SERVICE/build/libs/*.jar
#第二阶段,开始生成docker镜像
build:
  image: docker:stable
  stage: build
  only:
    - master
  tags:
    - my-tag
  script:
    - docker build -t $IMAGE_FRONT:$VERSION -f $FRONT_SERVICE/Dockerfile .

#第三阶段,发布
deploy:
  image: docker:stable
  stage: deploy
  only:
    - master
  tags:
    - my-tag
  script:
    - docker stop $CONTAINER ||true
    - docker rm $CONTAINER || true
    - docker run -d -p 8081:8081 -p 5005:5005 --name $CONTAINER -v /usr/local/projects/odf-editor-sys/backend/db:/usr/local/projects/odf-editor-sys/backend/db $IMAGE_FRONT:$VERSION

.gitlab-ci.yml是gitlab开启自动CICD的默认扫描文件,默认当发生代码push的时候gitlab会根据yml文件指定的runner开启CICD执行流程**。

下面简单讲一下该配置文件的语法和几个重要概念:


jobs(任务)

job_name:
  # 要跑的脚本或命令列表
  script:
    - rake spec
    - coverage
  # pipelines阶段
  stage: test
  # 只针对哪个分支
  only:
    - master
  # 除了哪个分支以外
  except:
    - develop
  # 指定哪些runner适用该job
  tags:
    - ruby
    - postgres
  # 是否容错
  allow_failure: true
关键字 是否必须 描述
script 必须 定义Runner需要执行的脚本或命令
image 非必须 需要使用的docker镜像
services 非必须 定义了所需的docker服务
stage 非必须 定义了工作的场景阶段,默认是test
type 非必须 stage的别名,不赞成使用
variables 非必须 在job级别上定义的变量

script(执行脚本语法)

script是一段由Runner执行的shell脚本,例如:

job:
  script: "bundle exec rspec"
job:
  script:
    - uname -a
    - bundle exec rspec

有些时候,script命令需要被单引号或者双引号所包裹。举个例子,命令中包涵冒号的时候,该命令需要被引号所包裹,这样YAML解析器才知道该命令语句不是“key: value”语法的一部分。当命令中包涵以下字符时需要注意打引号:: { } [ ] , & * # ? | - < > = ! % @ `

stage(阶段)

stage指定一组job在不同场景阶段执行。在相同stage下的job(任务)将会被并行的执行

only and except

only和except两个参数说明了job什么时候将会被创建:

only定义了job需要执行的所在分支或者标签
except定义了job不会执行的所在分支或者标签
以下是这两个参数的几条用法规则:

only和except如果都存在在一个job声明中,则所需引用将会被only和except所定义的分支过滤.
only和except允许使用正则
only和except允许使用指定仓库地址,但是不forks仓库

tags(runner标签)

tags这个参数是用来选择允许哪些runners来执行该jub的。

当你初始化Runner并注册一个Runner的时候,你被要求为Runner指定一个或多个标签,例如我的一个Runner被注册为test1。

job:
    tags:
        - test1
        - ruby

上面的声明将会保证该job将会被标签上有test1和ruby的runner所执行。如果没有就不执行

allow_failure

allow_failure被用于当你想允许job失败而不阻塞剩下的CI套件执行的时候,失败的job不会影响到commit状态(pipelines执行完会在commit上标记失败或成功状态,但是不会阻止commit提交)

当allow_failure为true,并且job失败了,pipline将会置绿或者置成功显示,但是你会在commit页面或者job页面看到一条“CI build passed with warnings”的警告信息哈。这样用户就能注意到失败并采取其他措施。

在下面的例子中,job1和job2将会并行执行(事实告诉我们其实还是顺序执行),不过如果job1失败了,整个pipeline不会停止,并且会流转到下一个场景阶段继续执行:

job1:
  stage: test
  script:
  - execute_script_that_will_fail
  allow_failure: true

job2:
  stage: test
  script:
  - execute_script_that_will_succeed

job3:
  stage: deploy
  script:
  - deploy_to_staging

上面的例子实测,job1显示警告,job2通过,job3通过

when

when参数是确定该job在失败或者没失败的时候执行不执行的参数。

when支持以下几个值之一:

1.on_success 只有在之前场景执行的所有作业成功的时候才执行当前job,这个就是默认值,我们用最小配置的时候他默认就是这个值,所以失败的时候pipeline会停止执行后续任务
2.on_failure 只有在之前场景执行的任务中至少有一个失败的时候才执行
3.always 不管之前场景阶段的状态,总是执行

下面是例子:

stages:
- build
- cleanup_build
- test
- deploy
- cleanup

build_job:
  stage: build
  script:
  - make build

cleanup_build_job:
  stage: cleanup_build
  script:
  - cleanup build when failed
  when: on_failure

test_job:
  stage: test
  script:
  - make test

deploy_job:
  stage: deploy
  script:
  - make deploy
  when: manual

cleanup_job:
  stage: cleanup
  script:
  - cleanup after jobs
  when: always

上面的例子将会:

1.只有在build_job失败的时候执行cleanup_build_job
2.在pipeline最后一步,不管前面是失败或者成功,执行cleanup_job
3.允许你在GitLabUI上手动执行deploy_job

artifacts

artifacts 被用于在job作业成功后将制定列表里的文件或文件夹附加到job上,传递给下一个job,如果要在两个job之间传递artifacts,你必须设置dependencies,下面有几个例子

传递所有binaries和.config:

artifacts:
  paths:
  - binaries/
  - .config

传递所有git没有追踪的文件

artifacts:
  untracked: true

传递binaries文件夹里所有内容和git没有追踪的文件

artifacts:
  untracked: true
  paths:
  - binaries/

禁止传递来的artifact:

job:
  stage: build
  script: make build
  dependencies: []

有时候用户可能只需要为打过标签的发行版创建artifacts去避免将临时构建的artifacts传递到生产服务器存储。

那么这时候我们可以只为打tags的行为创建artifacts:

default-job:
  script:
    - mvn test -U
  except:
    - tags

release-job:
  script:
    - mvn package -U
  artifacts:
    paths:
    - target/*.war
  only:
    - tags

最终artifacts将会在job执行完毕后送到GitLab ui前台来,你可以直接下载它(在tag页,在details页,在pipeline页的下载按钮上都会出现)。


在大概了解了.gitlab-ci.yml语法之后,我们直接把这次改动commit and push,然后查看项目CICD控制台:

gitlab-从零开始实现你的CICD_第12张图片
gitlab-从零开始实现你的CICD_第13张图片
可以看到,runner正在执行任务,按照我们的yml定义的compile、build、deploy定义的三个阶段job按顺序执行,过一会儿有邮件提醒;

gitlab-从零开始实现你的CICD_第14张图片
(哈哈,我是一边写博客一边操作),打开邮件:
gitlab-从零开始实现你的CICD_第15张图片

在这里插入图片描述
可以看见三个stage都完美的执行成功,那究竟服务有没有自动部署上去呢,在浏览器输入swagger的地址:

gitlab-从零开始实现你的CICD_第16张图片
可以看到确实是已经发布成功。

总结

通过整套的搭建流程下来,对于Docker、服务器、CICD、gitlab-ci.yml语法还是有一定的要求,目前我们公司使用的也是gitlab的CICD功能,在实际生产中通过多环境分支的代码控制,对于现如今的分布式微服务的快速上下线和项目的迭代来说,效率会提高不少,感谢阅读,欢迎讨论。

你可能感兴趣的:(devops,ci/cd,docker,gitlab)