Gitlab CI/CD 介绍
Gitlab CI/CD 是一款用于持续集成(CI),持续交付(CD)的工具,相似的工具有 Jenkins、Travis CI、GoCD 等。
持续集成,即 Continuous Integration, 即在源代码变更后(git push)后自动检测(code lint)、构建和进行单元测试的过程,持续集成的目标是快速确保开发人员新提交的代码是好的(少 bug),并且适合在代码库中进一步使用。
持续交付,即 Continuous Delivery, 通常是指整个流程链(管道),它自动监测源代码变更并通过构建、测试、打包和相关操作运行它们以生成可部署的版本(可以是 apk 打包,也可以是网站部署),基本上没有任何人为干预。它包括持续集成,持续测试(保证代码质量),持续部署(自动发布版本,供用户使用)。
Gitlab 的 CI/CD 算是比较简单的了,只需要依靠一份".gitlab-ci.yml",将该文件随代码上传,Gitlab 就会自动执行相应的任务,从而实现 CI/CD。
Gitlab 安装
要使用 Gitlab 的 CI/CD 功能,一般有以下几种选择:
- 第一,自己搭一套 Gitlab,再加上部署自己的 Runner(即实际执行代码的机器),推荐 docker 部署
- 第二,使用公司 / 官方的 Gitlab,且使用自带的 Runner,即 shared Runner, 见下图。
Gitlab-ci 编写
.gitlab-ci.yml遵循 YAML 文件的语法,这份文件记录了你想要执行的各种指令,这些指令可以用来对你的代码进行规范检查(例如 PEP8)、自动打包(例如 Android 自动打包)、自动部署等。对于新手,如果不知道自己写的
.gitlab-ci.yml是否有错误,可以通过 Gitlab 自带的CI Lint进行检查。本文使用版本:
GitLab Community Edition 12.5.6-ee官方文档
image
该关键字指定一个任务(job)所使用的 docker 镜像,例如
image: python:latest使用 Python 的最新镜像。镜像下载的策略:
- never: 当使用这个策略,会禁止 Gitlab Runner 从 Docker hub 或者其他地方下拉镜像,只能使用自己手动下拉的镜像
- if-not-present: 当使用这个策略,Runner 会先检测本地是否有镜像,有的话使用该镜像,如果没有再去下拉。这个策略如果再配合定期删除镜像,就能达到比较好的效果。
- always: 这个是 gitlab-ci 默认使用的策略,即每一次都是重新下拉镜像,导致的结果就是比较耗时间
services
该关键字指向其他 Docker 镜像,这些镜像会与 image 关键字指定的镜像绑定(link)。比如可以绑定一个 mysql 服务,存储单元测试所需要的假数据。
plain text_snippet
Plain Text · 8 lines|
| default_job: |
| image: python:3.6 |
| services: |
| - mysql:5.7 |
| before_script: |
| - apt-get install mysql-client |
before_script 和 after_script
before_script 关键字定义了一组在每个任务开始之前需要执行的命令,after_script 则相反。例如可以在 before_script 做好 ssh 连接的准备,见下文。
注:before_script 可以针对全部任务,也可以针对单个任务。
Gitlab-CI 实现 SSH 连接
1.生成 SSH 需要的公私钥 2.Gitlab-CI 文件编写如下:
plain text_snippet
Plain Text · 22 lines|
| image: python:3.6 |
| before_script: |
| # which ssh-agent 用于寻找ssh-agent是否存在, |
| - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' |
| # 启动ssh-agent |
| - eval $(ssh-agent -s) |
| # 设置环境变量SSH_PRIVATE_KEY,并将私钥添加到agent store |
| - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - |
| # 修改权限 |
| - mkdir -p ~/.ssh && chmod 700 ~/.ssh |
| # 跳过SSH询问 |
| - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' |
| # 上文添加的是项目私钥,如果SSH连接成功则能够下拉项目代码 |
| ssh-test: |
| script: |
| - git clone git@git.xx |
| # 远程到其他机器并执行命令 |
| - ssh root@172.17.0.3 "cd /root && mkdir your_dir" |
| # 如果使用非默认端口 |
| - ssh -p 5555 root@172.17.0.3 "cd /root && mkdir your_dir" |
stages
前面提到,我们可以定义一系列任务(job)去执行我们想要的指令,但怎样指定先后顺序呢,这就需要用到 stages 关键字了。
stages 关键字有两个特性:1.如果两个任务对应的 stage 名相同,则这两个任务会并行运行
2.下一个 stage 关联的任务会等待上一个 stage 执行成功后才继续运行,失败则不运行
plain text_snippet
Plain Text · 29 lines|
| image: python:3.6 |
| stages: |
| - build |
| - test |
| - deploy |
| # build-job1和build-job2会并行执行 |
| build-job1: |
| stage: build |
| script: |
| - echo $PWD |
| build-job2: |
| stage: build |
| script: |
| - echo $PWD |
| # 这个任务会在build-job1和build-job2执行成功后再运行 |
| test-job: |
| stage: test |
| script: |
| - echo $PWD |
| # 注意定义的stage都需要用到 |
| deploy-job: |
| stage: deploy |
| script: |
| - echo $PWD |
效果图如下:
注:如果想要控制某一个 stage 在最开始,或者最后执行,可以使用
.pre 和 .post 关键字only / except
这两个是让我又爱有恨的字段, 没有之一。
为了更好地说明,我们还需引入一个
Pipelines的概念,Pipelines 就是我们在 Gitlab 的 UI 界面看到一行行记录:如上图,一共有三个 Pipelines,每个 Pipelines 内部就是我们定义的任务的执行情况,就是这么简单。
前面提到,通过 stages 关键字可以控制任务执行的先后顺序,而通过
only/except关键字控制的是任务的触发条件。only/except 关键字包含了一些子关键字(或者你可以理解为策略),当符合定义的策略时才会触发 Pipelines 的执行,except 则相反。
only 关键字的默认策略是['branches', 'tags'],即你提交了一个分支或者打了标签,就会触发,
除此之外,only/except 关键字还有基础特性和高级特性之分,所包含的子关键字也各不相同,建议还是仔细阅读官方文档。
only/except(basic):
- branches: 当你的 Git Refs 对应的是一个分支时触发
- tags: 当你的 Git Refs 对应的是一个标签时触发
- pushes: 当你使用
git push时触发
- web: 当你使用 Web 界面的
Run Pipeline时触发
- merge_requests: 当你创建或者更新一个 merge_requests 时触发
- ...
only/except(advanced):
- refs: 注意异或关系
- variables
- changes
- kubernetes
下面是一些示例,希望能给你带来帮助
1.当提交的分支为 master 才会触发 Pipelines
plain text_snippet
Plain Text · 24 lines|
| image: python:3.6 |
| # 第一种方式 |
| job1: |
| only: |
| - master |
| script: |
| - echo $PWD |
| # 第二种方式 |
| job2: |
| only: |
| refs: |
| - master |
| script: |
| - echo $PWD |
| # 第三种方式 |
| job3: |
| only: |
| variables: |
| - $CI_COMMIT_REF_NAME == "master" |
| script: |
| - echo $PWD |
2.当打了 tag 才会触发 Pipelines(正常的提交不会)
plain text_snippet
Plain Text · 16 lines|
| image: python:3.6 |
| # 第一种方式 |
| job1: |
| only: |
| - tags |
| script: |
| - echo $PWD |
| # 第二种方式 |
| job2: |
| only: |
| refs: |
| - tags |
| script: |
| - echo $PWD |
3.当提交的分支为 master 或者打了 tags 触发 Pipelines
plain text_snippet
Plain Text · 9 lines|
| image: python:3.6 |
| # 举这个例子是想说明only关键字之间的关系是”或“关系 |
| job1: |
| only: |
| - master |
| - tags |
| script: |
| - echo $PWD |
4.当提交的分支为 master 且打了 tags 触发 Pipelines
目前没找到办法,应该可以配合
rules关键字实现5.当使用 Web 界面的
Run Pipelines时触发(个人感觉这种方式挺有用的)plain text_snippet
Plain Text · 8 lines|
| image: python:3.6 |
| # 举这个例子是想说明only关键字之间的关系是”或“关系 |
| job1: |
| only: |
| - web |
| script: |
| - echo $PWD |
link:git refs:git-scm.com/book/zh/v2/…
正则匹配:stackoverflow.com/questions/5…
tags
该关键字指定了使用哪个 Runner(哪个机器)去执行我们的任务,注意与上文 only 关键字的 tags 进行区分。
plain text_snippet
Plain Text · 10 lines|
| image: python:3.6 |
| # 举这个例子是想说明only关键字之间的关系是”或“关系 |
| job1: |
| only: |
| - dev |
| tags: |
| - machine1 |
| script: |
| - echo $PWD |
when
前面说过,stages 关键字可以控制每个任务的执行顺序,且后一个 stage 会等待前一个 stage 执行成功后才会执行,那如果我们想要达到前一个 stage 失败了,后面的 stage 仍然能够执行的效果呢?
这时候 when 关键字就可以发挥作用了,它一共有五个值:
- on_success:只有前面 stages 的所有工作成功时才执行,这是默认值。
- on_failure:当前面 stages 中任意一个 jobs 失败后执行
- always:无论前面 stages 中 jobs 状态如何都执行
- manual:手动执行
- delayed:延迟执行
plain text_snippet
Plain Text · 37 lines|
| # 官方示例 |
| stages: |
| - build |
| - cleanup_build |
| - test |
| - deploy |
| - cleanup |
| build_job: |
| stage: build |
| script: |
| - make build |
| # 如果build_job任务失败,则会触发该任务执行 |
| cleanup_build_job: |
| stage: cleanup_build |
| script: |
| - cleanup build when failed |
| when: on_failure |
| test_job: |
| stage: test |
| script: |
| - make test |
| deploy_job: |
| stage: deploy |
| script: |
| - make deploy |
| when: manual |
| # 总是执行 |
| cleanup_job: |
| stage: cleanup |
| script: |
| - cleanup after jobs |
| when: always |
cache
该关键字指定了需要缓存的文件夹或者文件,目的是为了加快执行速度。
值得注意的是,cache 不能缓存工作路径以外的文件。比如你的用户是 yangan,项目是 helloworld,默认的工作路径就是 /build/yangan/helloworld, 如果你想缓存 /root 下的某个文件,就会出现“找不到文件”的错误。
另外,cache 并不能保证每次都能命中,而且如果缓存的文件较大,有时候反而会适得其反,导致速度变慢。
plain text_snippet
Plain Text · 14 lines|
| image: python:3.6 |
| cache: |
| # 注意是相对路径 |
| paths: |
| - cache_dir/ |
| job1: |
| script: echo $PWD |
| # 针对单个任务 |
| cache: |
| - binaries/ |
artifacts
类似 cache 关键字,也可以缓存文件或者文件夹,不同的是,这些文件可以在 Gitlab 的 UI 界面中下载,一般可用来存储 Android 打包生成的 apk。
plain text_snippet
Plain Text · 10 lines|
| image: python:3.6 |
| job1: |
| artifacts: |
| paths: |
| - binaries/ |
| # 设置过期时间 |
| expire_in: 1 week |
变量
Gitlab-CI 的变量有几种,分别为预先定义的环境变量,比如
CI_COMMIT_SHA, CI_COMMIT_MESSAGE; 通过 Web 界面 Settings/CI-CD 设置的变量,以及自己在.gitlab-ci.yml定义的变量。通过预先定义的环境变量,我们可以获取分支名,提交的消息,从而判断任务是否需要执行;而 Web 界面 Settings/CI-CD 设置的变量,则非常适合用于存储像公私钥,账号密码这些不便公开的数据。因为只有 Owner 和 Maintainer 能够查看。
如下图就定义了一个
SSH_PRIVATE_KEY来存储私钥:link:variables 关键字: docs.gitlab.com/ee/ci/varia…
Gitlab 预定义的变量: docs.gitlab.com/ce/ci/varia…
环境变量: docs.gitlab.com/ce/ci/varia…
遇到的问题
1.
!= 这个符号只在 11.11 及以上的版本中支持2.通过 API 删除 Pipelines:docs.gitlab.com/ee/api/pipe…gitlab.com/gitlab-org/…
参考
✦ 卷终 · 柯航存卷 ✦