在现代化的 DevOps 实践中,GitLab CI/CD 已经成为实现自动化构建、测试和部署不可或缺的基石。你可能已经熟悉了如何在 GitLab 中编写 .gitlab-ci.yml 文件来定义流水线,但你有没有想过,这些任务究竟是在哪里、由谁来执行的呢?
这就是 GitLab Runner 发挥作用的地方。简单来说,如果 GitLab 是负责指挥的大脑,那么 GitLab Runner 就是辛勤劳动的双手。虽然 GitLab.com 提供了开箱即用的共享 Runner,但在生产环境中,为了满足特定的性能要求、安全策略或环境依赖,配置我们自己的专用 Runner 往往是必经之路。
在这篇文章中,我们将不仅仅停留在表面的安装上,而是会深入探讨如何深度配置 GitLab Runner。我们将引导你完成从基础安装到高级调优的全过程,帮助你构建一个高效、稳定且安全的 CI/CD 执行环境。无论你是使用裸金属服务器、云主机还是 Kubernetes 集群,我们都能找到适合你的配置方案。
目录
什么是 GitLab Runner?
GitLab Runner 是一个开源项目,它作为一个代理程序运行在与 GitLab 实例(可以是 GitLab.com 也可以是自托管的 GitLab CE/EE)独立的服务器上。当你推送代码或者创建合并请求时,GitLab 会触发 CI/CD 流水线。此时,GitLab 并不会直接在你的服务器上运行命令,而是将任务发送给注册好的 Runner。
Runner 接收到指令后,会根据配置的“执行器”来启动作业。这种架构设计带来了极大的灵活性。我们可以在物理机、虚拟机、Docker 容器,甚至是 Kubernetes Pod 中运行作业。
为了让你更好地理解,GitLab Runner 通常分为两类:
- 共享 Runner:
这些通常由 GitLab 官方或管理员维护,运行在多租户环境中。这意味着多个不同的项目可能会在同一个 Runner 上排队执行任务。虽然这对小型开源项目非常方便,但也带来了资源争抢和环境不可控的风险。你无法保证前一个任务是否污染了环境,也无法精确控制 CPU 和内存的分配。
- 特定/项目 Runner:
这是我们今天要重点讨论的对象。特定 Runner 专门为特定的项目或命名空间服务。这使得我们能够对运行环境进行精细化的控制,比如安装特定的私有依赖、挂载特定的硬件设备,或者确保只有授权的项目才能使用该计算资源。
为什么要配置自己的 GitLab Runner?
你可能会问:“GitLab 不是已经提供了共享 Runner 吗?为什么我还要费劲去配置自己的?” 这是一个非常好的问题。在实际的企业级开发中,自定义 Runner 配置能带来以下不可替代的优势:
- 极致的性能控制:你可以根据项目需求选择高配服务器,甚至通过 GPU 加速来执行机器学习相关的构建任务,而不受限于共享资源的性能瓶颈。
- 环境隔离与安全:某些敏感的构建任务可能涉及密钥访问或与内网数据库的交互。使用自托管的 Runner 可以确保数据流不出内网,从而极大地提升安全性。
- 自定义环境:如果你的项目需要安装特定版本的编译器、私有证书或者依赖庞大的本地库,使用 Docker 执行器的自定义 Runner 可以让你预先构建好包含所有依赖的镜像,实现“一次构建,到处运行”。
- 成本优化:对于大型团队,维护自己的 Runner 往往比使用付费的 CI 分钟数更划算。
配置 GitLab Runner 的完整流程
让我们卷起袖子,开始实际操作吧。我们将从环境准备开始,逐步完成安装、注册,最后深入到配置文件的细节。为了保证内容的实用性,我们将基于 Linux 环境(Ubuntu/CentOS)进行演示。
第一步:安装 GitLab Runner
配置 GitLab Runner 的第一步是在目标机器上安装该软件。GitLab Runner 是用 Go 语言编写的,这意味着它通常是一个单独的二进制文件,安装非常简单。
1. 准备环境
首先,确保你有一台可以访问互联网的服务器。如果是内网环境,你可能需要提前下载好二进制包。
2. 执行安装命令
对于 Linux 系统,我们可以直接使用 GitLab 官方的仓库来安装,这样可以确保后续的更新也非常方便。以 Ubuntu 为例,运行以下命令:
# 添加 GitLab Runner 官方仓库
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
# 安装最新版的 GitLab Runner
sudo apt-get install gitlab-runner
如果你使用的是 CentOS 或 RedHat 系统,可以使用以下命令:
# 添加仓库并安装
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash
sudo yum install gitlab-runner
3. 验证安装
安装完成后,我们可以检查一下 Runner 是否正常运行:
# 查看 GitLab Runner 的版本信息
sudo gitlab-runner --version
第二步:注册 GitLab Runner
安装好软件只是第一步,接下来我们需要告诉这个 Runner,“嘿,你应该听谁的话?”,这就是“注册”的过程。注册会将 Runner 连接到你的 GitLab 项目或群组。
1. 获取注册令牌
在此之前,你需要从 GitLab 界面获取注册令牌。进入你的项目页面,依次点击 Settings (设置) > CI/CD > Runners。在这里,你会看到一个 Registration Token(注册令牌)或者 New project runner 按钮。在新版本的 GitLab 中,通常会自动生成一个带有过期时间的 Token。请妥善保管这个字符串。
2. 运行注册命令
回到服务器终端,运行注册命令并按照提示输入信息:
# 启动交互式注册流程
sudo gitlab-runner register
3. 输入配置详情
系统会依次询问你以下几个关键信息,我们将详细解释每一个选项的意义:
- Instance URL:输入你的 GitLab 实例地址。如果你使用的是 GitLab.com,请输入
https://gitlab.com;如果是公司自托管的,请输入内网地址。 - Registration Token:粘贴上一步获取的令牌。
- Runner Description:给 Runner 起个名字,例如
production-docker-runner。这在有多个 Runner 时非常有用,方便识别。 - Tags:这是配置中最关键的一环。标签用于将特定的作业分配给特定的 Runner。例如,你可以输入 INLINECODE6a61c7aa。将来在 INLINECODEde46e113 中,你可以通过
tags: [docker]来指定该作业必须在这个 Runner 上运行。 - Executor:选择执行器。这里强烈建议选择 docker,因为它是目前最流行且隔离性最好的方式。如果你只是运行简单的 Shell 脚本,也可以选择 shell。
示例交互流程:
Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.example.com/
Enter the registration token:
glrt-xxxxxxxxxxxxxxxxxx
Enter a description for the runner:
[my-server]: My Custom Docker Runner
Enter tags for the runner (comma-separated):
docker, backend, deploy
Enter optional maintenance note for the runner:
Used for deploying backend services
Enter an executor: docker+machine, docker-ssh, parallels, shell, ssh, virtualbox, docker:
docker
4. 设置默认 Docker 镜像
如果你选择了 Docker 执行器,系统还会提示你输入默认的镜像。
Enter the default Docker image (for example, alpine:latest):
ubuntu:20.04
第三步:深度解析与优化配置 (config.toml)
虽然通过交互式命令行完成了注册,但 Runner 的真正威力隐藏在配置文件中。在 Linux 系统上,主配置文件通常位于 /etc/gitlab-runner/config.toml。让我们打开它,看看有哪些关键的优化点。
配置文件结构概览
concurrent = 4 # 全局并发限制:控制此服务器上最多同时运行多少个作业
check_interval = 0 # 检查新作业的间隔(秒),0 表示使用默认值
[session_server]
session_timeout = 1800
# Runner 定义区域
[[runners]]
name = "My Custom Docker Runner"
url = "https://gitlab.example.com/"
token = "glrt-xxxxxxxxxxxxxx"
executor = "docker"
[runners.custom_build_dir]
enabled = true # 允许使用自定义构建目录
[runners.cache]
[runners.cache.s3]
# 配置使用 S3 存储缓存,用于分布式部署
[runners.cache.gcs]
# 配置使用 Google Cloud Storage 存储缓存
# 执行器特定配置
[runners.docker]
tls_verify = false
image = "ubuntu:20.04" # 默认使用的镜像
privileged = false # 是否以特权模式运行(Docker-in-Docker 需要开启此选项)
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
# 这里的 volumes 是挂载配置,非常重要!
# 我们可以将宿主机的目录映射到容器中,实现缓存持久化或访问 Docker 套接字
volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
# 拉取镜像的策略
pull_policy = "if-not-present"
# 挂载宿主机上的预下载镜像缓存,加快构建速度
shm_size = 0
关键配置项详解与最佳实践
- Volumes (挂载配置):
在 INLINECODE73c2283a 下的 INLINECODE49463f92 参数是我们最常修改的地方。
* 缓存优化:挂载 /cache 目录可以让流水线之间共享依赖。例如,Gradle 或 npm 的依赖包可以存储在这里,避免每次构建都重新下载。
* Docker-in-Docker (DinD):如果你需要在 CI/CD 中构建 Docker 镜像,你需要挂载宿主机的 Docker 套接字:/var/run/docker.sock:/var/run/docker.sock。这允许容器内的命令直接控制宿主机的 Docker 守护进程。但请注意,这会带来安全风险,请确保 Runner 仅在可信项目中运行。
- Concurrency (并发设置):
顶部的 INLINECODE168b0487 选项决定了这台服务器能同时处理多少个 CI 作业。这应该根据服务器的 CPU 和内存资源来设定。如果设置得过高,会导致系统资源耗尽,构建变慢甚至崩溃。对于一个 4 核 8G 的服务器,建议从 INLINECODEb087f654 或 4 开始尝试。
- Pull Policy (拉取策略):
pull_policy 决定了 Runner 在开始作业前如何获取镜像。
* always:每次都重新拉取镜像,适合开发环境,确保使用最新版本。
* if-not-present:如果本地存在则使用本地镜像,否则拉取。这是生产环境的首选,因为它能极大地减少网络等待时间,提高流水线启动速度。
- Tags (标签管理):
虽然在 config.toml 中可以直接指定标签,但更推荐在 GitLab 界面中进行管理,或者通过环境变量动态调整。合理的标签策略(如 INLINECODEe3409eb5, INLINECODEdd43800d, gpu)能帮助你精确控制作业流向。
高级应用场景与代码示例
让我们通过几个实际场景,看看如何将这些配置应用到工作中。
场景一:使用 Docker 执行器实现环境隔离
假设我们需要在一个包含 Java 和 Node.js 的混合项目中运行构建。我们可以利用 .gitlab-ci.yml 配合 Runner 的 Docker 能力来实现。
.gitlab-ci.yml 示例:
stages:
- test
- build
# 定义变量,指定 Node.js 镜像
variables:
NODE_IMAGE: node:16
MAVEN_IMAGE: maven:3.8-openjdk-11
# 后端测试作业
backend-test:
stage: test
image: ${MAVEN_IMAGE}
tags:
- docker # 匹配我们刚才注册的 Runner 标签
script:
- mvn clean test
cache:
paths:
- target/ # Maven 构建产物缓存
# 前端构建作业
frontend-build:
stage: build
image: ${NODE_IMAGE}
tags:
- docker
script:
- npm install
- npm run build
cache:
paths:
- node_modules/ # NPM 依赖缓存
原理说明:
在这里,Runner 会根据每个作业的 image 字段动态启动对应的容器。前一个作业的容器销毁后,完全不会影响下一个作业,保证了环境的纯净。
场景二:使用 Shell 执行器访问物理硬件
有时,我们需要编译运行在特定硬件上的固件,或者需要访问宿主机上的特殊设备。这时 Docker 的隔离反而是一种障碍。
配置调整:
在注册时选择 INLINECODE1b5852f7 执行器,或在 INLINECODE15b9a3bd 中修改 executor = "shell"。
.gitlab-ci.yml 示例:
job:hardware_test:
stage: test
tags:
- baremetal # 指定使用 shell 执行器的 Runner
script:
- ls -l /dev/usb0 # 直接访问宿主机的 USB 设备
- python compile_firmware.py --port=/dev/usb0
only:
- master
场景三:动态扩缩容与 Kubernetes 集成
如果你的企业使用 Kubernetes,我们完全可以不维护固定的 Runner 虚拟机,而是让 GitLab 自动在 K8s 集群中创建 Pod 来运行任务,任务结束后 Pod 自动销毁。这虽然需要更复杂的配置,但能实现极致的资源利用率。
常见问题排查与解决方案
在配置过程中,你可能会遇到以下一些坑,这里我们提供了解决方案:
- 权限被拒绝:
错误信息:permission denied while trying to connect to the Docker daemon socket。
原因:gitlab-runner 用户没有访问 /var/run/docker.sock 的权限。
解决:在终端运行 INLINECODE45219079,然后重启 Runner 服务 (INLINECODE005aea48)。
- 作业卡住不动:
如果点击流水线一直显示 INLINECODEe092facf 且没有任何动静,通常是 Tag 不匹配。请检查你的 Runner 标签是否与 INLINECODEe30cbc0b 中定义的 tags 完全一致。如果 Runner 没有标签,你的作业也不能包含标签。
- 内存溢出 (OOM):
如果 Runner 在构建大型项目时突然消失,通常是内存不足。检查系统日志 (INLINECODE80249e8a),你会发现 INLINECODE3c5342ec 错误。试着减少 concurrent 的值,或者增加服务器的交换分区。
总结与下一步
通过这篇文章,我们从零开始配置了一个专属的 GitLab Runner,深入学习了 config.toml 的关键配置,并掌握了在不同场景下选择不同执行器的方法。配置 GitLab Runner 并不是一次性的工作,而是一个持续优化的过程。你现在可以监控流水线的执行时间,根据实际情况调整缓存策略和并发数,打造一个符合你项目节奏的高效自动化系统。
下一步,我们建议你尝试在你的私有云环境中搭建一个 Runner,并将其与项目的部署流程结合,体验完全掌控 CI/CD 基础设施的快感。祝你构建愉快!