GitLab-CICD配置

Gitlab

GitLab是现在很流行的持续集成部署代码仓库,其中的部署流水线具有强大灵活的配置能力,有效帮助开发者实现一键构建测试部署操作,对于个人开发者来说也非常有用。

基本用法

这里简单介绍 GitLab-CICD 的使用方式,具体操作可以查看官网文档,非常详细,GitLab Community Edition项目为CICD的使用制作了大量的案例供学习者参考。
接下来通过下图来简要说明GitLab-CICD的构建流程:

    1. 创建项目,并按照要求在项目中加入.gitlab-ci.yml文件,编写文件内容,具体内容如何编写文章后面配置会讲到,这里只说基本流程;
    1. 然后此时需要准备一个80端口开放并且可用的Linux服务器,安装GitLab-Runner,安装完成之后运行gitlab-runner start,使用ps -ef | grep gitlab-runner查看是否运行成功,如果存在,可以进行下一步操作;
    1. 此时打开第一步创建的项目,依次点击设置–>CI/CD–>Runner,展开后可以看到手动设置specific Runner部分,这里的配置下一步需要用;
    1. 此时登录Linux服务器,执行gitlab-runner register会要求你填写信息,按照要求使用第三步的配置填写即可,这里特殊的地方是要求填写tags和选择部署方式,tags可以先随意填写,因为是学习如何构建,部署方式有很多中,这个要看具体情况,我使用shell脚本方式构建,所以选择shell;
    1. GitLab-Runner注册完成之后,再次打开第三步的页面,就可以看到此项目已激活的Runner一项,如图,如果Runner已经连接并激活,就可以看到那个绿色圆点,如果未激活,就需要等待几分钟;
    1. 等待激活之后,就可以提交代码了,此时我们还没有完成编写yaml配置文件的任务,接下来就来完成这一步;

配置文件

yaml配置文件灵活强大,功能丰富,他的最基本单元是job,任何部署集成行为都是以job为单元的,下面是job的作用以及限制:

  • job 用于定义说明 pipeline 的执行条件
  • job 中至少应该包含一个 script 配置
  • job 没有数量限制
  • 每一个 job 都要有唯一的名字

最简单案例:

1
2
example_job:
script: pm2 start index.js

yaml 文件校验:
gitlab中每个项目都有一个 Lint,用于检查 yaml 文件的正确性,位置如图:

不可用的job名关键字:

  • image
  • services
  • stages
  • types
  • before_script
  • after_scritp
  • variables
  • cache

配置项

配置 作用
script 需要交给 runner 执行的 shell 脚本
image 配置要使用的 docker 镜像
services 配置要使用的 docker services 镜像
before_script 在 job 执行之前要执行的一组命令
after_script 在 job 执行完成后要执行的一组命令
stages 定义 pipeline 中的各个阶段
stage 定义 job 的阶段,默认 test
only 允许运行 job 的限制条件(include)
except 不允许运行 job 的限制条件(exclude)
tags 用于选择 runner 的标签列表
allow_failure 让 job 处于失败状态
when 何时运行执行 job
environment job 所部署的环境名称
cache 后台部署运行时应该缓存的文件
artifacts 配置需要上载 job 中的文件或者目录
dependencies job 所依赖的其他 job,以便在他们之间传递 artifacts
needs 无序执行 job
start_in 设置 job 延迟执行的时间
coverage 设置项目的代码覆盖率
retry 设置在 job 发生故障时,重试 job 的时机和次数
parallel 可以并行运行多少个 job 实例
trigger 定义下游 pipeline 的触发器
include 引入其他 yml 文件,就像 nginx 一样
extends 设置此 job 继承的 job 配置
pages 上传 job 结果到 gitlab pages
variables 定义此 job 作用域内的变量

设置默认参数

使用 default:keyword 可以将某些配置设置为全局的 job 默认值,可以定义的配置项包括:

  • image
  • services
  • before_script
  • after_script
  • cache

案例:

1
2
3
4
5
6
7
8
9
10
11
12
default:
# 设置所有 job 的缓存默认值
cache:
key: deploy-master
paths:
- node_modules/
# 所有 job 开始前都将运行此脚本,job 内的配置将覆盖全局
before_script:
- echo "before script"
# 所有 job 结束后都将运行此脚本,job 内的配置将覆盖全局
after_script:
- echo "after script"

参数解释

  • image
    • 指定要使用的镜像
    • 子 key
      • name 应该使用的镜像全名
      • entrypoint 作为容器入口点要执行的命令或脚本
  • stages
    • 定义可被 jobs 使用的 stage,这个配置可以构建灵活的多级 pipeline
    • 阶段的顺序就是 pipeline 组执行 jobs 的顺序
    • 同一个 stage 的 job 是并行运行的
    • 下一个 stage 的 jobs 在上一个 stage 的 jobs 完成之后开始运行
    • 在执行过程中,如果某个 jobs 失败,那么整个过程都将停止
    • 如果在 yml 文件中没有定义 stages,那么默认将允许使用 build、test、deploy 三个 stage
    • 如果未给 job 配置 stage,那么默认使用 stage: test
  • only/except

    • 这两项的作用是设置 job 策略用来限制 jobs 的创建
    • only 用于定义什么分支和 tags 触发此 job
    • except 用于定义什么分支和 tags 无法触发此 job
    • 可以使用正则表达式
    • 子 key:
      • branches: 代码库分支
      • tags: 标签
      • api: 当管道由第二个管道触发时(不是 trigger API)
      • extermal: 使用 gitlab 以外的 CI 服务
      • pipelines: 多项目触发器
      • pushes: git push
      • schedules: 计划任务,可以手动设定计划任务运行 pipeline,在 CI/CD –> Schedules 中配置
      • triggers: trigger 创建的 pipeline
      • web: 在 web 上手动点击
      • merge_requests: 当创建或更新一个 merge
      • chats: 使用聊天服务器触发
    • 如果没有设置 only 规则,那么默认为 [‘branches’, ‘tags’]
    • 如果没有设置 except,默认为空
    • 子key
      • refs 用来简化子配置
      • kubernetes 当 k8s 在项目中处于活跃状态
      • variables 定义表达式,只有符合某些表达式才会运行,详细文档
      • changes 用于监控在 git push 中,只有某些文件改变时才触发
    • example:
      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
      # 只在以 issue 开头的 ref 上运行,跳过所有分支
      job:
      # use regexp
      only:
      - /^issue-.*$/
      # use special keyword
      except:
      - branches

      # 为 gitlab-org/gitlab-ce 上的所有分支运行 job
      # 除了 master 和名称前缀为 release 的分支
      job:
      only:
      - branches@gitlab-org/gitlab-ce
      except:
      - master@gitlab-org/gitlab-ce
      - /^release/.*$/@gitlab-org/gitlab-ce

      # 当提交的文件包含下面文件修改时,触发 job
      docker build:
      script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
      only:
      changes:
      - Dockerfile
      - docker/scripts/*
      - dockerfiles/**/*
      - more_scripts/*.{rb,py,sh}

      test:
      script: npm run test
      only:
      changes:
      - "*.json"
      - "**/*.sql"

      docker build service one:
      script: docker build -t my-service-one-image:$CI_COMMIT_REF_SLUG .
      only:
      refs:
      - merge_requests
      changes:
      - Dockerfile
      - service-one/**/*
  • tags

    • 从所有的 runner 中选定具有此 tags 的 runner 运行这个 job
    • 在一个机器上可以给一个项目注册多个 runner,这些 runner 有相同或者不同的 tag
    • 不同 tag 的 runner 是多机器运行同一个代码库的好方法
    • 如果没有指定 tag,那么 job 会 pull 一个 ruby2.5的镜像去跑
  • allow_failure
    • 允许 job 失败而不会影响 CI 的整体流程,默认值为 false
    • 配置为 true 后,job 将在 UI 中显示橙色警告,但是 pipeline 的逻辑流程不会被阻止
    • 此次 commit 整体会被认为是通过的,并不会因为失败项导致整体失败
  • when

    • 用于控制 job 何时运行
    • 子 key:
      • on_success 仅当前一阶段所有 job 都成功时才执行此 job
      • on_failure 仅当前一阶段至少有一个 job 失败时才执行此 job
      • always 无论前面阶段工作状态如何,都执行此 job
      • manual 手动执行 job,手动操作主要用于向生产环境部署
      • delayed 延迟作业,用于在一段时间后执行script,如果想要避免 job 立即进入 pending 状态,这非常有用
        • 如果设置为delayed,那么需要使用 start_in 配置,默认单位为秒,延迟时长不可超过一小时
          1
          2
          3
          4
          5
          timed rollout 10%:
          stage: deploy
          script: echo 'Rolling out 10% ...'
          when: delayed
          start_in: 30 minutes
  • environment

    • 用于定义 job 部署到的环境
    • 子 key
      • name 可以包含 大小写字母数字空格-_/${}
      • url 可选配置,设置后会在 gitlab 的各个位置显示按钮,点击转到指定URL
  • cache
    • 用于在 job 之间设置要缓存的文件或目录列表,只可以使用项目空间的路径,缓存可以进行全局设置
    • 子 key
      • paths 指定要缓存的路径组
      • key 由于缓存是在 job 之前共享的,所以如果不同的 job 使用不同的 paths,就需要设置不同的缓存
      • untracked 设置为 true,会缓存代码库中的未跟踪的所有文件,也就是缓存 .gitignore 中忽略的文件
      • policy
        • 缓存的默认策略是在 script 执行时置入缓存文件,执行完成后把文件再次缓存,这样 job 就可以任意修改文件,并最终再次缓存文件,这个称为 pull-push 策略
        • 如果你清楚 job 不会修改缓存文件,那么就可以使用这个配置跳过上述步骤,只是 pull 缓存即可,不需要重置缓存,设置为 policy: pull
  • artifacts/dependencies

    • 这两个配置的作用是将一个 job 的产物导入给另一个 job 供其使用,可以命名并且设置 gitlab 将其持久化多长时间
    • 也可以单纯的视作一个进行产出产物的程序,例如一个 go 语言 build 程序,build 完成之后将结果二进制文件回传给 gitlab 作为产物,然后在 pipeline 页面就能下载这个产物
      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
      build:osx:
      stage: build
      script: make build:osx
      artifacts:
      paths:
      - binaries/

      build:linux:
      stage: build
      script: make build:linux
      artifacts:
      paths:
      - binaries/

      test:osx:
      stage: test
      script: make test:osx
      dependencies:
      - build:osx

      test:linux:
      stage: test
      script: make test:linux
      dependencies:
      - build:linux

      deploy:
      stage: deploy
      script: make deploy
  • needs

    • 此配置允许实现无序执行作业,允许你实现非顺序化的 pipeline,这是你可以在不等待其他任务的情况下运行某些job,无视 stage 顺序,因此你可以实现同时运行多个阶段
    • 下面 linux:rspec、linux:rubocop 任务将会在 linux:build 任务完成后立即执行,而不会等待 mac:build 任务完成才去执行
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      linux:build:
      stage: build

      mac:build:
      stage: build

      linux:rspec:
      stage: test
      needs: [linux:build]

      linux:rubocop:
      stage: test
      needs: [linux:build]

      mac:rspec:
      stage: test
      needs: [mac:build]

      mac:rubocop:
      stage: test
      needs: [mac:build]

      production:
      stage: deploy

  • coverage

    • 配置如何从 job 的输出中提取出代码覆盖率,配置只能用正则表达式
      1
      2
      3
      job1:
      script: rspec
      coverage: '/Code coverage: \d+\.\d+/'
  • retry

    • 配置发生故障时最多重试 job 的次数,当 job 失败并配置了 retry 时,它将会被再次处理,重试直到 retry 指定的次数
    • 子 key
      • max 最大重试次数
      • when 出现何种失败时进行重试
        • always: Retry on any failure (default)
        • unknown_failure: Retry when the failure reason is unknown
        • script_failure: Retry when the script failed
        • api_failure: Retry on API failure
        • stuck_or_timeout_failure: Retry when the job got stuck or timed out
        • runner_system_failure: Retry if there was a runner system failure (e.g. setting up the job failed)
        • missing_dependency_failure: Retry if a dependency was missing
        • runner_unsupported: Retry if the runner was unsupported
          1
          2
          3
          4
          5
          6
          7
          test:
          script: rspec
          retry:
          max: 2
          when:
          - runner_system_failure
          - stuck_or_timeout_failure
  • parallel

    • 配置可以并行运行的 job 实例数量,此配置必须在 [2-50]
    • 也就是说,一个 job 指向的 tags 对应多个 runner,这些 job 会尽可能的全部并行运行,除非 runner 不够
    • 因为多个 job 是要并行运行的,所以会尽可能的均匀分配给每个 runner,就能实现一个 tag 拉起多个 runner,跑多个服务
  • trigger
    • 用于触发不同项目中的 pipeline
    • 子 key
      • project 指定被触发项目
      • branch 指定被触发项目的分支
  • include

    • 引入外部 YAML 文件,要求外部文件包含 yml 或者 yaml 扩展名
    • 子 key

      • local 引入和当前主 yml 文件在同一 branch 的本地仓库 yml 文件
      • file 使用另一个项目的 yml 文件

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        include:
        - project: 'my-group/my-project'
        ref: master
        file: '/templates/.gitlab-ci-template.yml'

        - project: 'my-group/my-project'
        ref: v1.0.0
        file: '/templates/.gitlab-ci-template.yml'

        - project: 'my-group/my-project'
        ref: 787123b47f14b552955ca2786bc9542ae66fee5b # Git SHA
        file: '/templates/.gitlab-ci-template.yml'
      • template 使用预先设置好的 yml 文件模板

        1
        2
        include:
        - template: Auto-DevOps.gitlab-ci.yml
      • remote 使用远程 yml 文件

  • extends
    • 配置此 job 所继承的 job 名称,如果重复的 key,gitlab 将会进行反向深度合并,也就是覆盖继承来的配置
  • pages

    • pages 是一个特殊的 job,用于将静态内容上传至 gitlab,静态内容必须放在 public/ 目录下,并且必须定义具有 public/ 路径配置的 artifacts
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      pages:
      stage: deploy
      script:
      - mkdir .public
      - cp -r * .public
      - mv .public public
      artifacts:
      paths:
      - public
      only:
      - master
  • variables

    • 定义 job 执行环境中可以使用的变量,可以使用整数和字符串

other

  • 在使用自己的 runner 时,默认情况一次只会运行一个 job,参考配置文档中可以进行修改
  • job 中定义的 after_script/before_script 会覆盖全局的定义
  • docker架构由基本服务(services)组成镜像,再由镜像 copy 出 container
  • 可以这样理解 stages,stages 里面定义的就是一个表格的表头
  • stage 定义的就是这一个单元格是属于哪一个表头的
  • pipeline 会按照表头的顺序,顺序的执行单元格中的 job,但是同一个表头下的 job 是并行执行的
  • 可以在 variables 配置中重写 GIT_STRATEGY 变量为 clone/fetch/none 来制定 gitlab 的获取代码策略
  • gitlab 给的默认配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    default:
    image: ruby:2.5
    services:
    - docker:dind
    cache:
    paths: [vendor/]
    before_script:
    - bundle install --path vendor/
    after_script:
    - rm -rf tmp/
  • 如果要暂时禁用 job,可以将 job 名称以点开头,此时这个 job 不会显示在 pipeline 中

  • 锚点
    • 锚点可以理解为yaml文件的配置简化策略
      1
      2
      3
      4
      5
      6
      7
      # 设置job锚点
      .test: &job_anchor
      script: echo "哈哈"

      # 引用job锚点
      example:
      <<: *job_anchor

gitlab-ci/cd 中 job 选用 runner 的规则:

  • 明确概念:
    • 首先确定的是不同项目之间的 runner 毫无关系,tag 也毫无关系
    • 一个 runner 在同一时间只能运行一个 job
    • job 会去寻找至少包含所有指定 tags 的所有 runner
    • job 具有 runner 的选择权
    • 暂时没有可用 runner 的 job 会被挂起等待直到存在可用的 runner
    • 一个 runner 可以运行多个 job,这些 job 会排队等待 runner 可用
  • 大致过程:job 首先根据自己的 tags,选出对应一组 runner,然后剔除不可用 runner,从剩余可用 runner 中随机挑选一个或多个执行 script,不断找可用 runner,直到自己需要执行的次数完成为止

案例

下面是我自己测试配置的案例截图,其实配置文件是比较简单的,重要的是理解CICD的整个流程。

.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
stages:
- deploy
- test
- build
- statistic
- rollback

default:
# 设置所有 job 的缓存默认值
cache:
key: deploy-master
paths:
- node_modules/
# 所有 job 开始前都将运行此脚本,job 内的配置将覆盖全局
before_script:
- echo "before script"
# 所有 job 结束后都将运行此脚本,job 内的配置将覆盖全局
after_script:
- echo "after script"

pages:
stage: statistic
script:
- echo $NODE_ENV_TEST
- mv view public
artifacts:
paths:
- public
only:
- master
variables:
NODE_ENV_TEST: "✨✨✨✨✨production✨✨✨✨✨"

rollback:
stage: rollback
script:
- git reset --hard HEAD~1
- npm install --production
- sh script/deploy-client.sh
- sh script/deploy-server.sh
only:
- master
tags:
- deploy-scaffold
when: manual

include:
- '/.deploy-ci.yaml'
- '/.build-ci.yml'
- '/.test-ci.yml'

.deploy-ci.yaml

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
deploy-server:
stage: deploy
script:
- sh script/deploy-server.sh
only:
- master
tags:
- deploy-scaffold
# 这个 job 的失败不会导致整个 pipeline 失败
allow_failure: true
retry: 2

deploy-client:
stage: deploy
script:
- sh script/deploy-client.sh
only:
- master
tags:
- deploy-scaffold
when: manual

deploy-example:
stage: deploy
script:
- echo "(  ̄ー ̄)人(^▽^ )击掌"
tags:
- deploy-scaffold-a
- deploy-scaffold
# 设置产物保存的时间
artifacts:
paths:
- view/
- README.md
when: always
expire_in: 1 day
when: delayed
start_in: 1 minutes
environment:
name: development
url: http://scaffold.pingvim.com
# 付费版 gitlab 才可使用
# trigger: server-monitor

.build-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
# 使用锚点实现共用 job 配置
.buile: &build_anchor
stage: build
script:
- echo "(  ̄ー ̄)人(^▽^ )击掌"
environment:
name: production
url: http://scaffold.pingvim.com

build-example-a:
# 引入锚点配置
<<: *build_anchor
# 由于这里指定了两个 tag,所以会去找至少同时存在两个 tag 的 runner
tags:
- deploy-scaffold
- deploy-scaffold-a
# 11.3中引入的继承,暂时不可用
# extends:
# - .build

build-example-b:
stage: build
script:
- echo "(  ̄ー ̄)人(^▽^ )击掌"
environment:
name: production
url: http://scaffold.pingvim.com
# 这里指定了一个 tag,所以会去找至少存在这个 tag 的 runner
tags:
- deploy-scaffold
parallel: 2
# extends: .build

build-example-c:
stage: build
script:
- echo "(  ̄ー ̄)人(^▽^ )击掌"
environment:
name: production
url: http://scaffold.pingvim.com
# 这里指定了一个 tag,所以会去找至少存在这个 tag 的 runner
tags:
- deploy-scaffold-a
needs: [deploy-example]
# extends: .build

.test-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
test-server:
stage: test
script:
- sh script/deploy-server.sh
only:
- master
tags:
- deploy-scaffold-a
# when: manual

test-client:
stage: test
script:
- sh script/deploy-client.sh
only:
- master
tags:
- deploy-scaffold-a
# 这个 job 的失败不会导致整个 pipeline 失败
allow_failure: true
retry: 2

test-example:
stage: test
script:
- echo "(  ̄ー ̄)人(^▽^ )击掌"s
environment:
name: test
url: http://scaffold.pingvim.com

◀        
        ▶