功能概览
上图是基本的CI/CD工作流,与之对应的,gitlab几乎提供了上述流程节点所需的所有相关功能:
阶段 | 功能 |
---|---|
1. Verify |
|
2. Package |
|
3. Release |
|
4. 其它 |
|
Web端登录Gitlab仓库后,如果有用户有相应权限,可以在左侧边栏看到CI/CD菜单,包含:Pipelines
, Jobs
, Schedules
三个选项。下面对这些菜单的功能和使用方法进行介绍。
- 本文基于GitLab Community Edition 13.4.1进行介绍,在提到相关内容时会提供官网介绍链接方便读者获取一手资料
- GitLab CI/CD官方文档
- 从CircleCI迁移至Gitlab
- 从Jenkins迁移至Gitlab
Pipelines
管线(Pipelines)可以认为是一个集成工作流,可以在仓库代码发生变更时触发一系列操作(脚本或者Jobs)。想要创建管线并执行,需要做两个配置工作:
- 创建
.gitlab-ci.yml
管线配置文件 - 配置GitLab-Runner
配置好之后,可以在Pipelines
菜单中看到已有的管线列表。下面开始具体介绍。
创建第一个管线
想要创建一个管线,只需要在gitlab仓库根目录下创建一个.gitlab-ci.yml
文件,一个简单的示例如下:
default:
image: ruby:2.5
before_script:
- apt-get update
- apt-get install -y sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- gem install bundler --no-document
- bundle install --jobs $(nproc) "${FLAGS[@]}"
rspec:
script:
- bundle exec rspec
rubocop:
script:
- bundle exec rubocop
可以手动创建.gitlab-ci.yml
文件,然后编辑内容,也可以借助gitlab提供的模版文件,使用方式是:New File
-> Select a template type
选择.gitlab-ci.yml
即可。
先不管这里面做了什么,当我们配置了上述脚本后,GitLab-CI就自动为我们创建好了管线,但此时管线还是处理pending状态,想要GitLab-CI可以工作起来,我们还需要配置GitLab-Runner。
YAML
文件有两种后缀.yml
和.yaml
,都可以被解析。虽然.gitlab-ci.yml
使用的是.yml
,但是YAML
官方还是推荐使用.yaml
作为YAML
配置文件;- 关于
.gitlab-ci.yml
文件的配置项及其作用,可以参考这两篇:.gitlab-ci.yml
快速上手,.gitlab-ci.yml
完整手册;- 脚本中以返回0表示步骤执行失败,其他非0整数表示步骤执行成功;
- 配置默认需要放在仓库根目录,也支持自定义存放位置;
配置GitLab-Runner
GitLab-Runner是执行管线操作的服务器,可以是物理机、虚拟机、容器(集群)、VPS等任何可以访问该gitlab仓库的服务器,支持配置多个。
管线架构及其配置方法
正如前面提到,管线的目的是及时发现和报告开发集成过程中的问题,因此一个管线执行一般可以分为三个阶段:build
-> test
-> deploy
。
当然如果某个阶段中没有配置Job则会被跳过执行。如果你了解gradle,可以将Job类比gradle的Task,我们知道gradle在配置阶段结束后Task创建完毕并且Task之间建立起有向无环图结构的依赖关系,管线的Jobs也是如此,不过管线Jobs之间还有其他两种关系结构,下面分别了解下。
Basic
如上图,基础结构的特点是:
- 从build到deploy只有一条线路;
- 每个阶段内的Jobs之间并行执行,该阶段内所有Jobs执行完毕执行下个阶段Jobs;
- 每个阶段之间串行执行;
虽然执行效率可能不是很高,但是这个逻辑结构是最简单和易于维护的,因此优先推荐使用这个管线架构。假如要实现上述的集成工作流,我们可以编写如下的配置文件:
stages:
- build
- test
- deploy
image: alpine
build_a:
stage: build
script:
- echo "This job builds something."
build_b:
stage: build
script:
- echo "This job builds something else."
test_a:
stage: test
script:
- echo "This job tests something. It will only run when all jobs in the"
- echo "build stage are complete."
test_b:
stage: test
script:
- echo "This job tests something else. It will only run when all jobs in the"
- echo "build stage are complete too. It will start at about the same time as test_a."
deploy_a:
stage: deploy
script:
- echo "This job deploys something. It will only run when all jobs in the"
- echo "test stage complete."
deploy_b:
stage: deploy
script:
- echo "This job deploys something else. It will only run when all jobs in the"
- echo "test stage complete. It will start at about the same time as deploy_a."
总结一下:
- 每个Job使用
stage
指定执行阶段; - 使用
script
指定执行的脚本内容,脚本行与行之间用-
区分;
Directed Acyclic Graph(DAG)
从上面可以看出,DAG允许从build到test可以有多条线路,可能每条线路的执行时长是不同的,这就很适合那种构建时间比较长,追求执行效率的项目。DAG的特点如下:
- 允许存在多条线路,且线路之间逻辑独立,没有相关等待关系;
- 由于存在多条线路因此会有多个deploy成果物;
想要实现上述结构的管线,配置文件如下:
stages:
- build
- test
- deploy
image: alpine
build_a:
stage: build
script:
- echo "This job builds something quickly."
build_b:
stage: build
script:
- echo "This job builds something else slowly."
test_a:
stage: test
needs: [build_a]
script:
- echo "This test job will start as soon as build_a finishes."
- echo "It will not wait for build_b, or other jobs in the build stage, to finish."
test_b:
stage: test
needs: [build_b]
script:
- echo "This test job will start as soon as build_b finishes."
- echo "It will not wait for other jobs in the build stage to finish."
deploy_a:
stage: deploy
needs: [test_a]
script:
- echo "Since build_a and test_a run quickly, this deploy job can run much earlier."
- echo "It does not need to wait for build_b or test_b."
deploy_b:
stage: deploy
needs: [test_b]
script:
- echo "Since build_b and test_b run slowly, this deploy job will run much later."
总结一下:
- 使用
needs
关键词为每个Job指定所依赖的前置Job;
Child / Parent
针对具有多个deploy成果物且生成过程完全独立的场景,出于配置复用的目的,可以将每中线路单独制作成.gitlab-ci.yml
配置文件,然后用一个总的.gitlab-ci.yml
去集成它们,这就是父子接口的核心思想,其逻辑结构图如下:
同样的,父配置文件如下:
stages:
- triggers
trigger_a:
stage: triggers
trigger:
include: a/.gitlab-ci.yml
rules:
- changes:
- a/*
trigger_b:
stage: triggers
trigger:
include: b/.gitlab-ci.yml
rules:
- changes:
- b/*
子配置文件/a/.gitlab-ci.yml
如下:
stages:
- build
- test
- deploy
image: alpine
build_a:
stage: build
script:
- echo "This job builds something."
test_a:
stage: test
needs: [build_a]
script:
- echo "This job tests something."
deploy_a:
stage: deploy
needs: [test_a]
script:
- echo "This job deploys something."
子配置文件/b/.gitlab-ci.yml
如下:
stages:
- build
- test
- deploy
image: alpine
build_b:
stage: build
script:
- echo "This job builds something else."
test_b:
stage: test
needs: [build_b]
script:
- echo "This job tests something else."
deploy_b:
stage: deploy
needs: [test_b]
script:
- echo "This job deploys something else."
总结如下:
- 使用
stages
申明自定义的阶段; - 使用
trigger
+include
关键词引用子脚本; - 使用
rules
+changes
关键词指定子脚本的影响范围; - 子配置内容自由,不受父配置限制;
- 父配置除了定义引入子配置外,还可以做正常配置文件可以做的事情;
相信看上了面的介绍,你已经基本了解了gitlab管线的流程和配置方法,也能掌握Pipelines
、Jobs
和Schedules
这几个菜单的功能使用了。可以真要上手可能还是一脸懵逼,无从下手,幸好官方已经为我们提供了各种类型项目的.gitlab-ci.yml
模版,我们可以在理解这些模版基础上实现自己的配置文件。
Auto DevOps
官网链接,需要用到的时候参考。
Android工程配置模版
对于安卓项目,gitlab也提供了配置模版[Setting up GitLab CI for Android projects],拷贝如下:
image: openjdk:8-jdk
variables:
ANDROID_COMPILE_SDK: "28"
ANDROID_BUILD_TOOLS: "28.0.2"
ANDROID_SDK_TOOLS: "4333796"
before_script:
- apt-get --quiet update --yes
- apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
- wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_TOOLS}.zip
- unzip -d android-sdk-linux android-sdk.zip
- echo y | android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
- echo y | android-sdk-linux/tools/bin/sdkmanager "platform-tools" >/dev/null
- echo y | android-sdk-linux/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
- export ANDROID_HOME=$PWD/android-sdk-linux
- export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
- chmod +x ./gradlew
# temporarily disable checking for EPIPE error and use yes to accept all licenses
- set +o pipefail
- yes | android-sdk-linux/tools/bin/sdkmanager --licenses
- set -o pipefail
stages:
- build
- test
lintDebug:
stage: build
script:
- ./gradlew -Pci --console=plain :app:lintDebug -PbuildDir=lint
assembleDebug:
stage: build
script:
- ./gradlew assembleDebug
artifacts:
paths:
- app/build/outputs/
debugTests:
stage: test
script:
- ./gradlew -Pci --console=plain :app:testDebug
更多项目示例参考这里GitLab CI/CD Examples