Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
从零搭建一个高性能的前端CI服务器

从零搭建一个高性能的前端CI服务器

本系列教程介绍如何从零搭建一个前端CI服务器,以及如何优化其性能。

本系列教程均基于Gitlab CI,其它系统的酌情参考。

准备材料

在开始之前,你需要准备如下的基本材料:

  1. 一个有管理权限的Gitlab项目,可以是Gitlab.com上的,也可以是私有化Gitlab服务上的。
  2. 一台服务器,使用服务器、vps甚至自己的笔记本电脑等也可以,最好能满足一定的性能要求。2核4G以上吧。需要能访问到上面提到的Gitlab项目。

储备知识

如果能有以下方面的知识,会比较容易理解此系列教程。没有经验的话也可以,但需要通过文章中的各种外链,掌握一些相关知识。

  1. Linux系统结构与基本的Shell使用,可参考 Bash 脚本教程
  2. Docker相关知识,可参考 Docker入门教程
  3. YAML文件格式,可参考 YAML 语言教程
  4. Gitlab相关知识

目录结构

安装Linux服务器,配置Docker服务

前言

搭建一个CI服务器,并不一定需要一个Linux操作系统,甚至有些场景中还必须使用Mac/Windows等系统,但是Linux是最常用的。所以这个教程也会以Linux作为载体去讲解整个搭建流程。

如果已经有现成的Linux服务器了,或者已经有可用的Docker服务了,视各自的情况,可跳过这节教程。

安装Linux操作系统

选择发行版

Linux是一个开源的操作系统内核,我们常说的Linux操作系统,是GNU/Linux的各种发行版。它的发行版非常多,比如说有 Debian/Ubuntu系列,RedHat/CentOS/Fedora系列等,甚至Windows 10中内置的WSL也算。

因为Debian/Ubuntu系列和RedHat/CentOS/Fedora系列等都算是常用的发行版本,所以后面介绍的时候会尝试将二者都覆盖到,不过主要还是基于Ubuntu系统来讲。

下载安装镜像(ISO文件)

Ubuntu: https://ubuntu.com/download/server

CentOS: https://www.centos.org/download/

选择一个自己喜欢的发行版,一般下载最新的稳定版即可。

刻录USB安装盘

需要有一个U盘或移动硬盘,使用一些USB启动记录工具制作即可。

可以考虑使用Rufus、UltraISO、Unetbootin等工具。网络上较多此类教程,可参考Ubuntu官方文档:https://ubuntu.com.cn/tutorials/create-a-usb-stick-on-windows#1-overview

安装

具体安装步骤,网上也有很多。搜索"Ubuntu安装"或"CentOS安装"就会得到许多类似的结果。

举例来说,UbuntuServer-18.04 U 盘安装教程

设置软件源

主流的Linux发行版中,一般都会有软件仓库。它是一个集中的查询和下载软件包,以及解决软件依赖关系的仓库。

系统中自带的软件源地址一般是国外的,在国内访问国外的镜像时,速度一般都不太理想。所以会需要换成国内的软件源。常见的软件源有清华、网易、阿里云等。

以Ubuntu为例,更新软件源需要编辑/etc/apt/sources.list文件。写上更新后的地址。

具体的地址各大镜像源都会提供,如清华源有提供Ubuntu 镜像使用帮助

设置好软件源之后,运行sudo apt update更新仓库,apt是Debian系列的软件管理工具。

CentOS中对应于apt的命令是yum,对应的设置说明可参考:CentOS 镜像使用帮助

yum的大部分命令都会自动更新源信息,不需要主动执行apt update类似的操作。

安装Docker服务

Docker在CI中有比较大的作用,主要是以下三个方面:

  1. 隔离性,能保护host的安全。
  2. 稳定性,每次提供的都是干净的环境,不受上次CI的状态影响。
  3. 性能:活用Docker Cache,可以达到不错的优化效果。

所以在继续后面的操作之前,先安装下Docker服务。

关于Docker的安装,其官网是有文档的:Install Docker Engine on Ubuntu

这里复现下文档中的操作步骤:

删除旧的安装

如果之前没安装过,可以忽略这个步骤

$ sudo apt-get remove docker docker-engine docker.io containerd runc

添加Docker源

$ sudo apt-get update

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
    
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

安装Docker

 $ sudo apt-get update
 $ sudo apt-get install docker-ce docker-ce-cli containerd.io

如果是用的Windows或Mac,也可以使用Docker Desktop

启动Docker服务

$ sudo systemctl start docker

设置Docker源

Docker同样有一个仓库,它的官方地址是 https://hub.docker.com/,和其它常见镜像源类似,它在国内的访问速度也不佳。

为了能够正常地使用Docker源,我们需要把它设置成国内的镜像源,如网易源。

修改文件/etc/docker/daemon.json,如果没有则新建:

{
  "registry-mirrors": ["http://hub-mirror.c.163.com"]
}

测试拉取镜像

前端CI一般都会用到node相关的镜像,可以测试下拉取node的镜像,测试速度。

$ docker pull node:12-alpine

这里稍微解释下node:12-alpine的含义

docker pull 用来拉取镜像,冒号前面的是镜像名称,后面的是版本。如果不提供版本,默认为latest。这个方式和npm dist-tag很像。

下面是https://hub.docker.com/中node的版本列表:

node版本列表

虽然版本tag是随便定义的,但是也大概能看出维护node Docker镜像的团队,采用的命名方式为:<版本号>-<操作系统>的方式。12-alpine的含义即为 Node.js v12的最新版本 + alpine 操作系统。alpine是一个极简的Linux操作系统镜像,体积比较小。

如果能拉取成功,则说明Docker已经配置成功,本节就结束了。

下节我们讲下Gitlab CI的机制,以及基本的.gitlab-ci.yml的配置语法。

Gitlab CI的机制及配置文件写法

gitlab-ci-infograph

图片来自https://www.onyxpoint.com/account-level-ci-access-management-with-gitlab-setuid-runners/

Gitlab CI的基本原理

机制

对于Gitlab仓库中的每一次Push(注意不是每一次Commit),Gitlab系统都会尝试去执行CI。

它会做如下的判断:

  1. 当前分支中有无 .gitlab-ci.yml 配置文件?有则继续,无则退出
  2. .gitlab-ci.yml中有无命名当前分支的job,或者提交信息中有无 skip-ci?有则继续,无则退出
  3. 创建pipeline,通知Gitlab Runner
  4. .gitlab-ci.yml中有无语法错误?有则显示 invalid-yaml并退出,无则继续
  5. 根据job中配置的runner tag,可否找到匹配的Runner?有则继续,无则显示 stuck
  6. 通知Runner执行任务,并获取结果

流程图

image-20201231143017218

Runner机制

不同于Travis CI等公网的CI服务,自建的Gitlab CI更类似于Jenkins,需要自己提供CI/CD服务运行环境。

它可以是一个Linux服务器,也可以是一个PC机,或者一个笔记本电脑,甚至一个树莓派。只要能运行gitlab提供的Runner程序,即可注册到Gitlab项目中,成为一个Runner。

Runner的注册机制

每个Gitlab项目,都会有一个自己的Gitlab CI设置Token。在你运行起gitlab-runner之后,可使用它的register命令将自身注册到Gitlab中,需要提供的参数有gitlab host地址,项目token,runner名称,tag列表等。

每个Runner注册之后,都会出现在Gitlab CI的Runner列表中,并带有如下的属性:

image-20201231135403848

这些属性中比较重要的就是Tags了,它规定了这个Runner能执行哪些任务。

Runner的Tags

每个Gitlab Runner,都可以配置一系列的tags,当然也可以只配置一个。

它与.gitlab-ci.yml文件中,job的tag相对应。一个job指定了其tag之后,Gitlab就会尝试为其寻找一含有可用tag的空闲Runner,如果有可用Runner且符合一定的其它条件,就会使用指定的Runner运行这个CI任务。

这个对应关系是Job维度的,而不是Pipeline维度。所以一个Pipeline很可能会利用到多个不同的runner。

.gitlab-ci.yml文件的写法

讲完了Gitlab CI的大概运行机制,下面再来说下它的配置文件写法。

这个文件是Gitlab CI的核心配置文件,存储在代码仓库中,文件采用YAML文件格式,可参考YAML 语言教程

先看下它的基本写法:

stages:
  - build
  - test

windows job:
  stage:
    - build
  tags:
    - windows
  script:
    - echo Hello, %USERNAME%!

osx job:
  stage:
    - build
  tags:
    - osx
  script:
    - echo "Hello, $USER!"

test job:
  stage:
    - test
  tags:
    - test
  script:
    - echo "Test"

从示例中可以看出,它的最基本写法是先有stages,划定有哪些执行阶段。

Pipeline graph

每个stage,对应于图中的一个列。

然后下面是job列表,windows job,osx job以及test job等都是可随意起的名字,绕开保留关键字,以及不要重名即可。

每个job中都可以配置stagetagsscript参数,分别用于指定其执行阶段,需要使用的Runner选择器、需要执行的命令等。

script需要与runner配套,比如说要运行Shell脚本,那多半要选择Linux环境,要使用nodejs,则最好选一个预置好Node.js的运行时。

Gitlab官方也有关于.gitlab-ci.yml的入门教程:Get started with GitLab CI/CD

另外也有官方提供的完全手册GitLab CI/CD pipeline configuration reference,注意里面的各项配置有区分版本,部分配置只在新版本中可用。

安装和配置Gitlab Runner

前言

上节中提到了Gitlab CI执行过程中需要寻找匹配的Runner,这里我们就动手配置下Gitlab Runner。

在开始之前,假设你已经有了:

  1. 一台Linux服务器,已经安装好Docker服务。
  2. 一个有管理权限的Gitlab工程。

如果英文水平不错的话,可参考官方文档:

  1. Install GitLab Runner
  2. Configuring runners in GitLab

下面我讲下我安装和配置Gitlab Runner时的过程:

安装Gitlab Runner

选择安装方式

安装方式有多种,比如使用deb包、rpm包,或者直接下载二进制文件。

为了简便,我用的直接下载二进制包的做法。

一般现在用的都是x64的机器,所以直接下载amd64版本的可执行文件即可。如果是其他架构的,可对应选择其它的版本。

amd64版本的gitlab-runner下载地址为:https://s3.amazonaws.com/gitlab-runner-downloads/master/binaries/gitlab-runner-linux-amd64

可选的其它版本还有:

下载下来之后,会得到一个gitlab-runner-linux-amd64的文件,为它添加可执行权限,并将其移动到一个系统目录中:

chmod a+x ./gitlab-runner-linux-amd64
sudo mv ./gitlab-runner-linux-amd64 /usr/local/bin/gitlab-runner

# 添加gitlab runner用户
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash

# 安装
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner

# 启动
sudo gitlab-runner start

之后就可以运行gitlab-runner来测试下效果了。

配置Gitlab Runner

注册Runner到Gitlab项目中

Gitlab服务器与Runner之间的通信,是通过Runner建立的长连接。首先Runner将自身注册进Gitlab服务器中,填好相应的配置,然后在它启动时,会主动地与Gitlab服务器建立连接。Gitlab后续派发任务时,就是通过这个长连接进行。

这个模式的好处是不需要Runner有公网地址,因为它是作为一个客户端的角色存在的,而不是一个服务端。

这个模式会要求Runner将自身的信息注册给Gitlab服务器,下面我们就讲下注册的过程。

打开Gitlab项目控制端,找到CI/CD。

打开项目中的"Settings->CI/CD",一般对应的URL是 <Git服务地址>/<组或用户名>/<项目名>/settings/ci_cd。会看到一个如下的页面:

image-20210104105717232

左侧部分,给出了注册的流程,右侧部分是共享的runner,一般由git管理员指定,设定一个或多个通用的Runner,共享给不同的项目,这里可以忽略。

记住这个图里面的2、3两个步骤中的URL和Token,后面会用到。

在服务器中注册信息

继续回到刚安装好 gitlab-runner 的服务器上,运行下面的命令:

sudo gitlab-runner register

image-20210104111852520

这时会出现一个提示框Please enter the gitlab-ci coordinator URL,输入上面复制的Git服务器地址,回车即可。

接下来会提示Please enter the gitlab-ci token for this runner:,输入上面复制的Token,回车即可。

接下来就会询问名称Please enter the gitlab-ci description for this runner,随便起一个名字或直接回车均可。

再接下来会提示Please enter the gitlab-ci tags for this runner (comma separated):,这里要输入tag列表,英文逗号分隔,如 build,test,deploy 或 foo,bar等均随意定义即可。这里的tag列表要和.gitlab-ci.yml文件中的tags保持匹配,后面也可以改,这里不确定的话,随便填一两个就行了。

再接下来会让选择runner执行器的类型Please enter the executor: docker, docker-ssh, ssh, docker-ssh+machine, kubernetes, custom, parallels, shell, virtualbox, docker+machine:,可选值就是上面列出的这些。

关于各种executor的区别,可查看官方文档:Executors

简单来说,最常用的executor是docker和shell。它俩的区别是docker类型依赖docker服务,shell直接用服务器当前系统。docker类型的好处是比较安全,shell类型的不安全,相当于在服务器上开了后门,.gitlab-ci.yml中就算配置了rm -rf,也会照单接收,执行不误的。

这里我选择的是docker,这也是为什么一开始配置服务器那里,要安装好docker service。

选择docker之后,还会让输入基础镜像,即每次执行CI任务时,以哪个镜像作为基础镜像,这个就完全看个人需求了。比如说执行环境需要有node.js,那可以使用node镜像,需要python,可用python镜像,需要有docker,可选docker镜像等。比较常见的情况,还是自定义一个镜像,安装上自己需要的各种软件。

到这个步骤之后,Runner就注册成功了,可以在 "Settings->CI/CD"中看到它了。

image-20210104113304896

配置Runner属性

Tags

在上图中,小锁按钮旁边有个编辑按钮。点击它就会进入到这个Runner的详细配置页面。

image-20210104133113393

图中的"Tags"中可以随时更改这个Runner关联的Tag。使它能匹配到不同的job。

Active

同样是在上图中,Active是个复选框,勾除它可以暂时禁用这个Runner。

其它比较重要的属性,就需要在运行Runner的服务器上,修改它的配置文件了。

配置文件路径一般为/etc/gitlab-runner/config.toml,访问它需要提升权限,示例内容为:

concurrent = 3
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "foo"
  limit = 1
  url = "http://git.mycompnay.com/"
  token = "af95bc59242a89b4efe24c66332a57"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.docker]
    tls_verify = false
    image = "docker"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
    shm_size = 0
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

[[runners]]
  name = "bar"
  limit = 1
  url = "http://git.mycompany.com/"
  token = "eeee0ae4851d71fda2fb9d4354fff6"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.docker]
    tls_verify = false
    image = "docker"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
    shm_size = 0
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

这里面的关键属性解释一下:

  1. concurrent是并发数量,代表能并发运行的最大job数量。一般需要限制concurrent的场景,是为了限制资源占用率、或为了保证顺序性。它是一个全局控制。
  2. [[runners]] 每运行一次gitlab-runner register并成功注册,就会在[[runners]]中生成一项记录。代表此runner的配置。
  3. limit是runner内部的并发限制,只有同时满足limit限制和全局concurrent限制时,才能够分配资源给job。
  4. executor、url、token等是注册时就配置的信息,不再赘述。token是处理过的token,和gitlab项目中显示的token不是同一个。
  5. [runners.docker] 当executor是docker的时候,这项配置会生效,代表对Docker runner的配置
  6. [runners.docker] 中的 image 属性是基础镜像
  7. [runners.docker] 中的volumes可用来挂载运行时volume,默认只有"/cache",多加了一个"/var/run/docker.sock"是为了能够在Docker容器内部再继续调用Docker服务。

关于更高级的配置细节,检阅Gitlab的官方文档:https://docs.gitlab.com/runner/configuration/advanced-configuration.html

前端CI中的性能优化

前言

前端CI中的一个很难绕过的问题,是node_modules的处理。现在的前端生态建立在npm之上,而npm有黑洞之称,相信这个图大家都不陌生。

npm 黑洞

当然除了node_modules以外,还会有一些其它的性能瓶颈,这些问题都会逐渐地吞噬掉CI的时间,使的一次pipeline的时间越来越久,阻塞开发部署等流程。

常见的性能问题

在实际工作中,我遇到过一些影响性能的问题,这里和大家分享一下。

  1. node_modules的安装问题
  2. docker镜像拉取速度问题
  3. gitlab cache加载和保存缓慢问题
  4. runner性能跑满,服务器压力过高,导致整体变慢问题
  5. eslint等执行过慢问题
  6. 磁盘读写过慢问题
  7. 磁盘占用过多问题

常见的解决方案

一句话总结:缓存解万愁(当然缓存也带来新的烦恼,此处暂且不提)。

具体的策略包括但不限于如下范围:

  1. Gitlab CI中的Cache机制
  2. 镜像加速
  3. Gitlab Runner中docker executor的缓存
  4. Docker cache
  5. Yarn、npm 等cache
  6. ESLint Cache(针对eslint过慢的问题)
  7. BCache(混合SSD加速)
  8. 定期清理磁盘空间

实例分享与原理剖析

node_modules的优化

node_modules的体积比较大,涉及到的磁盘I/O和网络I/O比较多,再加上国内访问npm的速度不佳,很容易成为性能瓶颈。

对它的优化主要是如下几种思路:

  1. 最彻底的,能不安装就不安装。可使用基础镜像全局安装、Docker Cache等手段
  2. 次之的,在不得不安装node_modules的时候,缓存上一次的node_modules,或使用npm cache、yarn cache等。这种方式一般会用到 Gitlab CI中的cache机制
  3. 最后,切换镜像到一个国内镜像,也有助于加速。

先说尽量不安装node_modules的方法

全局安装

假定有一个工程,CI任务中只需要处理npm发包。那有可能node_modules中只有 typescript之类的工具。

如果是这种场景的话,可以考虑全局安装typescript等工具,代替项目中安装。

从原来的

FROM node:12-alpine
WORKDIR /workspace

ADD . /workspace
RUN npm i && npm run build && publish

转换成:

FROM node:12-alpine
WORKDIR /workspace
RUN npm i -g typescript

ADD . /workspace
RUN npm run build && publish

这样就省去了 npm i 这个过程,同时因为RUN npm i -g typescript这个步骤比较容易命中Docker cache,所以大多数时候并不会重复安装依赖。

全局安装的方法,很多时候还是避免不了安装node_modules,因为有可能用到一些更复杂的,必须随项目安装的依赖,比如一个需要webpack构建之后,部署构建产物的工程,也就是常见的前端业务工程,基本都实现不了全部用全局包。

这个时候就用到了依赖前置。

依赖前置

依赖前置是为了利用Docker Cache而对Dockerfile进行的一个改进。

修改之后的Dockerfile是这种样子:

FROM node:12-alpine
WORKDIR /workspace
RUN mkdir -p /workspace

COPY ./package.json ./package-lock.json /workspace
RUN npm i

ADD . /workspace
RUN npm run build && publish

它和上面的Dockerfile相比,主要的改动是把 npm install 放在了 ADD . /workspace的前面。

按照Docker cache的原理,上面的layer如果没变,下面的RUN命令就会直接使用缓存。如果package.jsonpackage-lock.json文件没有变化,就会命中缓存,如果这两个文件中任何一个有变化,则无法命中缓存,会重新执行安装。

在大多数场景中,发生变化的是代码,而非package.json等工程配置文件。因此ADD . /workspace之后基本上无法命中缓存,而COPY ./package.json ./package-lock.json /workspace之后则大概率能命中缓存。

以上两种都是避免安装node_modules实现加速,下面介绍下其它的方案:

Gitlab CI Cache

在有yarn cache或npm cache时,安装node_modules会比全新安装快不少,因为有不少的资源可以从本地获取了。

Gitlab CI中提供了一个Cache机制,可以指定要缓存的路径,在下次执行时先加载缓存,后执行任务。

比如说一个常见的CI配置:

cache: &global_cache
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/
    - public/
    - vendor/
  policy: pull-push

job:
  cache:
    # inherit all global cache settings
    <<: *global_cache
    # override the policy
    policy: pull

具体情况,可直接查阅官方文档https://docs.gitlab.com/ee/ci/caching/

在使用Gitlab CI Cache的时候需要注意,对于I/O压力比较高的服务器来说,CI Cache可能也会消耗较长的时间。

切换国内镜像

这一部分资源很多,此处不再赘述。可考虑使用nrm切换到taoabao等镜像源。

ESLint的优化

之前写过一篇博客专门讲这个:传送门

Gitlab Runner优化

Gitlab Runner中可以配置一些优化,我尝试过的主要是如下的方面:

  1. 通过限制资源和并发数,避免CI服务器限入激烈的资源竞争。
  2. 优先使用本地docker镜像,避免因为docker hub服务慢拖慢Runner

限制并发数

Gitlab Runner中可以设置并发数的上限,既有服务级别的,也有Job级别的。

示例配置:

concurrent = 7
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "hello"
  limit = 2
  url = "http://git.mycompany.com/"
  token = "mytoken"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.docker]
    tls_verify = false
    image = "docker"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
    pull_policy = "if-not-present"
    shm_size = 0

如上的示例代码中,concurrent = 7代表此Runner服务最多同时跑7个Job。而limit = 2则代表hello这个job最多同时跑两个。

限制资源

限制资源是通过Docker提供的限制资源的方式,会需要较新的Docker服务版本。可以用来限制CPU、GPU、内存等资源。

如限制Docker run过程中可用的cpu资源:

docker run --cpus="1" xxx

详细使用方法参考官方文档:Runtime options with Memory, CPUs, and GPUs

优先使用本地镜像

Runner服务器的 /etc/gitlab-runner/config.toml文件中,有关于docker executor的配置,在[[runners.docker]]中,默认是始终拉最新的,可以改成优先使用本地的:

  [runners.docker]
    pull_policy = "if-not-present"

配置成if-not-present之后,如果本地有此镜像,就不会再重新查询docker hub。

磁盘的优化

CI服务器需要的磁盘空间一般都比较大,尤其是在需要npm install的时候。所以有可能服务器中使用的是机械硬盘,而非固态。而读写速度对CI的影响会比较大。

在有些时候,有可能能有一些比较小的固态硬盘,CI存储全存在上面不太现实,但可以用来做混合SSD,实现I/O方面的优化。

Linux中可以使用BCache工具制作混合SSD,参考我之前写的博文:Linux中使用小容量SSD制作混合SSD硬盘

定期清理磁盘空间

磁盘过满的时候,也会影响性能,当磁盘空间不足时,甚至会使CI服务直接停止工作。所以需要加一些清理脚本。

在使用Docker executor的时候,磁盘的占用主要是已经不再使用的Docker image,以及一些Docker volume。

要定期清理它们,可以使用下面的命令:

#!/bin/bash

# 清理镜像,保留一天
docker system prune -f --filter "until=$((1*24))h"

# 清理volume,保留三天
docker volume prune -f --filter "until=$((3*24))h"

把这个脚本随便起个名字,加上可执行权限,然后复制到/etc/cron.daily/etc/cron.hourly等目录中,实现每天清理一次或每小时清理一次的效果。

以上。

Gitlab CI的精细化控制

当CI复杂到一定程度之后,就会衍生出各种各样的细致的控制需求。Gitlab CI默认的控制器能实现一定程度的控制。

失败时继续执行后续任务

Gitlab CI中默认情况下,如果上个阶段中有任务未成功执行,则下个阶段会被取消。这是因为它默认配置了 when: on_success 的条件,只有当前面的任务完成时,才会执行后续任务。

如果想接受一个任务失败,不阻塞后续任务,有两个方式能实现:

  1. 设置此任务的属性,allow_failure: true 参考allow_failure说明
  2. 设置此任务后续任务的属性,when: always 参考when的说明

类似地,还可以设置任务失败后的后续处理等。

自动取消过时的pipeline

当提交比较频繁时,可能会产生多个pipeline排队的情况。如果每次pipeline都是可以替代之前的pipeline的,那只有最新的commit需要执行pipeline,其余的已经没有意义了。

这种情况下,可以设置Gitlab自动取消过时的pipeline。

在"Settings->CI/CD"中,找到"Auto-cancel redundant, pending pipelines",勾选上它就可以了。

失败后自动重试

Gitlab 中提供了retry选项,可以设置任务失败后自动重试,最多重试两次,即总共3次。

且失败的时候,可以限制重试的原因。示例:

test:
  script: rspec
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

具体的用法可参考官方文档:retry

自动取消过时的Job

Gitlab v12.3之后提供了新的控制选项:interruptible,相较于Gitlab仓库中设置的"Auto-cancel redundant, pending pipelines",它的控制维度更细,细化到了一个Job的级别。

可惜的就是依赖的版本比较高,目前还没用上过。

如果想要在旧版Gitlab中实现类似的效果,可利用webhook技术,在本文的最后部分会提到。

跳过pipeline

如果在CI中涉及到自动修改代码的场景,有可能会希望修改之后的代码不再触发CI。

这种情况下,可以在提交信息中加上[skip ci][ci skip],就可以自动跳过了。

Webhook

最后,假如有Gitlab中暂不支持的功能需求,或者用的是Gitlab旧版本,没有某个需求。可以考虑使用webhook机制,自定义控制。

关于Webhook相关的介绍,可参考Gitlab官方文档:Webhooks

它的原理,简要地说就是在Gitlab仓库的各项事件发生时,Gitlab服务器可以调用一个外部服务,将相关信息发送给它。在这个服务中,配置上Gitlab Token就可以通过API或者一些封装SDK,如nodejs中的gitbeaker,来操作Git仓库,控制它的各项内容了。

graph LR;
end1[结束];end2[结束];
开始 --> yamlExists{{".gitlab-ci.yml是否存在?"}};
yamlExists -->|否| end1;
yamlExists -->|是| jobExists{{是否有job?}};
jobExists -->|否| end2;
jobExists -->|是| createPipeline[创建pipeline];
createPipeline --> invalidYaml{{yaml语法错误?}};
invalidYaml -->|否| invalid["invalid yaml"];
invalidYaml -->|是| getRunner{{"有无匹配Runner"}};
getRunner -->|否| stuck;
getRunner -->|是| run["执行任务"];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment