Gitlab的Pipeline执行机制
从gitlab 8.0开始,就可以通过.gitlab-ci.yml定义流水线的构建过程。所有分支互不影响,只有当前分支上存在这个文件才能够触发流水线的构建。
Pipeline基本概念
在gitlab上project->settings->CI/CD中可以设置提交以及MR时触发Pipeline构建。
一个流水线构建任务中有若干个构建阶段(stage),这些阶段都是按序进行构建的,根据在yaml文件中定义的顺序进行串行构建。在阶段下又有若干个构建任务(job),而这些构建任务在阶段中是并行运行的。
在一个流水线中,前项stage构建失败时,整个流水线失败,且后向的stage都不会再进行构建。在一个阶段中,如果任意一个job失败,则该阶段失败。
gitlab runner
gtilab并不会负责任务的构建,它只负责分发任务以及回显构建结果。构建的事情由gitlab runner负责,装载gitlab runner的机器将会是处理构建任务的计算机器。
注册runner
gitlab runner安装完成后,只需要运行注册命令:$ gitlab-runner register进行runner的注册即可。可以反复使用该命令为一个runner注册多次。在gitlab的project->settings->CI/CD->runner下能够看到gitlab给出的URL以及token,用于连接gitlab与gitlab runner。
具体步骤可以如下:
- 运行
sudo gitlab-runner register
- 输入 CI URL
- 输入 Token
- 输入 Runner 的标签
- 选择 Runner 的类型,简单起见还是选 Shell
其他一些比较通用的命令如:
$ gitlab-runner list #列出当前机器上注册的所有runner及其状态
$ gitlab-runner restart #重启runner
$ gitlab-runner unregister –url URL –token tokenNum #注销某个runner
.gitlab-ci.yml的编写
不同于python,yaml通过空格来区分代码块。将空格写成tab是不行滴。直接上代码吧:
# 定义 stages(阶段)。任务将按此顺序执行。
stages:
- build
- test
- deploy
# 定义 job(任务)
job1: #job的名称
stage: test
tags:
- XX #只有标签为XX的runner才会执行这个任务
only:
- dev #只有dev分支提交代码才会执行这个任务。也可以是分支名称或触发器名称
- /^future-.*$/ #正则表达式,只有future-开头的分支才会执行
script:
- echo "I am job1"
- echo "I am in test stage"
# 定义 job
job2:
stage: test #如果此处没有定义stage,其默认也是test
only:
- master
script:
- echo "I am job2"
- echo "I am in test stage"
allow_failure: true #允许失败,即不影响下步构建
# 定义 job
job3:
stage: build
except:
- dev #除了dev分支,其它分支提交代码都会执行这个任务
script:
- echo "I am job3"
- echo "I am in build stage"
when: always #不管前面几步成功与否,永远会执行这一步。它有几个值:on_success (默认值)\on_failure\always\manual(手动执行)
# 定义 job
.job4: #对于临时不想执行的job,可以选择在前面加个".",这样就会跳过此步任务
stage: deploy
script:
- echo "I am job4"
# 模板,相当于公用函数,有重复任务时很有用
.job_template: &job_definition # 创建一个锚,'job_definition'
image: ruby:2.1
services:
- postgres
- redis
test1:
<<: *job_definition # 利用锚'job_definition'来合并
script:
- test1 project
#下面几个都相当于全局变量,都可以添加到具体job中,这时会被子job的同名变量覆盖
before_script:
- echo "每个job之前都会执行"
after_script:
- echo "每个job之后都会执行"
variables: #变量
DATABASE_URL: "postgres://postgres@postgres/my_database" #在job中可以用${DATABASE_URL}来使用这个变量。常用的预定义变量有CI_COMMIT_REF_NAME(项目所在的分支或标签名称),CI_JOB_NAME(任务名称),CI_JOB_STAGE(任务阶段)
GIT_STRATEGY: "none" #GIT策略,定义拉取代码的方式,有3种:clone/fetch/none,默认为clone,速度最慢,每步job都会重新clone一次代码。我们一般将它设置为none,在具体任务里设置为fetch就可以满足需求,毕竟不是每步都需要新代码,那也不符合我们测试的流程
cache: #缓存
#因为缓存为不同管道和任务间共享,可能会覆盖,所以有时需要设置key
key: ${CI_COMMIT_REF_NAME} # 启用每分支缓存。
#key: "$CI_JOB_NAME/$CI_COMMIT_REF_NAME" # 启用每个任务和每个分支缓存。需要注意的是,如果是在windows中运行这个脚本,需要把$换成%
untracked: true #缓存所有Git未跟踪的文件
paths: #以下2个文件夹会被缓存起来,下次构建会解压出来
- node_modules/
- dist/
像stage、only、except等等都是yml的关键字,更多关键字的用法可以参考官方文档。
关键字needs:能够无序执行作业,无需按照阶段顺序运行某些作业,可以让多个阶段同时运行。比如阶段3的作业1可以在阶段1的作业2执行完成后就可执行,不需要等到阶段2执行完才能执行。
stages:
- build
- test
job1:
stage: build
needs: []
script:
- echo "I am job1"
job2:
stage: test
needs: []
script:
- echo "I am job2"
job3:
stage: test #如果此处没有定义stage,其默认也是test
needs: [job2]
script:
- echo "I am job3"
job1和job2将会并行,job3将在job2完成后立即执行,不需要等到build阶段的所有任务都完成才能执行。
YAML数据结构
yaml支持的数据结构有:对象(映射)、数组、纯量(scalars)。
对象
像stage: build就是一种对象,需要注意的是,冒号后面需要跟着一个空格。
对象还可以写成:hash: {stage: build, name: Jane}
数组
一组中划线组成的行,构成一个数组。
script:
-
- ele1
- ele2
- ele3
- outside
一个数组下有一个数组元素和一个名为outside的元素,数组元素中又有三个元素。
数组还可以写成:
script: [[ele1, ele2, ele3], outside]
纯量
数值直接以字面量的形式呈现。
布尔值可以直接使用true和false。
null用~表示。
yaml允许使用两个感叹号进行数据的强制转换:e: !!str 123 #将数值123转为字符串
字符串
字符串默认不使用引号表示。
如果字符串中包含空格或其他特殊字符如冒号,则需要用引号包含起来(单双引号都可以)。如e: ‘123 : 456’
若字符串中存在单引号,则需要用两个单引号进行转义。
字符串可以写成多行,但是从第二行开始,必须有单空格缩进。换行符会被转为空格。
sss: line1
line2
line3
引用
锚点&和别名*可以用于yaml的引用。
defaults: &defaults-anchor
adapter: postgres
host: localhost
development:
database: myapp_development
<<: *defaults-anchor
test:
database: myapp_test
<<: *defaults-anchor
公司的CI配置遇到了几个小坑:
ci的构建实际上是在每个runner机器上,用固定用户加sudo去执行每条指令。拿我司来说,代码会下到/home/gitlab-runner/下的某个子目录(Ubuntu),并在项目目录下执行自动化构建脚本。那么执行的用户就是gitlab-runner。Linux下的软件安装和下载都是分用户的,如果工具链装在了管理员用户的/root/下,那么其他普通用户将无法使用该工具链。
普通用户下的$HOME是/home/username,而管理员用户下的$HOME是/root/,不一样,在配置环境变量时如果输入了$HOME的话,可能结果和预期的不同。
所以需要用到的工具链最好安装在一些公用目录上,如/usr/local/bin,这样任意一个用户都能够访问到。