Gitlab CI/CD 简介
-
gitlab:一个基于git的、开源的代码仓库管理平台,类似的平台还有github、gitee这些。
-
CI:Continuous Integration 持续集成,对代码进行构建、编译、打包,得到产物 output.zip
-
CD:Continuous Delivery 持续交付,将产物 output.zip部署到测试服务器(服务端及web端)、推送到内部的移动app安装平台(移动端),以供测试、验收。
-
CD:Continuous Deployment 持续部署,将产物使用k8s部署到容器,上线预发布、正式环境。
个人理解:CI/CD是一种软件持续性开发的方法论。gitlab-ci是基于该方法论实现的一个工具,它使得软件工程师可以不断的对新代码进行构建、测试和部署。这样可以有助于提前发现问题,减少基于错误或失败的版本进行开发新代码的可能性,尤其是对于多人协同开发的大型项目,CI/CD显得尤为重要。使用这种方法,从新代码开发到部署,可以减少人工干预甚至不用干预。
关于gitlab ci/cd的一些基本概念,可以参考官网 Get started with GitLab CI/CD | GitLab
本文介绍的主要是如何配置gitlab ci/cd,以达到自动构建、自动部署的目的。
配置Gitlab CI/CD
配置之前,你需要创建一个仓库,如果是对已有仓库配置则需要有该仓库的 Maintainer或者Owner 权限。
gitlab runner
gitlab runner指的是用来执行构建编译的机器,类似于Jenkins的node节点。可以是Linux机器或者windows机器。
runner可以在项目 => setting => CI/CD中查看。
runner 分为共享型(所有项目都可以用)、指定型(当前项目可以用)。
如果还没有配置好的runner机器,可以用自己本地电脑作为runner机进行测试。
- Install GitLab Runner and ensure it’s running.
- 注册runner(可以在项目 => setting => CI/CD查看注册方法)
.gitlab-ci.yml
在项目根目录中创建一个.gitlab-ci.yml文件(使用的是yaml语法,配置前建议先了解下),当开发人员提交代码时,如果检测到根目录中有该文件,就会自动触发CI/CD流程。
一个pipeline包含若干个stage,这些stage是串行执行的,比如build、test、deploy。
每个stage又可以包含若干个job,这些job是并行执行的,比如test这个stage里有三个job:unit_test、lint_test、api_test。
简单模板
下面是一个简单的.gitlab-ci.yml模板:
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 | stages: # 在这里定义执行的stage,以及执行顺序 - build - test - deploy build-job: # job名称 stage: build # stage名称,用来标记这个job是在哪个stage执行的 script: - echo "Hello, $GITLAB_USER_LOGIN!" # 执行的脚本 test-job1: stage: test script: - echo "This job tests something" test-job2: stage: test script: - echo "This job tests something, but takes more time than test-job1." - echo "After the echo commands complete, it runs the sleep command for 20 seconds" - echo "which simulates a test that runs 20 seconds longer than test-job1" - sleep 20 deploy-prod: stage: deploy script: - echo "This job deploys something from the $CI_COMMIT_BRANCH branch." |
上面这个文件解析成pipeline是这样的:
上面$GITLAB_USER_LOGIN
和$CI_COMMIT_BRANCH
是预定义的变量,具体参考:Predefined CI/CD variables reference | GitLab
关键字
官方文档:CI/CD YAML syntax reference | GitLab
类似于编程语言的关键字(保留字)一样,.gitlab-ci.yaml中也有一些关键字,它们有特定的含义。这里列举一些常用的。
配置pipeline的全局关键字:
关键字 | 含义 |
---|---|
default | 定义job中的关键字默认值,如果job中没有配置相同关键字的值,则会使用这里的默认值。 |
include | 从其他yaml文件中导入配置。比如你有多个前端项目都是用npm构建的,那可以提取出一个公共配置,其他的项目继承自该公共配置就行了。 |
stages | 用来定义pipeline的阶段,以及每个阶段的执行顺序(按照从上到下执行) |
variables | 为pipeline中的所有job定义CI/CD用到的变量。 |
workflow | 控制运行什么类型的pipeline。 |
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 | default: image: ubuntu:16.04 # 默认镜像,如果job没有定义images after_script: "echo job finish" # 默认的after_script,如果job中没有定义,就会在ci脚本执行完执行该脚本。 include: - remote: 'https://gitlab.com/example-project/-/raw/main/.gitlab-ci.yml' # 引用远程文件 - local: '/templates/.gitlab-ci-template.yml' # 引用本地文件 - project: 'my-group/my-project' # 引用其他项目指定分支的文件,可以多个 ref: main file: - '/templates/.gitlab-ci-template.yml' - '/templates/.tests.yml' - project: 'my-group/my-project' ref: v1.0.0 # 可以是某个tag file: '/templates/.gitlab-ci-template.yml' - project: 'my-group/my-project' ref: 787123b47f14b552955ca2786bc9542ae66fee5b # 也可以是 Git SHA file: '/templates/.gitlab-ci-template.yml' stages: # 定义阶段和执行顺序 - build - test - deploy variables: # 定义全局变量,在job中用 $+变量名 来引用 DEPLOY_SITE: "https://example.com/" workflow: # 只有当至少符合一个规则时,才会执行pipeline rules: - if: $CI_COMMIT_TITLE =~ /-draft$/ # 这里指如果提交标题(提交消息的第一行)没有以-draft结尾,才为True when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" # 判断事件,merge事件时为True - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # 判断提交的分支是否默认分支 |
配置job的关键字
关键字 | 含义 |
---|---|
after_script | 覆盖job完成后执行的一组命令。 |
allow_failure | 允许job失败。如果配置为true,则该job失败后,不会导致整个pipeline失败终止。 |
artifacts | job执行成功后附加到job的文件或目录列表(类似构建产物),这些文件或目录会保存起来。 |
before_script | 覆盖job开始前执行的一组命令。 |
cache | 需要缓存的文件列表,可以用来缓存一些公共依赖(下面会单独介绍) |
coverage | 设置job的代码覆盖率。 |
dast_configuration | 在job级别使用dast配置文件中的配置。 |
dependencies | 这里定义的是一些job列表,表示会使用到这些job中工件(artifacts) |
environment | job部署到的环境名称。 |
except | 控制什么时候不执行job,与之相反的是only关键字。 |
extends | 继承配置,比如一些公共的配置。 |
image | 定义Docker镜像,会在该镜像中执行job,比如一些需要特定编译环境的job,可以先自定义构建镜像。 |
inherit | 配置true表示继承pipeline关键字default中定义的全部默认值,false表示全部不继承。配置列表,可以选定继承需要的默认值。 |
interruptible | 表示如果触发了相同的新的job,该job是否可以被终止。 |
needs | 这个关键字可以无视stages中定义的阶段顺序。配置空列表,会立即执行job。假如有build_a、build_b、test_a、test_b,定义的stage顺序是是build => test。但是build_b耗时很长,那么可以在test_a中配置needs: [“build_a”],这样的话build_a执行完就会马上执行test_a,而不用继续等待build_b执行完。 |
only | 控制什么时候执行job,与之相反的是except关键字。 |
pages | 将静态文件部署到git page,需要将内容放在public/目录中,然后在工件中path中包含public目录。 |
parallel | 在单个管道中并行运行多个job,可以配置每个job实例具有不同的变量值。 |
release | 这个是用于发布的,需要在runner上安装有release-cli。 |
resource_group | 避免job并发执行。配置资源组后,对于同一个组员组内的job,永远不会并发执行,即使是独立的pipeline之间也不会并发。这样可以避免重复测试、重复部署等问题。 |
retry | 用于配置job发生故障时可以自动重试的时间和次数。 |
rules | 定于规则以决定job是否执行,不能和only、except一起使用。 |
script | 由运行程序执行的 Shell 脚本。 |
secrets | CI/CD时需要用到一些敏感信息,如数据库密码等 |
services | 配置Docker 服务镜像,在镜像中执行pipeline。 |
stage | 定义job属于哪个阶段。 |
tags | 用于筛选runner机器。 |
timeout | job执行超时时间。 |
trigger | 用于触发下游pipeline,如果是触发一个,则配置yaml文件路径,如果是触发多个,则配置这些yaml所在的路径。 |
variables | 定义job级别的变量 |
when | 定义何时运行job,默认是on_success(pipeline成功时),on_failure(pipeline失败时)、always(总是)、manual(需要人工触发) |
在容器中运行CI/CD
配置了runner之后,pipeline是在runner指定目录去pull代码,然后执行.gitlab-ci.yml中定义的CI/CD脚本。
但是不同项目的依赖环境是不一样的(例如不同的后端语言java、python、c++、golang等),我们不可能在runner上安装好所有的构建环境。所以我们需要先自定义一些镜像,然后通过job的image配置使用哪个镜像。
配置之后,runner会基于该镜像启动一个容器,pipeline就会在容器中运行了。
1 2 3 4 5 6 7 | defalut: image: ubuntu-python3:latest # 定义默认的镜像 test-job1: image: ubuntu-python2:1.0 # 优先使用job中定义的镜像,如果不配置,就使用默认的。 script: - sh build.sh |
缓存依赖项提高构建速度
在job中配置需要缓存的文件或目录,可以在job之间共享(即使是不同的pipeline也可以)
使用cache:paths
关键字选择要缓存的文件或目录。
使用cache:key
关键字给每个缓存一个唯一的标识键。所有使用相同缓存键的作业都使用相同的缓存,包括在不同的管道中。
使用cache:key:files
监听一些文件,当这些更改时(通过计算这些文件sha值来确定是否更改),会生成新的缓存。比如监听yarn.lock文件,对应的缓存路径是node_modules,如果我的yarn.lock文件没有改动,则复用node_modules。如果yarn更新了,则重新创建缓存node_modules。
cache:untracked: true
缓存 Git 存储库中未跟踪的所有文件。因为这些文件是未跟踪的(也就是说不会变动),那么缓存下来可以提高下载速度。
cache:when
根据job的状态定义何时保存缓存。
要更改缓存的上传和下载行为,请使用cache:policy
关键字。默认情况下,job在开始时下载缓存,并在job结束时将更改上传到缓存。此缓存样式是pull-push
策略(默认)。
要将job设置为仅在作业开始时下载缓存,但在job完成时从不上传更改,请使用cache:policy:pull
.
要将job设置为仅在作业完成时上传缓存,但在job开始时从不下载缓存,请使用cache:policy:push
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | example_job1: script: - echo "This job uses a cache." cache: key: binaries-cache-$CI_COMMIT_REF_SLUG # 使用字符串作为key paths: - binaries/ example_job2: script: - npm i - npm run build cache: key: files: - package.json # 因为这个文件是用来管理依赖的,所以可以用它的sha值作为key,以确定使用哪个缓存 paths: - node_modules |
yaml文件优化
对于一个复杂的项目,.gitlab-ci.yml往往会比较复杂,重复的配置也多,这时可以利用yaml的锚点语法进行优化。
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 | .job_template: &job_configuration script: - test project tags: - dev .postgres_services: services: &postgres_configuration - postgres - ruby .mysql_services: services: &mysql_configuration - mysql - ruby test:postgres: <<: *job_configuration services: *postgres_configuration tags: - postgres test:mysql: <<: *job_configuration services: *mysql_configuration |
实际渲染之后的文件是这样的:
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 | .job_template: script: - test project tags: - dev .postgres_services: services: - postgres - ruby .mysql_services: services: - mysql - ruby test:postgres: script: - test project services: - postgres - ruby tags: - postgres test:mysql: script: - test project services: - mysql - ruby tags: - dev |
script中使用锚点
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 | .some-script-before: &some-script-before - echo "Execute this script first" .some-script: &some-script - echo "Execute this script second" - echo "Execute this script too" .some-script-after: &some-script-after - echo "Execute this script last" job1: before_script: - *some-script-before script: - *some-script - echo "Execute something, for this job only" after_script: - *some-script-after job2: script: - *some-script-before - *some-script - echo "Execute something else, for this job only" - *some-script-after |
变量的锚点
1 2 3 4 5 6 7 8 9 10 11 12 | # global variables variables: &global-variables SAMPLE_VARIABLE: sample_variable_value ANOTHER_SAMPLE_VARIABLE: another_sample_variable_value # a job that must set the GIT_STRATEGY variable, yet depend on global variables job_no_git_strategy: stage: cleanup variables: <<: *global-variables GIT_STRATEGY: none script: echo $SAMPLE_VARIABLE |
除此之外,还可以结合extends
和include
一起使用。
具体可以参考官方文档,这里不再赘述。Optimize GitLab CI/CD configuration files | GitLab
定时执行pipeline
进入你的项目中,然后点击左侧 CI/CD => Schedules,根据指引配置即可。这里的配置类似于Jenkins里的定时调度。