摘要:本文总结在使用 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() { |