摘要:本文总结在使用 GitLab 进行 CI/CD 部署中遇到的问题汇总。
基础入门
随着持续集成和持续部署的理念逐渐被更多的开发者所接受,目前类似的服务和技术层出不穷。目前开源领域 Jenkins 肯定是 CI/CD 的大佬,笔者也曾使用过,最近新出的 Blue Ocean 版本更加符合现代化 CI/CD。但是通过笔者的使用,发现问题很多,比如安装 JDK 就有一个 bug,需要装两个才能在构建时显示 JDK 选择。所以有很长一段时间感觉 Jenkins 的使用肯定会遇到很多坑,所以没有进一步的研究。后来更多的是使用云端服务,比如 travis-ci 和 circle ci,travis-ci 免费版只能构建开源程序,而 circle ci 不做限制,只是在 build 时间上做了限制,但是对于少量项目的构建戳戳由于。
两个产品的体验都还不错,travis-ci 目前用来自动化部署 hexo 博客, circle ci 用来部署更复杂的一个使用 Latex 构建的 PDF。两个产品都是基于 YAML 配置文件来实现自动化部署,其实 Jenkins 同样支持文件配置,称为 Jenkinsfile。
最近开始试用 GitLab,这也是一个老牌的 Git 服务器产品,最近开始支持 CI/CD。前段时间 GitHub 被微软收购的时候,着实火了一把。通过最近的试用,发现产品做得确实很好,而且其开源社区版功能非常给力,基本覆盖了主要功能,而收费的企业版都是一些锦上添花的功能,这一点 GitLab 确实是一个良心产品。
通过 GitLab 可以一站式解决中小企业的代码托管及自动化集成部署等功能。
GitLab 同样是使用 YAML 文件来描述自动化部署的流程,文件名为 .gitlab-ci.yml。目前官方文档也很详细,有一个完整的目录(使用功能和时间索引),在此推荐几篇基础的文档,在开始使用 GitLab 之前应该整体了解。
GitLab CI/CD 基础入门篇,应该大概浏览一下,有一个整体概念。
stage 和 job 什么关系
在 GitLab 配置文件中有两个重要的概念就是 stage 和 job,stage 是部署的流程,而 job 是部署的最小运行单元。
具体关系参考该文档:GitLab PipeLine
这里使用的时候需要注意一点就是关于 stage 和 job 的命令:
- 不能使用保留字,比如 before_script
- 不管是 stage 还是 job 名字理论上都是任意,注意保留字和某些特殊符号可能不允许
常见问题
部署过程如何访问其他私有库
这个问题参考该文档:在GitLab CI/CD 过程中使用 SSH keys
实际上访问的原理还是 SSH Key,一般我们都是将我们需要授权的客户端的 SSH 的 public key 加到帐户的授权列表中,然后这个授权列表的 SSH 请求都自动授权 push 和 fetch 的权限。这里 GitLab 为了避免创建一个只用于构建的用户,而加入了 Deploy key 的功能,也就是在 GitLab 的 Deploy Keys 中加入了某个 SSH 的 public key,那么使用这个 public key 就会获取一定的权限。
目前 GitLab 支持 项目独有的 Deploy Key 和整个服务器的 Deploy Key,后者需要管理员设置,这里介绍一下,如果设置了整个服务器的 Deploy Key,如何在项目目使用。
当我们通过 Admin 管理界面加入 Deploy Key 后,这个 key 实际上并没有任何用途,我们需要将这个 key 授权给某个项目。
我们需要到项目的 Settings-> Repository -> Deploy Keys 的最下面有三个选项卡,选择对应的选项卡,enable 对应的 key,同时如果需要写入权限,还要点击编辑勾上 write access allowed。一旦开启了这个权限,在 Admin 界面就会显示对应开启写权限的项目,因为这个是一个可能的安全隐患,所以需要管理员注意。

通过官方文档发现过程较繁琐,还需要加入 know_hosts 等操作。我这里通过使用 gitlab runner 的用户进行一下 SSH 操作,之后的访问就没有问题了。首先仍然需要进入到 gitlab-runner 的用户创建 SSH key pair
1 | sudo su - gitlab |
做完这个操作,在这个 gitlab-runner 用户空间下执行的任务都会有权限访问项目。
如何获取当前 Tag
参考官方文档: GitLab CI/DI 的变量
该文档详细介绍了 GitLab 预设的一些变量,可以方便我们在自动构建和部署时候使用,由于一般我们会在有新的 tag 推送到远程后开始构建,所以 GitLab 预设了变量来获取当前的分支的 tag。
1 | CI_COMMIT_TAG |
这里说明一点,如果当前推送的是 tag, CI_COMMIT_REF_NAME 这个变量也会获取当前 tag,实际上GitLab 的 tag 也算是一种分支。但是如果推送了其他分支, CI_COMMIT_REF_NAME 将会是分支名,而 CI_COMMIT_TAG 将会为空。
如何传递 artifacts
关于 artifacts 可以参考 GitLab 的配置文件详解文档 artifacts 部分
这里主要注意在不同的 stage 和 job 之间共享 artifact 的原理和路径问题。
stage 的共享,需要首先引入依赖,这时候就自动依赖 stage 的 artifact。路径和配置的路径一致。
job 之间实际上是不共享 artifact 的,因为 job 的理念是独立和并发,如果依赖了 artifact 就应该是属于不同的 stage
但是我们可以在一个 stage 的不同的 jobs 中创建 artifact 最终该 stage 将会分享所有的 artifact,但是在最终的下载页面,将会有独立的 artifact 下载。
比如
1 | stage1: |
最终在下一个 stage 将会看到路径 xxx/yyy 和 xxx/zzz,但是对于每个 jobs 的 artifact 都会有独立的下载,并且内容不会包含关系。job2 的 artifact 不会包含 xxx/yyy
如何使用 cache 加速重复的构建
增加 cache 选项
1 | cache: |
如何使用 submodule
如果我们在一个项目需要构建的时候,会触发构建其他多个项目,可以考虑创建一个独立的构建项目使用 submodule 来引入其他项目。这一点可以参考另外一篇关于这种构建的思考。
使用 submodule 需要注意一下几点
需要配置更新
1 | # get the code from submodules |
加入如下配置,每次构建会自动 pull 对应的 submodule
submodule 可以指定 branch
1 | # add submodule to track master branch |
从 Git 1.8.2 开始支持该操作,这样在 .gitmodules 文件中会有如下内容
1 | [submodule "name"] |
submodule 也需要更新
在使用中发现,如果在更改了其他代码然后推送代码后,其submodule 并不会更新,需要我们在提交项目在每个 submodule 中做一次 pull 操作,然后在提交。
而且在部署代码过程中对代码进行了修改,这时候这些修改会被缓存起来,下一次构建还会保留这些更改,所以需要自己在结束构建的时候清理 git,或者在下一次构建的时候保证代码是 remote 最新的。
清理代码
1 | # 如果代码只是修改没有提交 |
强制使用 remote 最新的代码
1 | git reset --hard origin/master |
如何约束构建的触发条件
1 | # only build tags and triggers |
通过 only 来约束, 如果 when 设置为 manual,那么需要手动触发该 job
如何实现 job 的并发
需要配置 gitlab-run 的配置文件,参考该文档 GitLab Runner 高级配置
只需要将对应的配置文件 /etc/gitlab-runner/config.toml 改为
1 | concurrent = 4 # 大于1 |
如何执行脚本
需要首先授予脚本执行权限
1 | - chmod +x ./backend-build.sh |
执行 powershell
1 | - pwsh -File deploy-server-170-4.ps1 |
如何访问 GtiLab Rest API
参考官方 Restful API 文档
curl 在 script 的使用
在 script 使用 curl 命令需要一定的技巧,推荐使用方法来构造命令,否则需要大量的转义字符,json 字符串也很难读
1 | commande() { |
案例:使用 API 为指定 tag 添加 Release Note
1 | commande() { |