GitLab Runner 详解
GitLab CI/CD 中,GitLab 负责管理代码、创建 Pipeline、调度 Job 和保存执行结果,而真正执行 .gitlab-ci.yml 中命令的是 GitLab Runner。
本文按照实际搭建流程,从 Runner 的工作原理开始,依次介绍 Runner 的创建、安装、注册、Executor、多个 Runner 管理,以及 Java 项目常见的 Build + Deploy 推荐架构。
本文主要以 GitLab Self-Managed + Ubuntu + Java/Maven + Docker 为例。
1、GitLab Runner 是什么
GitLab Runner 是 GitLab CI/CD 的 Job 执行程序。
GitLab 本身主要负责:
保存代码
解析
.gitlab-ci.yml创建 Pipeline
创建和调度 Job
保存 Artifact
展示执行日志和结果
真正执行下面这些命令的,是 Runner:
script:
- mvn clean package
- npm run build
- docker compose up -d整体关系可以理解为:
GitLab
↓
读取 .gitlab-ci.yml
↓
创建 Pipeline
↓
创建 Job
↓
根据 Runner 作用域 + tags 寻找可用 Runner
↓
Runner 获取 Job
↓
Runner 使用 Executor 创建执行环境
↓
执行 script
↓
上传日志 / Artifact / Cache
↓
GitLab 展示执行结果1.1 GitLab Runner 程序和 Runner 实例不是一回事
这是最容易混淆的地方。
Linux 上安装的是一个程序:
gitlab-runner例如:
gitlab-runner --version而在 GitLab 页面创建并注册以后,会产生一个具体的 Runner 配置。
可以这样理解:
一台 Linux 服务器
│
└── gitlab-runner 程序 / 服务
│
├── build-runner
├── deploy-runner
└── vue-build-runner所以:
GitLab Runner 程序 ≠ 一个 Runner一套 gitlab-runner 服务可以管理多个已注册 Runner。
1.2 Runner 的作用域
常见 Runner 有三种作用域:
公司内部项目一般优先使用:
Project Runner如果多个项目构建环境完全一致,也可以考虑 Group Runner。
1.3 Job 到底怎么找到 Runner
Job 不是通过 stage 找 Runner,而是主要通过:
Runner 作用域
+
Runner 是否在线
+
tags
+
Runner 是否允许执行 untagged job
+
Runner 是否满足 protected 等限制例如:
job_build:
stage: build
tags:
- buildGitLab 会寻找带有:
build标签的可用 Runner。
如果 Job 配置多个 tag:
tags:
- build
- docker
- linux那么 Runner 必须同时拥有:
build
docker
linux这三个 tag 才能接这个 Job。
tags是 Runner 选择条件,不是“满足任意一个即可”,而是 Job 中声明的 tag 必须全部匹配。
2、在 GitLab 创建 Runner
以 Project Runner 为例。
进入:
项目
↓
Settings
↓
CI/CD
↓
Runners
↓
Create project runner不同 GitLab 版本页面名称可能略有差异。
2.1 配置 Runner tags
例如创建构建 Runner:
Runner description:
build-runner
Tags:
build部署 Runner:
Runner description:
deploy-runner
Tags:
deploy这样 .gitlab-ci.yml 就可以通过:
tags:
- build或者:
tags:
- deploy决定由哪个 Runner 执行。
2.2 Run untagged jobs
如果 Runner 开启:
Run untagged jobs那么没有配置 tags 的 Job 也可能被这个 Runner 执行。
例如:
job_test:
stage: test
script:
- echo "test"如果不希望 Job 被错误 Runner 执行,建议公司的专用 Runner:
关闭 Run untagged jobs
+
明确配置 tags这样 Runner 的职责会更清晰。
2.3 Runner Token
创建 Runner 后,GitLab 会提供注册命令,例如:
gitlab-runner register \
--url http://gitlab.example.com \
--token glrt-xxxxglrt- 开头的是 Runner authentication token。
3、在 Ubuntu 安装 GitLab Runner
3.1 推荐使用 GitLab 官方软件源
不建议直接在一个全新的 Ubuntu 上执行:
sudo apt install gitlab-runner原因是 Ubuntu 自带软件源中的版本可能明显落后。
推荐先添加 GitLab 官方软件源。
下载官方仓库脚本:
curl -L \
"https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" \
-o /tmp/gitlab-runner-script.deb.sh执行:
sudo bash /tmp/gitlab-runner-script.deb.sh然后安装:
sudo apt update
sudo apt install -y gitlab-runner检查版本:
gitlab-runner --version3.4 使用 systemd 管理 Runner
安装完成后,一般会作为 systemd 服务运行。
查看状态:
sudo systemctl status gitlab-runner启动:
sudo systemctl start gitlab-runner停止:
sudo systemctl stop gitlab-runner重启:
sudo systemctl restart gitlab-runner设置开机启动:
sudo systemctl enable gitlab-runner查看最近日志:
sudo journalctl -u gitlab-runner -n 100 --no-pager持续查看日志:
sudo journalctl -u gitlab-runner -f4、注册 Runner,以及 GitLab 和 Linux 是如何关联的
创建 Runner 以后,还只是 GitLab 服务端存在这个 Runner 记录。
真正让 Linux 服务器能够执行 Job,需要在安装了 gitlab-runner 的机器上执行注册。
完整流程:
GitLab 项目
↓
创建 Project Runner
↓
GitLab 生成 Runner authentication token
↓
Linux 安装 gitlab-runner
↓
执行 gitlab-runner register
↓
本地生成 Runner 配置
↓
配置写入 config.toml
↓
Runner 开始向 GitLab 获取可执行 Job这里有点绕,简单理解:
gitlab可以创建多个runner,每个runner都根据自己的tag来确定是否执行工作流。
gitlab上创建的runner可以理解为一个标记, linux上的gitlab-runner是一个软件,(可以把他比作分布式系统的注册中心)gitlab创建的runner可以通过命令注册到gitlab-runner.gitlab-runner管理多个runner。
4.1 注册命令
例如在gitlab创建runner后会获得:
sudo gitlab-runner register \
--url http://gitlab.example.com \
--token glrt-xxxx执行后会进入交互式配置。
示例:
Runtime platform arch=amd64 os=linux version=xx.x.x
Running in system-mode.
#询问1 gitlab地址 命令已经包含直接回车即可
Enter the GitLab instance URL:
[http://gitlab.example.com]:
Verifying runner... is valid
#询问2:这个runner的注册名字 (自己可以自定义,默认直接回车,也可以去gitlab的toml文件修改):
Enter a name for the runner:
[DataBase]: build-runner
#询问3:选择这个runner的执行器命令,简单来说执行器执行命令的环境是 linux选 shell 是docker容器环境 此处选docker
Enter an executor: shell, docker, kubernetes, ...
docker
#询问4:我这里选择的是docker 所以我需要选择默认的镜像,如果runner执行的job指定了别的镜像 那么他会以job的为准
Enter the default Docker image:
maven:3.8.8-eclipse-temurin-21
Runner registered successfully.
#配置写入/etc/gitlab-runner/config.toml,你可以修改此文件来配置runner
Configuration was saved in:
"/etc/gitlab-runner/config.toml"4.2 Runner 名称和 GitLab 页面描述
需要注意:
注册过程中出现的:
Enter a name for the runner主要是本地 config.toml 中的 Runner 名称。
例如:
[[runners]]
name = "build-runner"它和 GitLab 页面上的 Runner description 是两个不同位置的配置。
为了方便维护,推荐两边使用相同或相近的命名。
5、Executor 执行器详解
Executor 决定:
Runner 到底在哪个环境中执行 Job。
常见 Executor:
shell
docker
kubernetes
ssh
custom
...对于普通 Java / Vue 项目,最常见的是:
Docker Executor
Shell Executor5.1 一个注册 Runner 使用一种 Executor
一个 [[runners]] 配置只有一个:
executor = "docker"或者:
executor = "shell"不能同一个 Runner 同时既是:
Docker Executor
+
Shell Executor如果项目需要:
Docker 环境构建
+
Shell 操作宿主机部署推荐注册两个 Runner:
build-runner
executor = docker
tag = build
deploy-runner
executor = shell
tag = deploy但是:
两个 Runner ≠ 两台服务器两个 Runner 完全可以在同一台 Linux 服务器上:
DataBase 服务器
│
├── build-runner
│ └── executor = docker
│
└── deploy-runner
└── executor = shell5.2 Docker Executor
Docker Executor 会使用 Docker 容器作为 Job 的执行环境,一般build都使用docker执行器,因为build的时候要用到特定的环境 比如vue项目在build时要用到node,java要用到maven和jdk。
例如:
job_build:
stage: build
tags:
- build
image: maven:3.8.8-eclipse-temurin-8
script:
- mvn -B -ntp clean package大致过程:
GitLab 创建 Job
↓
build-runner 获取 Job
↓
Docker Executor 准备容器环境
↓
准备代码工作目录
↓
使用 Maven 镜像执行 script
↓
生成构建结果
↓
上传 Artifact / Cache
↓
Job 结束
↓
临时 Job 容器被清理Docker Executor 的优点
构建服务器宿主机不需要安装每个项目需要的:
JDK 8
JDK 17
JDK 21
Maven
Node.js
pnpm
Python
...项目直接在 .gitlab-ci.yml 中声明环境:
image: maven:3.8.8-eclipse-temurin-8另一个项目可以使用:
image: maven:3.9-eclipse-temurin-21不会因为服务器本机 JDK 或 Maven 版本冲突影响不同项目。
因此:
Build / Test 阶段通常优先推荐 Docker Executor。
5.3 Docker Executor 中 image 的作用
例如:
job_build:
image: maven:3.8.8-eclipse-temurin-21这里的:
image:不是:
docker build也不是每次重新构建一个 Maven 镜像。
它的意思是:
使用指定 Docker 镜像作为当前 Job 的容器执行环境。
例如:
image: node:22-alpine表示当前 Job 使用 Node.js 镜像执行。
5.4 image 不等于“本地有镜像就一定不拉取”
这个地方很容易理解错。
Docker Executor 默认的镜像拉取策略通常是:
pull_policy = "always"也就是说,即使本机已经存在对应镜像,Runner 默认仍会尝试从 Registry 拉取 / 检查镜像。
这和:
重新 docker build 镜像是两回事。
如果是可信的专用 Runner,希望优先复用本地镜像,可以在/etc/gitlab-runner/config.toml配置:
[runners.docker]
pull_policy = "if-not-present"逻辑变成:
本地有镜像
↓
直接使用
本地没有
↓
docker pull但共享 Runner 不建议随意使用 if-not-present,尤其是涉及私有镜像时,需要考虑镜像访问权限和安全问题。
5.5 Runner 默认镜像和 YAML image 的区别
注册 Docker Runner 时可能会配置:
[runners.docker]
image = "node:22-alpine"它表示:
Runner 默认镜像如果 Job 中没有:
image:就使用默认镜像。
如果 .gitlab-ci.yml 中明确指定:
image: maven:3.8.8-eclipse-temurin-8那么 Job 中的镜像优先。
关系:
.gitlab-ci.yml 中的 image
↓ 优先
Runner config.toml 默认 image
↓
Job 未指定 image 时使用因此注册 Runner 时填写:
node:22-alpine以后 Java Job 完全可以写:
image: maven:3.8.8-eclipse-temurin-21不冲突。
5.6 Shell Executor
Shell Executor 会直接在 Runner 所在宿主机执行命令。
例如:
job_deploy:
stage: deploy
tags:
- deploy
script:
- mkdir -p /opt/test
- cp dist/admin.jar /opt/test/admin.jar
- systemctl restart test这些命令本质上就是在宿主机执行:
mkdir -p /opt/test
cp dist/admin.jar /opt/test/admin.jar
systemctl restart test所以 Shell Executor 很适合:
部署文件
systemctl
Docker Compose
操作宿主机目录
执行部署脚本当前 GitLab 官方文档已将 Shell Executor 标记为 maintenance mode:仍会获得关键安全更新,但不再规划新功能。对于需要直接操作宿主机的内部部署场景仍然可以使用,但新项目的构建、测试任务更推荐 Docker 等隔离性更好的 Executor。
但是它的缺点也很明显:
依赖宿主机环境
隔离性弱
不同 Job 可能相互影响
需要自己维护 JDK / Maven / Node / Docker 等工具例如 Job 中执行:
mvn clean package宿主机就必须能够执行:
mvn -v否则会出现:
mvn: command not found5.7 Shell Runner 的权限问题
Shell Job 通常不是以你当前登录的 root 用户执行,而是由 Runner 服务对应的用户执行,例如:
gitlab-runner因此你手动执行成功:
cp xxx.jar /opt/test/不代表 CI 中一定成功。
可以检查:
id gitlab-runner目录权限:
ls -ld /opt/test如果部署目录需要 Runner 写入,应正确配置目录属主和权限,例如:
sudo chown -R gitlab-runner:gitlab-runner /opt/test相比在 Job 中大量使用:
sudo ...更推荐提前把部署目录、服务权限设计好。
Shell Runner 能直接操作宿主机,风险明显高于 Docker 构建 Runner。部署 Runner 建议限制项目、限制 tag,并结合 protected branch / protected runner 使用。
6、常见阶段如何选择 Executor
不同类型的任务,对执行环境的要求不同。选择 Runner 时,核心是根据任务特点选择合适的 Executor。
例如:
需要固定 JDK / Maven / Node 环境
↓
Docker Executor
需要直接操作宿主机目录或服务
↓
Shell Executor6.1 常见选择
6.2 推荐职责划分
普通 Java 项目推荐:
build
↓
Docker Runner
test
↓
Docker Runner
deploy
↓
Shell Runner也就是:
构建环境容器化
部署动作宿主机化这样职责最清晰。
7、多个 Runner 管理
7.1 不需要安装多套 GitLab Runner
一台 Linux 不需要:
安装一个 build 版 gitlab-runner
+
再安装一个 deploy 版 gitlab-runner只需要安装一次:
gitlab-runner然后多次注册:
sudo gitlab-runner register ...每注册一个 Runner,通常会在:
/etc/gitlab-runner/config.toml增加一个:
[[runners]]例如:
concurrent = 2
[[runners]]
name = "deploy-runner"
url = "http://gitlab.example.com"
token = "glrt-xxxx"
executor = "shell"
[[runners]]
name = "build-runner"
url = "http://gitlab.example.com"
token = "glrt-yyyy"
executor = "docker"
[runners.docker]
image = "maven:3.8.8-eclipse-temurin-8"
volumes = ["/cache"]结构可以理解为:
gitlab-runner 服务
│
└── config.toml
│
├── [[runners]] build-runner
│ └── docker
│
└── [[runners]] deploy-runner
└── shell7.2 不推荐手动复制 [[runners]]
虽然理论上可以直接编辑:
/etc/gitlab-runner/config.toml但创建新 Runner 时更推荐:
gitlab-runner register因为注册过程会正确完成:
Runner authentication
+
服务端关联
+
本地配置写入手动复制 Token 或配置容易造成:
Runner 身份混乱
Token 重复
配置和 GitLab UI 不一致
后续排查困难
7.3 常用 Runner 管理命令
查看已注册 Runner:
sudo gitlab-runner list验证 Runner:
sudo gitlab-runner verify查看版本:
gitlab-runner --version查看服务:
sudo systemctl status gitlab-runner取消注册时,应明确指定对应 Runner,不建议直接暴力删除整个 config.toml。
7.4 concurrent 是什么
config.toml 顶层可以看到:
concurrent = 2表示整个 gitlab-runner 进程最多同时执行多少个 Job。
例如:
concurrent = 2即使本机注册了:
build-runner
deploy-runner
vue-runner
test-runner整个 Runner 服务同时最多执行:
2 个 Job如果:
concurrent = 1那么多个 Runner 即使都在线,也只能一个 Job 执行完后再执行下一个。
所以:
Runner 数量和:
最大并发 Job 数量不是一个概念。
7.5 多个 Runner 可以使用相同 tag 吗
可以。
例如:
runner-01 → tag = build
runner-02 → tag = build
runner-03 → tag = buildJob:
tags:
- build那么 GitLab 可以把多个构建 Job 分配给这些可用 Runner。
这种方式适合扩容构建能力。
如果希望指定不同职责:
Java 构建 → java-build
Vue 构建 → vue-build
部署 → deploy就配置不同 tags。
8、推荐架构
对于普通 Java + Vue + Docker 部署项目,推荐把:
Build和:
Deploy分开。
8.1 推荐 Runner
同一台 Linux 服务器
├── build-runner
│ ├── tag = build
│ └── executor = docker
│
└── deploy-runner
├── tag = deploy
└── executor = shell如果前端构建也很多,可以进一步拆分:
├── java-build-runner
│ ├── tag = java-build
│ └── executor = docker
│
├── vue-build-runner
│ ├── tag = vue-build
│ └── executor = docker
│
└── deploy-runner
├── tag = deploy
└── executor = shell但是项目规模不大时,没有必要拆得太细。
8.2 Runner 职责建议
项目规模不大时,推荐至少拆成两个 Runner:
build-runner
├── executor = docker
├── tag = build
└── 负责:
├── Java / Maven 构建
├── Vue / Node 构建
├── 单元测试
└── 代码扫描
deploy-runner
├── executor = shell
├── tag = deploy
└── 负责:
├── 操作部署目录
├── systemctl
├── docker compose
└── 其他宿主机部署命令这样做的重点不是把 Runner 数量拆得越多越好,而是:
构建环境
和
宿主机部署权限
分开管理如果后期构建任务明显增多,再增加新的 Docker Runner 扩展构建能力即可。
8.3 整体架构
GitLab
│
根据 Runner 条件调度 Job
│
┌────────────┴────────────┐
│ │
build-runner deploy-runner
│ │
executor = docker executor = shell
│ │
Maven / JDK 容器 Linux 宿主机
│ │
Java / Vue 构建 部署 / 重启服务优点:
1. 构建环境干净
2. Maven / JDK / Node 版本容易控制
3. 不需要在宿主机安装多套构建环境
4. 构建 Runner 和部署 Runner 职责分离
5. 部署 Runner 权限可以单独控制
6. 后期可以独立增加构建 Runner 扩容8.4 不推荐的结构
不太推荐一个 Shell Runner 什么都做:
Shell Runner
│
├── Maven Build
├── Vue Build
├── Unit Test
├── Docker Build
└── Deploy因为最后服务器可能需要同时安装:
JDK 8
JDK 21
Maven
Node 18
Node 22
pnpm
Docker
各种扫描工具
...项目越多,宿主机环境越容易混乱。
更推荐:
编译 / 测试 → Docker Executor
部署 → Shell Executor8.5 Runner 常见问题排查顺序
Runner 出现 Pending、无法执行、环境异常等问题时,可以按下面顺序排查:
1. Runner 是否在线?
↓
GitLab 页面查看 Runner 状态
systemctl status gitlab-runner
2. GitLab Runner 服务是否正常?
↓
systemctl status gitlab-runner
journalctl -u gitlab-runner -n 100 --no-pager
3. Runner 是否已经正确注册?
↓
gitlab-runner list
gitlab-runner verify
4. config.toml 中 Executor 是否配置正确?
↓
/etc/gitlab-runner/config.toml
5. Docker Executor 是否能正常使用 Docker?
↓
docker version
docker images
docker pull <image>
11. concurrent / Runner limit 是否限制了并发?
↓
检查 config.toml
8.6 最后记住这 5 句话
1. GitLab 负责调度,GitLab Runner 负责真正执行 Job。
2. 一套 gitlab-runner 服务可以同时管理多个已注册 Runner。
3. 一个已注册 Runner 配置一种 Executor。
4. 多个 Runner 不代表多台服务器,同一台 Linux 可以注册多个 Runner。
5. 构建任务通常更适合 Docker Executor,需要直接操作宿主机的部署任务可以使用 Shell Executor。