GitLab CI/CD

持续集成

基本概念

  • 持续集成(Continuous Integration):频繁地(一天多次)将代码集成到主干。让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。“持续集成并不能消除 Bug,而是让它们非常容易发现和改正。
  • 持续交付(Continuous Delivery):频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。持续交付可以看作持续集成的下一步。它强调的是,不管怎么更新,软件是随时随地可以交付的。
  • 持续部署(continuous Deployment):代码通过评审以后,自动部署到生产环境。是持续部署是持续交付的下一步,持续部署的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。

持续集成有哪些优点

  • 每天集成变化的代码,尽早发现风险,尽早估量软件的质量
  • 流程自动化,减少重复性劳动 自动化部署工作可以解放集成、测试、部署等重复性劳动
  • 保持随时部署,能快速反馈bug的修改而不是每次都需要改一段时间之后重新打包部署验证

GitLab CI

GitLab CI/CD

GitLab CI/CDGitLab Continuous Integration(Gitlab持续集成)的简称。GitLabGitLab 8.0开始提供了持续集成的功能,且对所有项目默认开启。只要在项目仓库的根目录添加.gitlab-ci.yml文件,并且配置了Runner(运行器),那么每一次push或者合并请求(Merge Request)都会触发CI Pipeline

GitLab Runner

GitLab Runner是一个开源项目,可以运行在 GNU / Linux,macOS 和 Windows 操作系统上。每次push的时候 GitLab CI 会根据.gitlab-ci.yml配置文件运行你流水线(Pipeline)中各个阶段的任务(Job),并将结果发送回 GitLab。GitLab Runner 是基于 Gitlab CI 的 API 进行构建的相互隔离的机器(或虚拟机)。GitLab Runner 不需要和 Gitlab 安装在同一台机器上,且考虑到 GitLab Runner 的资源消耗问题和安全问题,也不建议这两者安装在同一台机器上。

  • 共享Runner: Shared Runners 对于多个项目之间具有类似要求的作业非常有用。 您可以拥有一个或多个可以处理多个项目的Runners,而不是让多个Runner为多个项目空闲。 这样可以更轻松地维护和更新它们。 Shared Runners使用公平队列处理作业。与使用FIFO队列的特定Runner相比,这可以防止项目创建数百个可能导致吃掉所有可用共享Runners资源的作业的情况。
  • 专属Runner: Specific Runners 对于具有特殊要求的作业或具有特定需求的项目非常有用。 如果某个工作有一定的要求,您可以考虑到这一点设置特定的Runner,而不必为所有Runner执行此操作。 例如,如果要部署某个项目,可以设置特定的Runner以获得正确的凭据。 在这种情况下,标签的使用可能是有用的。Specific Runners 使用FIFO队列处理作业。
  • 分组Runner: 当您在一个组下有多个项目并且希望所有项目都可以访问一组Runners时, Group Runners 非常有用。 Group Runners 使用FIFO队列处理作业

Runner详细

Pipelines

Pipelines 我们也叫他流水线,比如一条流水线可能包含构建、测试打包、部署到服务器等步骤i,每次 push 和 Merge Request 都会触发流水线。

pipeline

Stage

Stage 表示构建阶段,比如上图中的 Build、Test、Staging、Production这四个阶段,一个 Pipeline 有多个 Stage

  1. Stage会按照配置的顺序执行,当一个 Stage 完成之后才会执行下一个 Stage
  2. 所有 Stage 成功完成,这个 pipeline 才算成功(manual 任务可以不执行)。
    3.如果一个 Stage 失败,后面的 Stage 都不会执行,当然也可以配置失败也往下继续执行。

Jobs

Jobs 表示每个作业,也就是 Stage 里面具体的某个任务,因为可以从上图中看到一个 Pipleline 包含多个 Stage。一个 Stage 可以包含多个任务。

  • 相同 Stage 中的 Jobs 无执行顺序要求,会并行执行
  • 相同 Stage 中的 Jobs 都执行成功时,该 Stage 才会成功
  • 如果任何一个 Job 失败,那么该 Stage 失败,即该构建任务 (Pipeline) 也失败(可以在.gitlab-ci.yml文件中配置允许某 Job 可以失败,也算该 Stage 成功)

.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
variables:
MAVEN_OPTS: "-Xms256m -Xmx512m"
MAVEN_CLI_OPTS: "-B -V -ff -Dfile.encoding=UTF-8 -Dsonar.sourceEncoding=UTF-8"
SONAR_URL: http://sonar.xxx.com

# stages
stages:
- test
- sonar

# 编译测试和生成代码覆盖率.
编译测试:
stage: test
only:
- master
- develop
- /^feature\/.*$/
- /^bugfix\/.*$/
- /^hotfix\/.*$/
tags:
- maven
script:
- mvn $MAVEN_CLI_OPTS clean org.jacoco:jacoco-maven-plugin:prepare-agent test
- cat target/site/jacoco/index.html

#sonar问题检查
Sonar问题检查:
stage: sonar
tags:
- maven
only:
- master
script:
- mvn --batch-mode verify sonar:sonar -Dsonar.host.url=$SONAR_URL -Dsonar.login=497a0e0e2fc07f64c4b54edc17bb47dfa251ba34 -Dsonar.gitlab.project_id=$CI_PROJECT_PATH -Dsonar.gitlab.max_major_issues_gate=0 -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME

Sonar问题定时检查:
stage: sonar
tags:
- maven
only:
- schedules
script:
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent test sonar:sonar -D sonar.host.url=$SONAR_URL -D sonar.login=497a0e0e2fc07f64c4b54edc17bb47dfa251ba34


Sonar非主干问题检查:
stage: sonar
tags:
- maven
except:
- master
script:
- mvn --batch-mode verify sonar:sonar -Dsonar.host.url=$SONAR_URL -Dsonar.login=497a0e0e2fc07f64c4b54edc17bb47dfa251ba34 -Dsonar.analysis.mode=preview -Dsonar.gitlab.max_major_issues_gate=0 -Dsonar.gitlab.project_id=$CI_PROJECT_PATH -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME

# 针对 develop 分支做 Sonar 检查.
Sonar手动检查:
stage: sonar
when: manual
tags:
- maven
only:
- develop
script:
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent test sonar:sonar -D sonar.host.url=$SONAR_URL -D sonar.login=497a0e0e2fc07f64c4b54edc17bb47dfa251ba34

上面就定义了一个简单的 pipeline,其实只有两个阶段,一个test,一个sonar。

GitLab CI/CD yaml 常用配置

Job

Job 可以定义很多个,但是每个 Job 只能有一个唯一的名字,下面的字段来描述 job 做的事

  • script: 必须,表示 Runner 执行的脚本
  • extends: 继承的配置条目
  • images: 这个 job 所用的 Docker 镜像
  • services: 依赖的 Docker 服务,比如 Docker:dind
  • stage: 哪个 stage
  • variables: job级别的变量
  • only: 指定触发的情况
  • except: 指定不能触发的情况
  • tags: 筛选 Runner
  • allow_failure: 允许失败,失败不影响 stage 失败
  • when: 定义何时开始job,可以是on_success,on_failure,always或者manual
  • dependencies: 定义job依赖关系,这样他们就可以互相传递artifacts
  • cache: 定义在后续运行的缓存文件列表,后面的job可以用
  • before_script: script运行之前运行的脚本
  • after_script: script运行之后的命令
  • environment: 作业完成部署的环境名称
  • retry: 定义在发生故障时可以自动重试作业的时间和次数
  • parallel: 定义并行运行的作业实例数

only 和 except

only 和 except 可以指定分支名字,或者是个正则表达式,比如

1
2
3
4
job:
# use regexp
only:
- /^issue-.*$/

或者使用一些特殊的值:

  • branches: 只要是分支
  • tags: 打tag的时候
  • merge_requests: Merge Request 的时候
  • pushes: push代码就触发
  • schedules: 定时任务运行
  • trigger: 触发器运行
  • web: 页面点了才运行

cache

使用paths指令选择要缓存的文件或目录。也可以使用通配符。

如果 cache 定义在 jobs 的作用域之外,那么它就是全局缓存,所有 jobs 都可以使用该缓存。

比如 rspec 阶段我缓存他的 binaries 目录下的 apk 文件和 .config 文件

1
2
3
4
5
6
rspec:
script: test
cache:
paths:
- binaries/*.apk
- .config

但是某个任务的缓存会覆盖全局的,比如 下面这个 rspec job 只会缓存 binaries/ 目录下的文件。

1
2
3
4
5
6
7
8
9
10
cache:
paths:
- my/files

rspec:
script: test
cache:
key: rspec
paths:
- binaries/

可以使用下面这个配置项缓存 binaries 下没有被 git 跟踪的文件

1
2
3
4
5
6
rspec:
script: test
cache:
untracked: true
paths:
- binaries/

缓存作业的默认行为是在执行开始时下载文件,并在最后重新上传它们。这允许将作业所做的任何更改保留以供将来运行,并称为先拉再推缓存策略。 如果您知道作业不会更改缓存文件,则可以通过设置策略跳过上传步骤:拉入作业规范。通常,这将在较早阶段与普通缓存作业配对,以确保缓存不时更新。可以使用 policy: push 跳过下载只上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
stages:
- setup
- test

prepare:
stage: setup
cache:
key: gems
paths:
- vendor/bundle
script:
- bundle install --deployment

rspec:
stage: test
cache:
key: gems
paths:
- vendor/bundle
policy: pull
script:
- bundle exec rspec ...

artifacts

artifacts用于指定成功后应附加到 job 的文件和目录的列表。只能使用项目工作间内的文件或目录路径。在job成功完成后artifacts将会发送到GitLab中,同时也会在 GitLab UI 中提供下载。如果想要在不通的 job 之间传递artifacts,请查阅依赖关系。以下是一些例子

发送binaries和.config中的所有文件:

1
2
3
4
artifacts:
paths:
- binaries/
- .config

可以配置文件过期时间

1
2
3
job:
artifacts:
expire_in: 1 week

比如下面这个例子,文件传递

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
installing-dependencies:
stage: prepare
script:
- xxx
artifacts:
name: "vendor"
untracked: true
expire_in: 60 mins
paths:
- $CI_PROJECT_DIR/vendor
code-review:
stage: testing
dependencies:
- installing-dependencies
script:
- xxx
test-image:
stage: build
image: docker:latest
services:
- docker:dind
dependencies:
- installing-dependencies
script:
- xxx
only:
- develop

artifacts 默认会在多个job传递,可以使用如下配置禁止传递

1
2
3
4
5
sonar-job:
image: ${REGISTRY_URL}/dzjz/ci/maven
stage: sonar
when: manual
dependencies: []

cache 和 artifacts 的区别

其中 cache 指的是缓存, 常用于依赖安装中, 如几个jobs都需要安装相同的依赖, 可以使用依赖, 此时可以加快依赖的安装进度;
对于artifacts则是将某个工件上传到GitLab提供下载或后续操作使用, 由于每个job启动时, 都会自动删除.gitignore中指定的文件, 因此对于依赖安装目录, 即可以使用cache, 也可以使用artifacts。

主要区别如下:

  1. cache不一定命中,artifacts肯定命中, 能否使用cache取决当当前机器是否生成过cache, artifacts则每次都会从GitLab下载
  2. 特别是开发环境, 如果每次都希望使用最新的更新, 应当删除cache, 使用artifacts, 这样可以保证确定的更新
  3. artifacts中定义的部分, 会自动生成, 并可以传到下面的job中解压使用, 避免了重复依赖安装等工作
  4. 如果使用Docker运行Gitlab-Runner, cache会生成一些临时容器, 不容易清理
  5. artifacts可以设置自动过期时间, 过期自动删除,cache不会自动清理
  6. artifacts会先传到GitLab服务器, 然后需要时再重新下载, 所以这部分也可以在GitLab下载和浏览

更多

配置定时任务

GitLab CI 中可以在 GitLab Settings -> CI/CD -> Schedules中配置定时任务,点击New Schedule按钮,可以配置你流水线的定时执行任务,包括:描述信息、定时的Cron表达式、目标分支、变量等信息。

然后在需要定时执行的作业的only分支写上schedules即可。

校验 .gitlab-ci.yml

CI/CD -> pipelineS 列表页面右上角有个 CI Lint 可以进行校验。