基于 Kubernetes 的 GitLab CI/CD

GitLab Runner

GitLab Runner 是 GitLab CI/CD 负责在 Pipeline 中执行 Job 的组件,部署在 GitLab 核心服务之外,通过主动注册的方式和 GitLab 建立通信。

Runner 可以直接在机器上运行,或者通过 Docker 或 K8s 部署运行,安装部分参考文档。然后就是启动 Runner 并注册到 GitLab,注册时需要提供:

  • GitLab 对外的 URL:Runner 会通过这个 URL 访问 GitLab,例如 https://example.com
  • 认证 Token:Token 从 GitLab 中获取,GitLab 通过 Token 认证 Runner,Token 会标识不同的 Runner 类型
  • Runner 配置,例如 Executor 类型

Registration Token 和 Authentication Token

现阶段 Runner 注册支持两种类型的 Token:Registration TokenAuthentication Token

Registration Token 是 GitLab 15.10 之前 Runner 的认证方式,目前已经被标记为弃用。在 Runner 注册时提供从 GitLab 中获取的 Registration Token,只要配置没问题,Runner 就会出现在 GitLab Runner 实例列表中,可以使用相同的 Registration Token 注册多个 Runner 实例。Runner 重启(你也可以认为时重新注册)后也会成为新的 Runner 实例,之前实例上的配置都会丢失。

Authentication Token 是 GitLab 目前默认的认证方式。这种方式需要你在 GitLab 上创建 Runner 实例,然后获得 Runner 实例的 Authentication Token,原版注册需要的部分配置从注册 Runner 时提供改为了在 Runner 实例上配置。

后续 GitLab 会按计划弃用和删除 Registration Token,所以在 16.0 版本后尽可能 Authentication Token 方式注册 Runner。

Runner 类型

Runner 分为三种类型,分别是:Instance Runner、Group Runner 和 Project Runner。

Instance Runner 是对所有项目和组可见的共享 Runner。Instance Runner 的 Authentication Token 在 Admin Area > CI/CD > Runners,通过 New instance runner 创建 Runner 后显示,并且在注册后无法再次看到。如果使用 Registration Token 在相同位置通过 Register an instance runner 获取,Token 只会在 Reset 后改变。

如果想要 Instance Runner 对全部项目启用,通过管理员身份启用 Admin Area > Settings > CI/CD > Continuous Integration and DeploymentEnable shared runners for new projects,这个选项默认启用。如果只想针对部分项目启用,可以在项目的 Settings > CI/CD > Runners 中单独启用 Enable shared runners for this project

Group Runner 是对组可见的 Runner,组内所有项目的子组的项目都会默认启用。默认情况下,组的 Owner 可以注册 Runner,方式和 Instance Runner 接近,在组的 Build > Runners 页面创建 Runner 后获取到 Token。你可以在组的 Settings > CI/CDRunners 项下选择是否启用 Instance Runner。

Project Runner 是仅对项目可见的 Runner,在项目 Settings > CI/CD > Runners 下创建 Runner,Runner 注册成功后默认仅用于被注册的项目。Project Runner 也允许被共享给其他项目,在 Admin Area > Runners 或者项目的 Runners 设置页面中找到你要共享的 Runner,编辑 Runner,通过 Select projects to assign to this runner 为其他项目启用这个 Runner。注意 Group Runner 是没有类似配置的。

Executor

Executor 负责在不同环境里执行 Job,例如直接在机器上执行 Job,或是临时创建一个 VM 去运行 Job,再或是使用 Docker 或 K8s 执行 Job 等等。Runner 配置 Executor 并不一定表示 Runner 和 Executor 必须在用同一个环境,例如可以 Runner 部署在 K8s 中,但是通过 SSH 执行 Job。

如果想在 Docker 环境下构建容器镜像,可能需要 Docker 执行器配置特权模式来访问宿主机上的 Docker,这会带来一定的安全风险。推荐使用 dind-rootless 镜像或者不依赖特权模式的构建方式来构建容器镜像,例如 kaniko

K8s 执行器通过 K8s API 来管理 CI Job 的 Pod,需要为 Runner 创建对应权限的 RBAC,再由 Pod ServiceAccount 授权到 Runner。Pod 容器内的安全限制和 Docker 类似,应尽量避免通过特权模式构建容器镜像。

通过 Helm 安装 GitLab Runner(Kuberntes Executor)

添加 GitLab chart repo:

1
2
helm repo add gitlab https://charts.gitlab.io/
helm repo update

通过 helm show values 获取默认 values:

1
helm show values gitlab/gitlab-runner > gitlab-runner-values.yaml

或者直接参考我的文件:gitlab-runner-values.yaml,注意我为了内外测试配置了 Host Aliases

然后安装 GitLab Runner:

1
helm upgrade -i -n gitlab -f ./gitlab-runner-values.yaml gitlab-runner gitlab/gitlab-runner

顺利的话,在获取 Token 的地方会看到 Runner 成功注册的信息。

CI/CD YAML

你只需要项目根目录创建 .gitlab-ci.yml,每次代码提交时,GitLab 都会根据这个文件创建对应 CI/CD 任务。

参考 GitLab 为项目创建的默认 .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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# This file is a template, and might need editing before it works on your project.
# This is a sample GitLab CI/CD configuration file that should run without any modifications.
# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
# it uses echo commands to simulate the pipeline execution.
#
# A pipeline is composed of independent jobs that run scripts, grouped into stages.
# Stages run in sequential order, but jobs within stages run in parallel.
#
# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
#
# You can copy and paste this template into a new `.gitlab-ci.yml` file.
# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
#
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml

stages: # List of stages for jobs, and their order of execution
- build
- test
- deploy

build-job: # This job runs in the build stage, which runs first.
stage: build
script:
- echo "Compiling the code..."
- echo "Compile complete."

unit-test-job: # This job runs in the test stage.
stage: test # It only starts when the job in the build stage completes successfully.
script:
- echo "Running unit tests... This will take about 60 seconds."
- sleep 60
- echo "Code coverage is 90%"

lint-test-job: # This job also runs in the test stage.
stage: test # It can run at the same time as unit-test-job (in parallel).
script:
- echo "Linting code... This will take about 10 seconds."
- sleep 10
- echo "No lint issues found."

deploy-job: # This job runs in the deploy stage.
stage: deploy # It only runs when *both* jobs in the test stage complete successfully.
environment: production
script:
- echo "Deploying application..."
- echo "Application successfully deployed."

通过这个例子快速了解几个概念:

  • Job 定义一个 CI 子任务,包括执行的脚本,所属的 stage 和 environment 等配置
  • Stage 是 Job 的合集,同一个 Stage 下的 Job 并发执行,多个 Stage 由 stages 决定顺序,如果没有配置 Stage,默认使用 test 作为 Stage
  • Environment 用来描述关联部署环境以及相关的部署动作,不是必须的

把这个例子添加到项目并提交代码后,你就可以在项目 Build > Pipelines 看到 CI 任务的执行情况。

如果你安装 GitLab Runner 用的是我的 gitlab-runner-values.yaml,那么执行 Job 的 Pod 镜像就会是配置中指定的 ubuntu:22.04

GitLab CI/CD YAML 配置非常丰富,建议参考官方文档,我就不细聊了。

阿里云 ECI

阿里云 ECI(Elastic Container Instance) 可以灵活申请容器计算资源,并且在容器退出后回收资源,费用按量收取,通常用于 Serverless 场景。在 CI 场景中,如果不是对构建时间特别关注,通过 ECI 执行 CI Job 可以节约不少资源。

K8s 中使用 ECI

ECI 对接 K8s 集群非常方便,ACK 集群只需要在组件管理中安装 Virtual Node,就可以在节点列表下看到 virtual-kubelet-<network-region>,为了高可用,Virtual Node 组件通常会在 ACK 每个网络区下部署一个 virtual-kubelet。如果是阿里云自建 K8s 集群,安装可以参考文档

在成功安装 virtual kubelet 后,只需要为 Pod 增加 Label alibabacloud.com/eci: true 就能把 Pod 调度到 ECI 中,你也可以根据文档 调整容器的 ECI 配置。如果是业务中使用,不希望 ECI 配置侵入,可以通过 eci-profile 方式配置 ECI Pod。

CI Job 使用 ECI

GitLab Runner 使用 ECI 可以参考以下配置

1
2
3
4
5
6
7
8
9
10
11
runners:
...
config: |
[[runners]]
...
[runners.kubernetes]
...
cpu_limit = "1"
memory_limit = "2Gi"
[runners.kubernetes.pod_labels]
"alibabacloud.com/eci" = "true"

除了必须的 Pod Label,把 Pod 调度到 ECI 强烈建议增加 cpu_limitmemory_limit 来限制每次创建的 ECI 容器资源,这影响到 ECI 计费。

在 GitLab CI/CD 中使用 ECI 还需要特别注意一点,多个 Job 任务会启动多个 Pod,每个 Pod 创建后调度到 ECI 过程非常慢,Stage 越多这部分时间的开销越大(同 Stage 下并发 Job 可以接受)。如果启动镜像相同,可以考虑调整 CI 流程,把多个 Job 合并到一个 Job 中。

ACR 镜像加速

Pod 启动后可以在 ECI 控制台中看到正在执行的容器,ECI 会把执行过的镜像缓存以加速将来容器的运行。我们可以使用阿里云的 ACR 来管理镜像,这样就可以通过内网端点来加速拉取镜像了。

ECI 默认支持免密拉取同账号下 ACR 中的镜像,也能通过 RAM 授权跨账号访问 ACR。

不过 GitLab Runner 也支持 Image Pull Secret 拉取镜像,在 Runner 的命名空间下创建 Secret acr-pull-secret 后在 Runner 配置中引用:

1
2
3
4
5
6
7
8
runners:
...
config: |
[[runners]]
...
[runners.kubernetes]
...
image_pull_secrets = ["acr-pull-secret"]

写在最后

我只用到了 GitLab CI/CD 很小部分的功能,像是 Service、Cache、Artifact 之类的功能只能说下次一定了。原本写了点在 CI 里用 kaniko 的内容,写着太烦,全给删了。另外每次聊这些东西都会聊到阿里云产品,只是为了分享一下使用经验或者踩过的坑。