Gitlab 一行配置

通过一行简单的配置即可接入CI

Posted by poos on April 30, 2022

背景

使用 gitlib 可以自动化的进行仓库检查、仓库发布、自动提交主项目、制作二进制(优化build时长)等任务。通过脚本也可以做到定时打包,配置项同步数据库等。

但是不合适的使用,也未必能给大家减轻负担。比如:仓库太多,每个仓库的 .gitlab.yml 都要手动配置?本文以此为开始

include

话不多说,了解一下include

.gitlab.yml本身支持一种include的方式,继承其他yml

也就是说,以前每个job都要自己写一套,现在好了,直接一行代码引用(呼应标题)。

除了方便,还有其他好处吗?

  • 稳定,通常的 CI 方案,每次执行任务都需要去拉取脚本仓库,那么 gitlab 不稳定时候会有大量 timeout;
  • 可修改,有的一些脚本,执行效率低,理论上结合当前我们项目的使用,可以优化执行任务;各个仓库自己使用各自.gitlab.yml配置,没法同步新功能;
  • 可配置,全局选项等;
  • 日志,可监控,随时调整;

其他仓库通过继承其他yml,无需关心其他,内部优化可走起来,大干一场了。

知识储备

yml

  • 首先储备一下 yml 的基本文档,知道一下当前安装版本的 gitlab 能够做哪些事情:

gitlab-readme

  1. 除了常见的script, stags, tags,还要知道 before_script, after_script, only, except, artifacts, variables等参数。
  2. 还有一些 include, extends 这样的层级嵌套问题。
  • 常见的参数需要知道它运行的环境,支持的参数。即可,然后绕过一些坑点即可。 only要特别注意一下支持的参数类型,因为任务通常通过 only 来执行。

gitlab-predefined_variable

where_variables_can_be_used

设置 runner

runner 比较独立,只在这里提一嘴,为了对得起标题 “一文学会配置 Gitlab yml”🤣,一些具体可以可以查看下面官方文档,或者 google 百度。

gitlab.com/runner/install/osx [gitlab.com/runner/configuration/macos_setup](https://docs.gitlab.com/runner/configuration/macos_setup.html)

安装和升级 gitlab

这块咱也没搞过,也不敢说,拉个文档吧。 gitlab.com/ee/install/

不过要是想发外网,倒是有个低成本的路子。首先得有个服务器,就搞自己家电脑上吧。 1. 搞公网ip,给联通客服打电话,2. 搞域名,一些二级域名是免费dns的,挂上自己的ip,3. 几块买个域名,转到刚才的二级域名

这块显然也不是文章重点,转到.gitlab.yml文件来吧👇。

YML

那加快一下进度,对于基础知识储备通过上面的文档备查即可,下面直接通过场景来看下如何使用:

目标

首先明确一下目标: 目标是大家不同 group 的所有仓库,能够用同一份 yml 文件,并且支持背景中的那些设想:

.gitlab.yml

1
2
3
include:
  - project: 'group/project'
    file: 'component_yml.yml'

runner 分流

针对的是牛逼plus的多台机器冗余负载。

场景是,有不同的项目在不同的组内,要根据组的不同分配相应的执行机器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
stages:
  - check
  - check-1
  
check:  
  script:
    - xx
  tags:
    - RunnerTAG1
  only:
    variables:
      - $CI_PROJECT_PATH =~ /group1/i

check-1:
  script:
    - xx
  tags:
    - RunnerTAG2
  only:
    variables:
      - $CI_PROJECT_PATH =~ /group2/i

如下,还是比较简单的,通过variables:来分配即可。值得一提的是这里的参数只能使用 Gitlab 提供的参数,因为此时 runner 没分配,所以自定义的参数并没有执行。这个还是有一定局限。

看到上面有个弊端,实际同一个执行脚本需要写两遍。那事实上我们有更多个项目组,那就是乘数层级的上升。

有注意到上面 知识储备 部分有个extend参数,这个可以解决一部分这个问题。那就一步到位在加上include参数吧,直接拆分文件即可,通过 yml.yml 即可知道需要执行的任务。

yml.yml

1
2
3
4
5
6
7
8
include: "yml2.yml"
stages:
  - check

.check:
  stage: check
  script:
    - xx

yml2.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
check:  
  extends: .check
  tags:
    - RunnerTAG1
  only:
    variables:
      - $CI_PROJECT_PATH =~ /group1/i

check-1:
  extends: .check
  tags:
    - RunnerTAG2
  only:
    variables:
      - $CI_PROJECT_PATH =~ /group2/i

坑点1,有些版本include:不支持嵌套,所以就不能使用这种方式,或者在使用时候同时include:两份文件。 坑点2,目前使用的extends:不支持嵌套。下标有一些讨论: multiple extends support https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53134 https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/26801

那,升级 gitlab之后,第二份文件会更简洁:

yml2.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.group1:
  tags:
    - RunnerTAG1
  only:
    variables:
      - $CI_PROJECT_PATH =~ /group1/i

.group2:
  tags:
    - RunnerTAG2
  only:
    variables:
      - $CI_PROJECT_PATH =~ /group2/i

check:  
  extends: 
    - .check
    - .group1

check-1:
  extends: 
    - .check
    - .group2

小小的改变之后,后续增加任务会简洁很多,所以用新版还是香的。

参数影响 job 执行

where_variables_can_be_used 理论上可以使用如下的方式,但是这样就需要定义variables: CUSTOM_VAR.gitlab.yml 配置相关参数。

因为篇幅较长,下面就以最简参数示例了:

1
2
3
4
5
6
7
variables:
  CUSTOM_VAR

check:
  only:
    variables:
      - $CUSTOM_VAR =~ /group2/i

那配置的参数基本是不统一的,所以就没发使用这样的方式来动态增加减少 job 项。

那另外一种退求其次的方法是:

1
2
3
4
5
6
before_script:
  - "export CUSTOM_VAR=`sh`"

job2:
  script:
    "if [[ $CUSTOM_VAR == true ]]; then\n  shxxx.sh;\nfi"

这部分可以追一下最新的 Gitlab,是有可能有新支持的。毕竟在 Gitlab 主服务上是可以执行一些内容的。

另外如果出发任务不能满足你的需求和科研配合 except 使用。例如默认的 only 是 [tags, branches],所以可使用如下,只在 tag 上运行。

1
2
  except:
    - branches

shell 脚本可玩性

这里简单介绍一下多行的 shell 在 gitlab的 yml 怎么写,官方文档其实有部分描述:

NOTE: Note: Sometimes, script commands will need to be wrapped in single or double quotes. For example, commands that contain a colon (:) need to be wrapped in quotes so that the YAML parser knows to interpret the whole thing as a string rather than a “key: value” pair. Be careful when using special characters: :, {, }, [, ], ,, &, *, #, ?, |, -, <, >, =, !, %, @, `.

简单来说在终端是这样写的

1
2
3
4
5
if [ -d xxx ]; then\
  cd xxx; git add .; git stash; git pull;\
else\
  git clone xxx.git xxx;\
fi

在 yml 需要这样写

1
2
3
4
5
6
7
8
"if [ -d xxx ]; then\n  cd xxx; git add .; git stash; git pull;\nelse\n  git clone git@xxx.gilab/a-shell.git xxx;\nfi"


sh '''if [ -d xxx ]; then\\
  cd xxx; git add .; git stash; git pull;\\
else\\
  git clone git@xxx.gilab/a-shell.git xxx;\\
fi'''

运行沙箱

关于job,有几个规则是默认的,即默认有几个坑点

  1. 同一个 tag 名,有两台实际机器注册。如果连续5个任务指定了这个runner tag,那么并不保证在同一台机器执行。即,负载均衡规则
  2. script, before_script 等脚本默认是 job 级别的沙箱环境。下边附加例子。
  3. 沙箱规则,运行完成会清理脚本相关文件和目录,不会带到下一个job。所以即使实际在同一台机器的相邻 job 也不能共享文件。

顺序执行 job 1, job 2,位于同一个runnner tag id机器上。 job 1 (tagid=0001)

1
2
3
4
5
6
7
8
9
before_script:
  - pwd
  # 输出结果为 xxx
  - cd A
  - pwd
  # 输出结果为 xxx/A
script:
  - pwd
  # 输出结果为 xxx/A

job 2 (tagid=0001)

1
2
3
script:
  - ls
  # 输出结果没有 A

其他参数

列举一些使用上有用的参数,基本见名知意,没有坑点就不展开了,查阅文档即可方便使用。

allow_failed dependencies retry artifacts

其他非 yml 部分

谈一谈yml之外的,跟本文有有一点点相关的。 通过分层,分仓库的方式,来实现一些定制的功能:

ymls 仓库,作为统一存储 yml 的地方

  • 存储 库统一使用的yml
  • 存储 脚本库更新本地脚本的的yml
  • 一些简单的日志打点脚本

脚本仓库,存储所有使用的脚本,

  • 引用 更新本地脚本的的yml
  • 存储各个 job 的脚本
  • 存储全局黑白名单

还有就是咱们的代码仓库了:

代码库

  • 使用 库统一使用的yml
  • 项目代码

日志

因为 gitlab 的 ci 没有一个大盘(大佬知道的纠正我),所以需要自己记录。

以前我们是没有记录的,那现在就可以把它写在统一的 yml 配置 的 before 和 after 相关脚本上。

同时在旧脚本上加上日志能作一些对比,还能发现一些问题。

关于性能清理

  • 关于打包

之前一个库,如果有十个左右的字库,完整的打包需要30分钟以上。但是我们在项目中使用是完整导入的,分子库更多的是为了代码隔离。 现在使用了打包整个库的形式,加上快速发布,基本在5分钟左右即可。

  • 关于 gitlab 频繁拉脚本仓库

上面的知识有提到 job 的沙箱特性,所以需要拉脚本。以前每个库发包就需要执行 5次左右的 git clone,新改造之后从本地复制,将在频繁打包时候减少很多连接错误。 本地的更新通过添加脚本仓库的ci,每次更新脚本时候即更新机器上相应目录的脚本。

关于全局配置文件

通过全局的黑白名单,进行不同库的定制化脚本即可。

那其实也有个好处,知道当前大家所有库的当前一些选项开关,一个小盘。

后续

那通过上面的一些措施,功能上实现了上面的规划:

  1. 全局配置问题
  2. CI 收归,支持动态更新 job 问题
  3. 个性化配置,不同仓库执行不同 job 问题
  4. CI日志问题

有些因为 gitlab 提供的能力不够大,如果后续有更好的支持的话,那就能写出更优雅的代码了。本文就结束了,重点还是在 YML 使用和对于当前的需求的一些理解上,大家多多提意见。