GitLab的CI/CD使用问题汇总

摘要:本文总结在使用 GitLab 进行 CI/CD 部署中遇到的问题汇总。

基础入门

随着持续集成和持续部署的理念逐渐被更多的开发者所接受,目前类似的服务和技术层出不穷。目前开源领域 Jenkins 肯定是 CI/CD 的大佬,笔者也曾使用过,最近新出的 Blue Ocean 版本更加符合现代化 CI/CD。但是通过笔者的使用,发现问题很多,比如安装 JDK 就有一个 bug,需要装两个才能在构建时显示 JDK 选择。所以有很长一段时间感觉 Jenkins 的使用肯定会遇到很多坑,所以没有进一步的研究。后来更多的是使用云端服务,比如 travis-cicircle citravis-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 文档汇总

GitLab CI/CD 基础入门篇,应该大概浏览一下,有一个整体概念。

GitLab CI/CD 配置文件详解

stage 和 job 什么关系

在 GitLab 配置文件中有两个重要的概念就是 stagejobstage 是部署的流程,而 job 是部署的最小运行单元。

具体关系参考该文档:GitLab PipeLine

这里使用的时候需要注意一点就是关于 stagejob 的命令:

  1. 不能使用保留字,比如 before_script
  2. 不管是 stage 还是 job 名字理论上都是任意,注意保留字和某些特殊符号可能不允许

常见问题

部署过程如何访问其他私有库

这个问题参考该文档:在GitLab CI/CD 过程中使用 SSH keys

实际上访问的原理还是 SSH Key,一般我们都是将我们需要授权的客户端的 SSH 的 public key 加到帐户的授权列表中,然后这个授权列表的 SSH 请求都自动授权 push 和 fetch 的权限。这里 GitLab 为了避免创建一个只用于构建的用户,而加入了 Deploy key 的功能,也就是在 GitLabDeploy Keys 中加入了某个 SSHpublic 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 界面就会显示对应开启写权限的项目,因为这个是一个可能的安全隐患,所以需要管理员注意。

enter description here

通过官方文档发现过程较繁琐,还需要加入 know_hosts 等操作。我这里通过使用 gitlab runner 的用户进行一下 SSH 操作,之后的访问就没有问题了。首先仍然需要进入到 gitlab-runner 的用户创建 SSH key pair

1
2
3
4
5
6
7
sudo su - gitlab
ssh-keygen -t rsa -C "[email protected]"
# run ssh agent
eval $(ssh-agent -s)
# add key
ssh-add ~/.ssh/id_rsa
# try to clone a git repo and add know hosts

做完这个操作,在这个 gitlab-runner 用户空间下执行的任务都会有权限访问项目。

如何获取当前 Tag

参考官方文档: GitLab CI/DI 的变量

该文档详细介绍了 GitLab 预设的一些变量,可以方便我们在自动构建和部署时候使用,由于一般我们会在有新的 tag 推送到远程后开始构建,所以 GitLab 预设了变量来获取当前的分支的 tag

1
CI_COMMIT_TAG

这里说明一点,如果当前推送的是 tagCI_COMMIT_REF_NAME 这个变量也会获取当前 tag,实际上GitLabtag 也算是一种分支。但是如果推送了其他分支, CI_COMMIT_REF_NAME 将会是分支名,而 CI_COMMIT_TAG 将会为空。

如何传递 artifacts

关于 artifacts 可以参考 GitLab 的配置文件详解文档 artifacts 部分

这里主要注意在不同的 stagejob 之间共享 artifact 的原理和路径问题。

stage 的共享,需要首先引入依赖,这时候就自动依赖 stageartifact。路径和配置的路径一致。

job 之间实际上是不共享 artifact 的,因为 job 的理念是独立和并发,如果依赖了 artifact 就应该是属于不同的 stage

但是我们可以在一个 stage 的不同的 jobs 中创建 artifact 最终该 stage 将会分享所有的 artifact,但是在最终的下载页面,将会有独立的 artifact 下载。

比如

1
2
3
4
5
6
7
stage1:
job1:
artifacts:
paths: xxx/yyy
job2:
artifacts:
paths: xxx/zzz

最终在下一个 stage 将会看到路径 xxx/yyyxxx/zzz,但是对于每个 jobsartifact 都会有独立的下载,并且内容不会包含关系。job2artifact 不会包含 xxx/yyy

如何使用 cache 加速重复的构建

增加 cache 选项

1
2
3
4
5
6
7
8
cache:
paths:
# cache maven dependencies
- .m2/repository
# cach npm dependencies
- node_modules/
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2"

如何使用 submodule

如果我们在一个项目需要构建的时候,会触发构建其他多个项目,可以考虑创建一个独立的构建项目使用 submodule 来引入其他项目。这一点可以参考另外一篇关于这种构建的思考。

使用 submodule 需要注意一下几点

需要配置更新

1
2
3
4
5
6
7
# get the code from submodules
variables:
GIT_SUBMODULE_STRATEGY: recursive
before_script:
- git config --global http.sslverify false
- git submodule sync --recursive
- git submodule update --init --recursive

加入如下配置,每次构建会自动 pull 对应的 submodule

submodule 可以指定 branch

1
2
3
4
5
# add submodule to track master branch
git submodule add -b master [URL to Git repo];

# update your submodule
git submodule update --remote

Git 1.8.2 开始支持该操作,这样在 .gitmodules 文件中会有如下内容

1
2
3
4
[submodule "name"]
path = name
url = ../../name.git
branch = master

submodule 也需要更新

在使用中发现,如果在更改了其他代码然后推送代码后,其submodule 并不会更新,需要我们在提交项目在每个 submodule 中做一次 pull 操作,然后在提交。

而且在部署代码过程中对代码进行了修改,这时候这些修改会被缓存起来,下一次构建还会保留这些更改,所以需要自己在结束构建的时候清理 git,或者在下一次构建的时候保证代码是 remote 最新的。

清理代码

1
2
3
4
# 如果代码只是修改没有提交
git reset HEAD --hard
# should keep the ignord files
git clean -fd

强制使用 remote 最新的代码

1
2
git reset --hard origin/master
git pull origin master

如何约束构建的触发条件

1
2
3
4
5
6
# only build tags and triggers
only:
- ci-test # branch
- tags
- triggers
#when: manual

通过 only 来约束, 如果 when 设置为 manual,那么需要手动触发该 job

如何实现 job 的并发

需要配置 gitlab-run 的配置文件,参考该文档 GitLab Runner 高级配置

只需要将对应的配置文件 /etc/gitlab-runner/config.toml 改为

1
concurrent = 4 # 大于1

如何执行脚本

需要首先授予脚本执行权限

1
2
- chmod +x ./backend-build.sh
- ./backend-build.sh

执行 powershell

1
2
- pwsh -File deploy-server-170-4.ps1
- cat ~/.ssh/id_rsa.pub

如何访问 GtiLab Rest API

参考官方 Restful API 文档

curl 在 script 的使用

script 使用 curl 命令需要一定的技巧,推荐使用方法来构造命令,否则需要大量的转义字符,json 字符串也很难读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
commande() {
curl -X POST \
-H "Content-Type: application/json" \
-H "Private-Token: PERSON_ACCESS_TOKEN" \
-k \
-v \
-d@- \
https://gitlab.dualshield-cloud.com/api/v4/projects/{PROJECT_ID}/repository/tags/${CI_COMMIT_TAG}/release <<EOF
{
"tag_name": "${CI_COMMIT_TAG}",
"description": "${release_note}"
}
EOF
}
# echo $commande
commande

案例:使用 API 为指定 tag 添加 Release Note

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
commande() {
curl -X POST \
-H "Content-Type: application/json" \
-H "Private-Token: PERSON_ACCESS_TOKEN" \
-k \
-v \
-d@- \
https://gitlab.dualshield-cloud.com/api/v4/projects/{PROJECT_ID}/repository/tags/${CI_COMMIT_TAG}/release <<EOF
{
"tag_name": "${CI_COMMIT_TAG}",
"description": "${release_note}"
}
EOF
}
# echo $commande
commande

部署思路