JB的git之旅--.gitlab-ci.yml介绍

11,310 阅读8分钟

前言

之前有专门写过一篇文章,对gitlab ci进行介绍,其中有涉及到gitlab-ci.yml进行介绍,那可能会有疑问,为什么要再开一篇来再说yml这玩意?

主要是因为之前在用gitlab-ci遇到不少问题,一路上摸索不容易,虽然有前车之鉴,但仍然遇到不可描述的问题,因为想重新了解下这块,当做是一个笔记;
上一篇关于gitlab ci介绍的链接:
https://juejin.cn/post/6844903609864290311

gitlab-ci运行介绍

由以下两个模块组成:

gitlab-ci server
gitlab-ci-runner

其中,gitlab-ci server负责调度、触发Runner,以及获取返回结果.
而gitlab-ci-runner则是主要负责来跑自动化CI(测试,编译,打包等)。

基本流程是:

用户提交代码
检查是否有.gitlab-ci.yml文件
如果无,则结束;
如果有,则调用runner执行脚本
获取返回的结果。

.gitlab-ci.yml

从7.12版本开始,GitLab CI使用YAML文件(.gitlab-ci.yml)来管理项目配置。
该文件存放于项目仓库的根目录,它定义该项目如何构建。

开始构建之前YAML文件定义了一系列带有约束说明的任务。
这些任务都是以任务名开始并且至少要包含script部分:

job1:
  script: 
      - echo "jb1"
  
job2:
  script: 
      - echo "jb2"

上面例子是一个简单且带有两个独立任务的CI配置,每个任务分别执行echo输出对应的内容。

script可以直接执行系统命令
(例如:./configure;make;make install)或者是直接执行脚本(test.sh)

任务是由Runners接管并且由服务器中runner执行。
每一个任务的执行过程都是独立运行的

例子1:

# 定义 stages
stages:
    - build
    - test

# 定义 job
job1:
    stage: test
    script:
        - echo "I am jb1"
        - echo "I am in test stage"

# 定义 job
job2:
    stage: build
    script:
        - echo "I am jb2"
        - echo "I am in build stage"

根据在 stages 中的定义,build 阶段要在 test 阶段之前运行,
所以 stage:build 的 jobs 会先运行,
之后才会运行 stage:test 的 jobs。

再来个复杂的例子2:

image: ruby:2.1
services:
  - postgres

before_script:
  - bundle install

after_script:
  - rm secrets

stages:
  - build
  - test
  - deploy

job1:
  stage: build
  script:
    - execute-script-for-job1
  only:
    - master
  tags:
    - docker5+4X5=25

下面列出保留字段,这些保留字段不能被定义为job名称:

关键字 是否必须 描述
image 用于docker镜像,查看docker文档
services 用于docker服务,查看docker文档
stages 定义构建阶段
types stages 的别名(已废除)
before_script 定义在每个job之前运行的命令
after_script 定义在每个job之后运行的命令
variable 定义构建变量
cache 定义一组文件列表,可在后续运行中使用

image和services
这两个关键字允许使用一个自定义的Docker镜像和一系列的服务,并且可以用于整个job周期。

before_script
before_script用来定义所有job之前运行的命令,包括deploy(部署) jobs,
但是在修复artifacts之后。它可以是一个数组或者是多行字符串。

after_script

GitLab 8.7 开始引入,并且要求Gitlab Runner v1.2

after_script用来定义所有job之后运行的命令。它必须是一个数组或者是多行字符串;

stages
stages用来定义可以被job调用的stages。

stages中的元素顺序决定了对应job的执行顺序:

1. 相同stage的job可以平行执行。
2. 下一个stage的job会在前一个stage的job成功后开始执行。

这里划重点:下一个stage的job会在前一个stage的job成功后开始执行
正因为如此,所以可以在stage设定很多相互依赖的步骤,而且不需要判断是否成功状态,因为能执行下来的,必定是成功的!

看会上方的例子1:

stages:
    - build
    - test
    1. 首先,所有build的job都是并行执行的
    1. 所有build的jobs执行成功后,test的jobs才会开始并行执行
  • 3.所有的test的jobs执行成功,commit才会标记为success
  • 4.任何一个前置的jobs失败了,commit都会标记为failed并且下一个stages的jobs都不会执行。

这里提两个特殊例子:

1.如果.gitlab-ci.yml中没有定义stages,那么jobs的stages会默认定义成build,test和deploy
2.如果一个job没有指定stage,那么这个任务会分配到test stage

types

已废除,将会在10.0中移除。用stages替代,含义与stages一致

variable
GItLab CI 允许在.gitlab-ci.yml文件中添加变量,并在job环境中起作用。
因为这些配置是存储在git仓库中,所以最好是存储项目的非敏感配置,例如:

variables:
  jb: "jb is here"

这里注意,:号后面是带上空格的,不然会说语法错误 这些变量可以被后续的命令和脚本使用.

除了用户自定义的变量外,Runner也可以定义它自己的变量。
CI_COMMIT_REG_NAME就是一个很好的例子,它的值表示用于构建项目的分支或tag名称。
除了在.gitlab-ci.yml中设置变量外,还有可以通过GitLab的界面上设置私有变量。

cache
用来指定需要在job之间缓存的文件或目录。只能使用该项目工作空间内的路径。

从GitLab 9.0开始,pipelines和job就默认开启了缓存

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

缓存git中没有被跟踪的文件:

rspec:
  script: test
  cache:
    untracked: true

缓存binaries下没有被git跟踪的文件:

rspec:
  script: test
  cache:
    untracked: true
    paths:
    - binaries/

缓存key
key指令允许我们定义缓存的作用域(亲和性),可以是所有jobs的单个缓存,也可以是每个job,也可以是每个分支或者是任何你认为合适的地方。

cache:key可以使用任何的预定义变量

缓存每个job:

cache:
  key: "$CI_JOB_NAME"
  untracked: true

缓存每个分支:

cache:
  key: "$CI_COMMIT_REF_NAME"
  untracked: true

缓存每个job且每个分支:

cache:
  key: "$CI_JOB_NAME/$CI_COMMIT_REF_NAME"
  untracked: true

缓存每个分支且每个stage:

cache:
  key: "$CI_JOB_STAGE/$CI_COMMIT_REF_NAME"
  untracked: true

如果使用的Windows Batch(windows批处理)来跑脚本需要用%替代$

cache:
  key: "%CI_JOB_STAGE%/%CI_COMMIT_REF_NAME%"
  untracked: true

Jobs

.gitlab-ci.yml允许指定无限量jobs。
每个jobs必须有一个唯一的名字,而且不能是上面提到的关键字。
job由一列参数来定义jobs的行为。

job_name:
  script:
    - rake spec
    - coverage
  stage: test
  only:
    - master
  except:
    - develop
  tags:
    - ruby
    - postgres
  allow_failure: true
关键词 是否必需 描述
script yes Runner执行的命令或脚本
image no 所使用的docker镜像,查阅使用docker镜像
services no 所使用的docker服务,查阅使用docker镜像
stage no 定义job stage(默认:test)
type no stage的别名(已弃用)
variables no 定义job级别的变量
only no 定义一列git分支,并为其创建job
except no 定义一列git分支,不创建job
tags no 定义一列tags,用来指定选择哪个Runner(同时Runner也要设置tags)
allow_failure no 允许job失败。失败的job不影响commit状态
when no 定义何时开始job。可以是on_success,on_failure,always或者manual
dependencies no 定义job依赖关系,这样他们就可以互相传递artifacts
cache no 定义应在后续运行之间缓存的文件列表
before_script no 重写一组在作业前执行的命令
after_script no 重写一组在作业后执行的命令
environment no 定义此作业完成部署的环境名称
coverage no 定义给定作业的代码覆盖率设置

script
script是Runner执行的yaml脚本。

job:
  script: "bundle exec rspec"

该参数也可以用数组包含多个命令:

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

这里需要注意一点,如果script命令里面包含冒号:时,script需要被包在双引号内,这样YAML解析器才可以正确解析为一个字符串,而不是一个键值对(key:value);
使用下面这些特殊字符的时候,都要注意下:

:,{,},[,],,,&,*,#,?,|,-,<,>,=,!。

stage
stage允许一组jobs进入不同的stages。
jobs在相同的stage时会parallel同时进行;
更多的用法请点击这里;

tags
tags可以从允许运行此项目的所有Runners中选择特定的Runners来执行jobs。
在注册Runner的过程中,我们可以设置Runner的标签,比如ruby,postgres,development。

tags可通过tags来指定特殊的Runners来运行jobs:

job:
  tags:
    - ruby
    - postgres

上面这个示例中,需要确保构建此job的Runner必须定义了ruby和postgres这两个tags。

allow_failure
allow_failure可以用于当你想设置一个job失败的之后并不影响后续的CI组件的时候。
失败的jobs不会影响到commit状态。

下面的这个例子中,job1和job2将会并列进行,如果job1失败了,它也不会影响进行中的下一个stage,因为这里有设置了allow_failure: true。

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

when
when is used to implement jobs that are run in case of failure or despite the failure.
翻译过来就是:用于实现在失败或失败时运行的作业。

可以设置以下值:

on_success - 只有前面stages的所有工作成功时才执行。 这是默认值。
on_failure - 当前面stages中任意一个jobs失败后执行。
always - 无论前面stages中jobs状态如何都执行。
`manual ` - 手动执行。

举个例子:

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

脚本说明:

  • 只有当build_job失败的时候才会执行`cleanup_build_job 。
  • 不管前一个job执行失败还是成功都会执行`cleanup_job 。
  • 可以从GitLab界面中手动执行deploy_jobs。

coverage
允许你配置代码覆盖率将会从该job中提取输出。
在这里正则表达式是唯一有效的值。
因此,字符串的前后必须使用/包含来表明一个正确的正则表达式规则。特殊字符串需要转义。

举个例子:

job1:
  coverage: '/Code coverage: \d+\.\d+/'

Git Strategy

GitLab 8.9中以试验性功能引入。将来的版本中可能改变或完全移除。GIT_STRATEGY要求GitLab Runner v1.7+。

可以通过设置GIT_STRATEGY用于获取最新的代码,可以再全局variables或者是在单个job的variables模块中设置。
如果没有设置,将从项目中使用默认值。

可以设置的值有:clone,fetch,和none。

clone是最慢的选项。它会从头开始克隆整个仓库,包含每一个job,以确保项目工作区是最原始的。

variables:
  GIT_STRATEGY: clone

当它重新使用项目工作区是,fetch是更快(如果不存在则返回克隆)。
git clean用于撤销上一个job做的任何改变,git fetch用于获取上一个job到现在的的commit。

variables:
  GIT_STRATEGY: fetch

none也是重新使用项目工作区,但是它会跳过所有的Git操作(包括GitLab Runner前的克隆脚本,如果存在的话)。
它主要用在操作job的artifacts(例如:deploy)。

variables:
  GIT_STRATEGY: none

Git Checout
GIT_STRATEGY设置为clonefetch时,可以使用GIT_CHECKOUT变量来指定是否应该运行git checkout
如果没有指定,它默认为true。就像GIT_STRATEGY一样,它可以设置在全局variables或者是单个job的variables中设置。

如果设置为false,Runner就会:

  • fetch - 更新仓库并在当前版本中保留工作副本,
  • clone - 克隆仓库并在默认分支中保留工作副本。

如果设置这个为true将意味着clone和fetch策略都会让Runner执行项目工作区更新到最新

variables:
  GIT_STRATEGY: clone
  GIT_CHECKOUT: false
script:
  - git checkout master
  - git merge $CI_BUILD_REF_NAME

Git Submodule Strategy
GIT_SUBMODULE_STRATEGY变量用于在构建之前拉取代码时,Git子模块是否或者如何被引入。
它的可用值有:none,normal和recursive:
1)none意味着在拉取项目代码时,子模块将不会被引入。这个是默认值.
2)normal意味着在只有顶级子模块会被引入。它相当于

git submodule sync
git submodule update --init

3)recursive意味着所有的子模块(包括子模块的子模块)都会被引入,他相当于:

git submodule sync --recursive
git submodule update --init --recursive

注意:如果想要此功能正常工作,子模块必须配置(在.gitmodules)下面中任意一个:

可访问的公共仓库http(s)地址,
在同一个GitLab服务器上有一个可访问到另外的仓库的真实地址。

Job stages attempts
正在执行的job将会按照你设置尝试次数依次执行下面的stages:

变量 描述
GET_SOURCES_ATTEMPTS 获取job源的尝试次数
ARTIFACT_DOWNLOAD_ATTEMPTS 下载artifacts的尝试次数
RESTORE_CACHE_ATTEMPTS 重建缓存的尝试次数

默认是一次尝试。

例子:

variables:
  GET_SOURCES_ATTEMPTS: 3

pages
pages是一个特殊的job,用于将静态的内容上传到GitLab,可用于为您的网站提供服务。
它有特殊的语法,因此必须满足以下两个要求:

任何静态内容必须放在public/目录下
artifacts必须定义在public/目录下

下面的这个例子是将所有文件从项目根目录移动到public/目录。
.public工作流是cp,并且它不会循环复制public/本身。

pages:
  stage: deploy
  script:
  - mkdir .public
  - cp -r * .public
  - mv .public public
  artifacts:
    paths:
    - public
  only:
  - master

Runner 定义的变量

上面在介绍variable的时候,有提及到Runner也有自己定义的变量,如下:

变量 gitlab版本 runner 描述
CI all 0.4 标记这个 job 是在 CI 环境执行的
CI_COMMIT_REF_NAME 9.0 all 构建项目的 branch 或 tag 名称
CI_COMMIT_REF_SLUG 9.0 all $CI_COMMIT_REF_NAME小写,除了0-9和a-z,替换为-
CI_COMMIT_SHA 9.0 all 构建项目的 commit SHA 值
CI_COMMIT_TAG 9.0 0.5 构建项目的 commit 的 tag 名,只有在构建 tags 时才会使用。
CI_COMMIT_MESSAGE 10.8 all 完整的提交消息。
CI_COMMIT_TITLE 10.8 all 提交的标题——消息的完整第一行
CI_COMMIT_DESCRIPTION 10.8 all 对提交的描述
CI_CONFIG_PATH 9.4 0.5 CI 配置文件路径,默认值是 .gitlab-ci.yml
CI_DEBUG_TRACE all 1.7 是否启用 debug tracing 跟踪调试功能
CI_DEPLOY_USER 10.8 all GitLab部署令牌的身份验证用户名
CI_DEPLOY_PASSWORD 10.8 all GitLab部署令牌的身份验证密码
CI_DISPOSABLE_ENVIRONMENT all 10.1 标记job 是否运行在一次性环境中
CI_ENVIRONMENT_NAME 8.15 all 执行 job 的环境名,如 produciton,dev,pre-production 等
CI_ENVIRONMENT_SLUG 8.15 all 环境名称的简化版本,适用于DNS、url、Kubernetes标签等。
CI_ENVIRONMENT_URL 9.3 all 执行 job 的环境 URL
CI_JOB_ID 9.0 all 当前 job 在 GitLab CI 内部的唯一 ID
CI_JOB_MANUAL 8.12 all 标识该 job 是要手动开始的
CI_JOB_NAME 9.0 0.5 在 .gitlab-ci.yml 中定义的 job 名称
CI_JOB_STAGE 9.0 0.5 在 .gitlab-ci.yml 中定义的 stage 名称
CI_JOB_TOKEN 9.0 1.2 用于在GitLab容器注册表进行身份验证的令牌
CI_JOB_URL 11.0 0.5 工作的URL
CI_REPOSITORY_URL 9.0 all 克隆 Git repository 的 URL
CI_RUNNER_DESCRIPTION 8.10 0.5 保存在 GitLab 中的 runner 描述
CI_RUNNER_ID 8.10 0.5 正在运行的 runner 的唯一 ID
CI_RUNNER_TAGS 8.10 0.5 正在运行的 runner tags (标签)
CI_RUNNER_VERSION all 10.6 正在执行当前 job 的 GitLab Runner 版本
CI_RUNNER_REVISION all 10.6 正在执行当前 job 的 GitLab Runner 修订版本
CI_RUNNER_EXECUTABLE_ARCH all 10.6 GitLab GitLab Runner 可执行程序的 OS/架构
CI_PIPELINE_ID 8.10 0.5 当前 pipeline 在 GitLab CI 内部的唯一 ID
CI_PIPELINE_TRIGGERED all all 标记这个 job 是 triggered 由触发器触发的
CI_PIPELINE_SOURCE 10.0 all 标明如何触发 pipeline 。 可用的选项有: push, web, trigger, schedule, api, and pipeline
CI_PROJECT_DIR all all 克隆下来的仓库的完整路径,以及 job 在哪个目录运行
CI_PROJECT_ID all all 当前项目在 GitLab CI 内部的唯一 ID
CI_PROJECT_NAME 8.10 0.5 当前正在构建的项目名称(实际上是项目文件夹名)
CI_PROJECT_NAMESPACE 8.10 0.5 当前正在构建的项目命名空间 (用户名或groupname)
CI_PROJECT_PATH 8.10 0.5 完整的项目路径,即命名空间+项目名
CI_PROJECT_PATH_SLUG 9.3 all $CI_PROJECT_PATH 小写,除了0-9和a-z,替换为-
CI_PIPELINE_URL 11.0 0.5 Pipeline 详细URL
CI_PROJECT_URL 8.10 0.5 访问项目的HTTP地址
CI_PROJECT_VISIBILITY 10.3 all 项目可见性(内部、私人、公共)
CI_REGISTRY 8.10 0.5 如果启用了注册中心,它将返回GitLab的注册表的地址
CI_REGISTRY_IMAGE 8.10 0.5 如果为项目启用了注册表,那么它将返回与特定项目相关联的登记处地址
CI_REGISTRY_PASSWORD 9.0 all 用来将容器推到GitLab容器注册表的密码
CI_REGISTRY_USER 9.0 all 用来将容器推到GitLab容器注册表的用户名
CI_SERVER all all 标记CI环境中执行
CI_SERVER_NAME all all 用于协调作业的CI服务器的名称
CI_SERVER_REVISION all all 用于安排工作的GitLab修订
CI_SERVER_VERSION all all 用于安排工作的GitLab版本
CI_SHARED_ENVIRONMENT all 10.1 标记工作是在共享环境中执行的
GET_SOURCES_ATTEMPTS 8.15 1.9 试图获取运行作业的资源的次数
GITLAB_CI all all 在GitLab CI环境中执行该作业
GITLAB_USER_EMAIL 8.12 all 开始工作的用户的电子邮件
GITLAB_USER_ID 8.12 all 开始工作的用户的id
GITLAB_USER_LOGIN 10.0 all 启动该工作的用户的登录用户名
GITLAB_USER_NAME 10.0 all 启动工作的用户的真实姓名
RESTORE_CACHE_ATTEMPTS 8.15 1.9 试图休息的次数

因为没有汉化版,因此都是拿到网上翻译的,大致了解就行,详细信息,可以看官网的介绍:
https://docs.gitlab.com/ce/ci/variables/README.html

不过从上面runner自己定义的变量,是真的有很多很实用的变量,之前写脚本都是没有用变量,自己造轮子的~

小结

本文主要介绍了.gitlab-ci.yml一些常用的指令,比较常用的就是jobs,stages跟runner自己定义的变量,其他的可以先了解;

就介绍到这里吧,主要介绍常用的,剩下的,可以去官网了解下:
https://docs.gitlab.com/ce/ci/yaml/README.html

下篇文章会介绍,怎么在gitlab ci上获取每次提交的信息,并且发送到钉钉通知,这块也不难,感兴趣的同学可以想想怎么做;

谢谢大家~