本文记录一个前端部署Gitlab的CI。不是在自己的服务器上面搭建的Gitlab。使用的是Gitlab.com的Gitlab的CI,在腾讯云撸的羊毛的小水管也搭不起Gitlab,做个CI的服务器还是能勉勉强强的。

Gitlab和Github

Github 一个网站,提供给用户空间创建git仓储,保存用户的一些数据文档或者代码等。

Gitlab 一个基于git实现的在线代码仓库软件,你可以用Gitlab自己搭建一个类似于Github一样的系统,一般用于在企业、学校等内部网络搭建Git私服。

什么是持续集成

阮一峰的这个文章写的挺好的持续集成是什么?

持续集成指的是,频繁地(一天多次)将代码集成到主干。

持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。

Gitlab的CI

从 GitLab 8.0 开始,GitLab CI 就已经集成在 GitLab 中,我们只要在项目中添加一个 .gitlab-ci.yml 文件,然后添加一个 Runner,即可进行持续集成。 而且随着 GitLab 的升级,GitLab CI 变得越来越强大。

首先明白Gitlab CI 几个基本的概念

GitLab-CI

这个是一套配合GitLab使用的持续集成系统,是GitLab自带的,也就是你装GitLab的那台服务器上就带有的。无需多考虑。.gitlab-ci.yml的脚本解析就由它来负责。

GitLab-Runner

这个是脚本执行的承载者,.gitlab-ci.yml的script部分的运行就是由runner来负责的。GitLab-CI浏览过项目里的.gitlab-ci.yml文件之后,根据里面的规则,分配到各个Runner来运行相应的脚本script。这些脚本有的是测试项目用的,有的是部署用的。

.gitlab-ci.yml

这个是在git项目的根目录下的一个文件,记录了一系列的阶段和执行规则。GitLab-CI在push后会解析它,根据里面的内容调用runner来运行。

简单来说就是,你利用Git版本管理Push了本地代码到Remote上(这里就是你gitlab.com),然后Gitlab,就通知你的服务器,也就是Gitlab-runner来运行构建任务。然后跑测试用例,测试用例通过了就生成Build出相应的环境的代码,自动部署上不同的环境服务器上面去。

安装Gitlab Runner

如果想要使用Docker Runner,则需要安装Docker。(可选)

1
curl -sSL https://get.docker.com/ | sh

安装 GitLab Runner 太简单了,按照着 官方文档 的教程来就好拉! 下面是 Debian/Ubuntu/CentOS 的安装方法,其他系统去参考官方文档:

1
2
3
4
5
6
# For Debian/Ubuntu
$ curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | sudo bash
$ sudo apt-get install gitlab-ci-multi-runner
# For CentOS
$ curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.rpm.sh | sudo bash
$ sudo yum install gitlab-ci-multi-runner

然后注册Gitlab Runner Runner需要注册到Gitlab才可以被项目所使用,一个gitlab-ci-multi-runner服务可以注册多个Runner。

在 GNU/Linux 系统上注册 Runner:

运行下面命令启动注册程序:

1
sudo gitlab-runner register

输入 GitLab 实例 URL:

1
2
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
https://gitlab.com

输入获取到的用于注册 Runner 的 token:

1
2
Please enter the gitlab-ci token for this runner
xxx //这个token在你gitlab.com的项目的 setting >> CI/CD >> Runners settings 下

输入该 Runner 的描述,稍后也可通过 GitLab’s UI 修改:

1
2
3

Please enter the gitlab-ci description for this runner
一个配置很低的服务器

给该 Runner 指派 tags, 稍后也可以在 GitLab’s UI 修改:

1
2
Please enter the gitlab-ci tags for this runner (comma separated):
tengxun // 这个就相当于每一个runner的唯一id,记住这个tag。

选择 Runner 是否接收未指定 tags 的任务(默认值:false), 稍后可以在 GitLab’s UI 修改:

1
2
Whether to run untagged jobs [true/false]:
[false]: true

选择是否为当前项目锁定该 Runner, 之后也可以在 GitLab’s UI 修改。 该功能通常用于被指定为某个项目的 Runner (默认值:true):

1
2
Whether to lock Runner to current project [true/false]:
[true]: true

选择 Runner executor:

1
2
Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker...:
docker // 我选择的docker,请记住,选择docker的之前要把docker启动,docker的使用就不是本文所讲的了

如果你选择 Docker 作为你的 executor,注册程序会让你设置一个默认的镜像, 作用于 .gitlab-ci.yml 中未指定镜像的项目

1
2
Please enter the Docker image (eg. ruby:2.1):
alpine:latest

然后,你刷新你setting下面的配置,有个绿色的小圆点,这就表明你已经注册Runner成功了。

setting

配置.gitlab-ci.yml

配置好 Runner 之后,我们要做的事情就是在项目根目录中添加 .gitlab-ci.yml 文件了。 当我们添加了 .gitlab-ci.yml 文件后,每次提交代码或者合并 MR 都会自动运行构建任务了。

在.gitlab-ci.yml有一些需要讲解的概念

Pipeline

一次 Pipeline 其实相当于一次构建任务,里面可以包含多个流程,如安装依赖、运行测试、编译、部署测试服务器、部署生产服务器等流程。我们的任何提交或者 Merge Request 的合并都可以触发 Pipeline。如下图:

1
2
3
4
5
+------------------+           +----------------+
| | trigger | |
| Commit / MR +---------->+ Pipeline |
| | | |
+------------------+ +----------------+

Stages

Stages 表示构建阶段,说白了就是上面提到的流程。 我们可以在一次 Pipeline 中定义多个 Stages,每个Stage可以完成不同的任务。 Stages有下面的特点: 所有 Stages 会按照顺序运行,即当一个 Stage 完成后,下一个 Stage 才会开始 只有当所有 Stages 完成后,该构建任务 (Pipeline) 才会成功 * 如果任何一个 Stage 失败,那么后面的 Stages 不会执行,该构建任务 (Pipeline) 失败

因此,Stages 和 Pipeline 的关系就是:

1
2
3
4
5
6
7
8
9
+--------------------------------------------------------+
| |
| Pipeline |
| |
| +-----------+ +------------+ +------------+ |
| | Stage 1 |---->| Stage 2 |----->| Stage 3 | |
| +-----------+ +------------+ +------------+ |
| |
+--------------------------------------------------------+

Jobs

Jobs 表示构建工作,表示某个 Stage 里面执行的工作。 我们可以在 Stages 里面定义多个 Jobs,这些 Jobs 会有以下特点:

  • 相同 Stage 中的 Jobs 会并行执行
  • 相同 Stage 中的 Jobs 都执行成功时,该 Stage 才会成功
  • 如果任何一个 Job 失败,那么该 Stage 失败,即该构建任务 (Pipeline) 失败

所以,Jobs 和 Stage 的关系图就是:

1
2
3
4
5
6
7
8
9
+------------------------------------------+
| |
| Stage 1 |
| |
| +---------+ +---------+ +---------+ |
| | Job 1 | | Job 2 | | Job 3 | |
| +---------+ +---------+ +---------+ |
| |
+------------------------------------------+

这些是部署脚本里面的一些基本结构,接下来说一些.gitlab-ci.yml的基本构成

下面是一个最简单的Node项目的部署脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
image: node:alpine  // 默认的ci部署的docker镜像

stages: // 首先按顺序定义有几个步骤。步骤下面的所有job是同步执行的
- test
- build

job1:
stage: test // 属于test的stage
script:
- npm run test // 这个job执行的脚本
only:
- master // 只监听master分支的代码提交
tags:
- tengxun // 要使用哪个runner

job2:
stage: build
script:
- npm run build
only:
- master
tags:
- tengxun

注意这里的job1,job2名字是随便取的,没有关系,只要不用Gitlab-CI的关键字就可以。

列出一些常用的关键字,掌握了这些就可以开发了。

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

发现这篇文章对每个关键字对解释的好清楚,算是个抛砖引玉吧。比我写的都还要清楚。

Gitlab CI yaml官方配置文件翻译

配置之前,你还要在你gitlab.com的对应项目里面的setting >> CI/CD >> Secret variables 定义一些构建变量,因为你的Gitlab-CI要去自动的登录服务器,然后把打包出来的文件自动更新上去。

Runner 登录你的服务器是不能用传统输入账号密码的形式,(因为都自动化了,还需要输入密码,那还叫什么自动化)。只能通过走SSH登录的形式,如果不懂SSH登录,请自行谷歌。私钥这些东西肯定不能写在yml文件里面,因为yml文件是每个人都能看见的,所以Gitlab-CI就可以在项目里面自定义私有的变量名。至少项目开源出去之后,不是每个人都能看见部署服务器的私钥的。

这里的$SSH_PRIVATE_KEY 是你开发机(也就是你的的开发电脑)生成的私钥。 注意一定要复制

1
2
3
-----BEGIN RSA PRIVATE

-----END RSA PRIVATE KEY-----

这些东西。(这里是个坑。。。因为很少会让你复制私钥的) 下面配置的${CI_COMMIT_REF_NAME},是Gitlab CI 的默认关键字。详情见文档

最后贴上我那个腾讯云小水管的配置,亲测有用。

还真是一个很简单的vue项目。就用vue-cli生成,然后发布到服务器上面指定的目录,服务器上面的nginx配置已经配置好了。

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
stages:
- test
- build
- deploy

cache:
key: ${CI_COMMIT_REF_NAME}
paths:
- node_modules/

test_dev:
image: node:alpine
stage: test
only:
- dev
tags:
- tengxun
script:
- npm run test


build:
image: node:alpine
stage: build
only:
- master
- dev
tags:
- tengxun
script:
- npm set registry https://registry.npm.taobao.org # 设置淘宝镜像地址
- npm install --progress=false
- npm run build
artifacts:
expire_in: 1 week
paths:
- dist

deploy_dev:
image: alpine
stage: deploy
only:
- dev
tags:
- tengxun
script:
- echo "http://mirrors.aliyun.com/alpine/v3.7/main/" > /etc/apk/repositories
- apk add --no-cache rsync openssh
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" >> ~/.ssh/id_dsa
- chmod 600 ~/.ssh/id_dsa
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
- rsync -rav --delete dist/ "$SERVER_USER_HOST:$SERVER_DEV_PATH"

deploy_master:
image: alpine
stage: deploy
only:
- master
tags:
- tengxun
script:
- echo "http://mirrors.aliyun.com/alpine/v3.7/main/" > /etc/apk/repositories
- apk add --no-cache rsync openssh
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" >> ~/.ssh/id_dsa
- chmod 600 ~/.ssh/id_dsa
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
- rsync -rav --delete dist/ "$SERVER_USER_HOST:$SERVER_MASTER_PATH"
when: manual

这里用rsync来同步Build出来的代码,其实也很简单的。

在GitLab-CI中, cache与artifacts比较容易混淆.

其中 cache 指的是缓存, 常用于依赖安装中, 如几个jobs都需要安装相同的依赖, 可以使用依赖, 此时可以加快依赖的安装进度; 对于artifacts则是将某个工件上传到GitLab提供下载或后续操作使用, 由于每个job启动时, 都会自动删除.gitignore中指定的文件, 因此对于依赖安装目录, 即可以使用cache, 也可以使用artifacts.

两个主要有以下几个区别:

  • 虽然定义了cache, 但是如果cache和.gitignore中重复的这部分, 仍然需要重新安装
  • 重新安装时因为使用的是缓存, 所以很有可能不是最新的
  • 特别是开发环境, 如果每次都希望使用最新的更新, 应当删除cache, 使用artifacts, 这样可以保证确定的更新
  • artifacts中定义的部分, 会自动生成, 并可以传到下面的job中解压使用, 避免了重复依赖安装等工作 如果使用Docker运行Gitlab-Runner, cache会生成一些临时容器, 不容易清理
  • artifacts可以设置自动过期时间, 过期自动删除
  • artifacts会先传到GitLab服务器, 然后需要时再重新下载, 所以这部分也可以在GitLab下载和浏览

因为我们这里要重复使用之前job打包出来在dist下面的文件,所以我会使用artifacts。

when: manual

这里master一般是值稳定的代码版本,所以最好手动执行的。when: manual就是需要手动部署,所有job默认都是自动执行的。

鲁迅曾经说过:有国内加速镜像地址的一定要用国内镜像地址

这里的镜像地址配置包括Docker,APK,NPM。一定要设置啊,当时就卡在这里了很久。

然后我们push代码到master,点击你项目的CI/CD面板。

然后小水管过了2分钟才跑完Build,扎心了。

点击右面的那个播放三角形,就可以部署到我的那个腾讯云服务器上面去了。(当然,dev分支是默认自动部署和的),这就开始构建了。

好啦,一个简单的前端CI尝试就完了。

that’s all。

参考资料

gitlab之gitlab-ci自动部署 GitLab 中文文档 Gitlab CI yaml官方配置文件翻译 用 GitLab CI 进行持续集成