1
0
0

GitLab Runner从一个 Job 一个 Runner的错误用法纠正

2026-10-13
2026-10-13
文章摘要
|

环境:自建 GitLab、GitLab Runner 19.3.1、Linux、Docker Executor / Shell Executor 关键词:GitLab CI/CD、Runner、Tag、Docker、Shell、release、unregister、403 Forbidden、config.toml

一、背景:最初的 Runner 设计

为了让不同任务相互隔离,我最初采用了一个 Runner 对应一个 Tag,每个 Job 指定对应 Tag的方式。Java、Vue 的构建与部署,以及 AI Review,都配置了独立 Runner。

执行 gitlab-runner list,可以看到本地注册配置(已省略认证 Token):

java-deploy          Executor=shell
build-runner-maven   Executor=docker
vue-build            Executor=docker
vue-deploy           Executor=shell
ai-review            Executor=docker
java-build           Executor=docker
vue-test             Executor=docker

可以把最初的映射关系理解为:

Runner 名称

Executor

示例 Tag

用途

java-deploy

shell

java-deploy

Java 部署

build-runner-maven

docker

maven-build

Maven 构建

vue-build

docker

vue-build

Vue 构建

vue-deploy

shell

vue-deploy

Vue 部署

ai-review

docker

ai-review

AI 代码评审

java-build

docker

java-build

Java 构建

vue-test

docker

vue-test

Vue 测试

表格中的 Tag 是为了说明旧设计的示例名称,实际 Tag 请以 GitLab 网页 Runner 配置为准。gitlab-runner list 只展示本地配置,不能证明各 Runner 的实际 Tag。

对应 .gitlab-ci.yml 可以像这样配置(示意,省略具体构建步骤):

stages:
  - build
  - test
  - deploy
​
java-build:
  stage: build
  tags:
    - java-build
  script:
    - mvn -B clean package -DskipTests
​
vue-test:
  stage: test
  tags:
    - vue-test
  script:
    - npm ci
    - npm run test
​
java-deploy:
  stage: deploy
  tags:
    - java-deploy
  script:
    - echo "这里执行实际的部署脚本"

这样做的好处是任务分工清晰,不同 Executor 可以按需准备环境,例如 Docker Executor 用于构建、Shell Executor 用于部署。但随着 Runner 数量增多,会出现注册项多、维护成本高、配置重复等问题。

二、理解 Runner、Tag、Job 的关系

  • Runner:实际领取并执行 CI/CD Job 的执行器注册对象。

  • Executor:Job 在哪里、以什么方式运行,比如 Docker 或 Shell。

  • Tag:GitLab 调度 Job 时使用的匹配条件,不是一个独立运行环境。

  • Job:.gitlab-ci.yml 中定义的具体任务。

一个 Runner 不必只配一个 Tag;一个 Tag 也不必只有一个 Runner。 一个 Runner 可以服务多个 Job,多个具备同样 Tag 的 Runner 也可以共同承接任务。

例如 Runner 配了 docker、java、build 三个 Tag,则 Job 指定 java、build 时可以匹配,因为 Runner 含有 Job 所要求的全部 Tag;如果 Job 还要求 gpu,则不能匹配。

需要特别注意:Tag 只是调度标签,不会改变 Executor,也不会让 Docker 构建容器自动获得宿主机部署权限。run untagged jobs 选项也会影响无 Tag Job 是否能被领取。

三、故障:在 GitLab 删除 Runner 后,unregister 返回 403

这次我准备清理已经不再使用的三个 Runner:

vue-deploy
java-build
vue-test

先在 GitLab 网页中删除了对应 Runner,随后在服务器上执行:

gitlab-runner unregister --name vue-deploy java-build vue-test

返回:

ERROR: Unregistering runner manager from GitLab forbidden
status=DELETE .../api/v4/runners/managers: 403 Forbidden
FATAL: failed to unregister runner

为什么报错?

gitlab-runner unregister 不只是删除本地的一行配置,它会向 GitLab API 发起注销请求。对于使用 GitLab UI/API 创建、再由 Runner 认证 Token 注册的 Runner,注销操作通常涉及删除 Runner Manager。

这次服务端 Runner 已经被删除,但 /etc/gitlab-runner/config.toml 仍留有对应的本地配置。客户端继续尝试通过 API 注销,就可能得到 403,导致本地配置未被自动移除。

不过,403 本身并不能单独证明一定是服务端已删除导致;权限、认证 Token 状态等也可能触发。这里是结合“刚在 GitLab 网页删除”的操作顺序,判断本地残留配置是首要排查方向。

还有一点:上面的 --name 用法也不对。--name 只接受一个 Runner 名称,不能追加三个名称来一次注销。正常应逐条调用:

gitlab-runner unregister --name vue-deploy
gitlab-runner unregister --name java-build
gitlab-runner unregister --name vue-test

但在服务端已删除的情况下,没有必要继续反复执行 unregister。

四、解决方法一:官方命令清理失效配置

首先备份本地配置:

sudo cp /etc/gitlab-runner/config.toml \
  /etc/gitlab-runner/config.toml.bak

确认网络连通且 GitLab 服务端运行正常后,先执行验证:

sudo gitlab-runner verify

GitLab 官方文档提供了自动清理服务端已删除 Runner 的命令:

sudo gitlab-runner verify --delete

注意:该命令会修改本地 config.toml,不是按名称选择三个 Runner 删除。 执行前必须备份;如果网络异常、认证失败,先排除这些问题,以免误清理本来正常的 Runner。

验证结果:

sudo gitlab-runner list

如果已经只剩下需要保留的 Runner,就完成了本地清理。-

五、解决方法二:手动精准删除指定 Runner

故障处理阶段,明确需要清理 vue-deploy、java-build、vue-test 等已在服务端删除的配置,因此手动删除对应配置块更可控。之后又进一步合并执行资源,最终只保留两种 Runner:Docker 和 Shell。

先备份:

sudo cp /etc/gitlab-runner/config.toml \
  /etc/gitlab-runner/config.toml.bak

打开配置文件:

sudo nano /etc/gitlab-runner/config.toml

配置结构大致如下:

concurrent = 4
​
[[runners]]
  name = "java-deploy"
  url = "http://192.168.20.111"
  executor = "shell"
  # 其他配置(含 token)省略
​
[[runners]]
  name = "vue-deploy"
  url = "http://192.168.20.111"
  executor = "shell"
  # 其他配置(含 token)省略

只删除目标 Runner 的整个 [[runners]] 块,包括该块内部的 [runners.docker]、[runners.cache] 等子配置(如果有),不要误删其他 Runner,也不要把有效的全局配置删掉。

保存后检查:

sudo gitlab-runner list
sudo gitlab-runner verify

如果仅处理最初的三个无效 Runner,阶段性可能剩下:

java-deploy          Executor=shell
build-runner-maven   Executor=docker
vue-build            Executor=docker
ai-review            Executor=docker

注意:这不是最终架构。 后续还要把构建任务、测试任务、AI Review 合并到同一个 Docker Runner,把发布任务统一交给 Shell Runner。完成迁移后再清理多余 Runner,最终以两个 Runner 为准。

Runner 通常会自动检测配置变化;为方便现场验证,也可以在确认当前没有关键 Job 运行时重启服务:

sudo systemctl restart gitlab-runner
sudo systemctl status gitlab-runner

如果编辑失误,可以从备份还原,然后再检查:

sudo cp /etc/gitlab-runner/config.toml.bak \
  /etc/gitlab-runner/config.toml
sudo systemctl restart gitlab-runner

六、最终架构:两个 Runner,按执行方式分工

经历最初的“一个 Runner 对应一个 Tag、每个 Job 都指定专属 Tag”后,我最终把配置收敛为 两个 Runner:

Runner(逻辑名称)

Executor

Tag

用途

Docker Runner

docker

docker

Java/Maven 构建、Vue 构建与测试、AI Review 等容器化任务

Shell Runner

shell

shell、release

发布与部署,必要时操作宿主机 Docker 或部署目录

这里的 Runner 名称是便于理解的逻辑名称,实际名称以你的最终 GitLab 配置为准。按你的最终设计,Docker Runner 使用 docker Tag;Shell Runner 使用 shell 与 release 两个 Tag。

架构如下:

GitLab CI/CD Pipeline
         │
         ├── build / test / ai-review
         │          │
         │       tag: docker
         │          │
         │    Docker Runner (docker executor)
         │
         └── release / deploy
                    │
                 tag: release
                    │
             Shell Runner (shell executor)

6.1 为什么只需要两个 Runner?

多个 Job 可以共用一个 Runner,并不要求“一 Job 一 Runner”。只要 Runner 拥有 Job 需要的全部 Tag,并且运行环境可用,就能够领取该 Job。比如 Java 构建和 Vue 构建虽然使用不同镜像,但均可由同一个 Docker Executor Runner 执行。

要注意: tags: [shell, release] 表示 Runner 必须同时具有这两个 Tag,并非二选一。如果发布 Job 只写 release,同样能匹配具有 shell 和 release 的 Shell Runner。

6.2 GitLab Runner 配置示意

/etc/gitlab-runner/config.toml 中最终只需要保留两个 [[runners]] 块。以下是结构示例,不是可直接覆盖的完整生产配置:

concurrent = 2
​
[[runners]]
  name = "docker-runner"
  url = "http://192.168.20.111"
  executor = "docker"
  # token 等敏感配置省略
  [runners.docker]
    image = "maven:3.9-eclipse-temurin-8"
​
[[runners]]
  name = "shell-runner"
  url = "http://192.168.20.111"
  executor = "shell"
  # token 等敏感配置省略

Runner Tag 通常在 GitLab 网页的 Runner 设置中配置。 config.toml 里的 name、executor 并不能替代 GitLab 上的 Tag 配置,也不能根据这份示例推断实际 Token 或 Runner ID。

6.3 .gitlab-ci.yml 配置示例

下面是一份展示“Docker 构建 + Shell 发布”分工的简化 CI 配置:

stages:
  - build
  - release
​
java-build:
  stage: build
  tags:
    - docker
  image: maven:3.9-eclipse-temurin-8
  script:
    - mvn -B clean package -DskipTests
  artifacts:
    paths:
      - target/*.jar
​
vue-build:
  stage: build
  tags:
    - docker
  image: node:20-alpine
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
​
java-release:
  stage: release
  tags:
    - release
  dependencies:
    - java-build
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: manual
  script:
    - echo "在受保护的发布 Runner 上执行部署命令"
    # 正式环境请替换为经过审查的部署脚本

Java 构建示例使用 Java 8 镜像;maven:3.9-eclipse-temurin-8 和 node:20-alpine 都是 Docker 镜像,不是 Java 8 自带组件。Vue 构建镜像版本应以项目真实的 Node.js 要求为准。示例为说明调度,省略了具体部署、制品校验及回滚逻辑。

注意一个容易踩的坑: 上面的 image: 对 Docker Executor 有效,但 Shell Executor 不会因此在容器内运行,它直接使用宿主机环境。Shell Runner 需要事先安装好部署脚本依赖、Docker CLI 等工具,并配置最小必要权限。

6.4 最终方案的好处与注意事项

  • 配置少: 不再为 Java 构建、Vue 构建、测试、AI Review 分别注册 Runner。

  • 职责明确: docker 负责容器化 CI 任务,release 负责发布部署。

  • 镜像隔离: Docker Executor 的各个 Job 可以按需指定 Maven 或 Node 镜像。

  • 避免 Tag 膨胀: 新增同类型 Job 直接复用现有 Tag,不需要重新注册 Runner。

  • 生产安全: Shell Executor 能接触宿主机,release Runner 应限制到可信项目和受保护分支/Tag,并妥善保护部署密钥。

  • 容量限制: 两个 Runner 不代表只能同时运行两个 Job;具体并发还取决于 concurrent、Runner 的 limit、机器资源和构建负载。反过来,仅一个 Docker Runner 也可能形成构建排队瓶颈,需要监控再决定是否横向扩容。

6.5 迁移时的正确顺序

  1. 在 GitLab 中确认 Docker Runner 的 Tag 为 docker,Shell Runner 具有 shell、release。

  2. 将旧的 java-build、vue-build、vue-test、ai-review 等 Job 的 tags 改为 docker。

  3. 将 Java/Vue 发布 Job 的 tags 改为 release(需要时也可同时写 shell)。

  4. 触发流水线,确认 Job 已由预期 Executor 执行,发布权限和部署脚本正常。

  5. 确认没有 Job 依赖旧 Tag,再从 GitLab 删除冗余 Runner、清理本地配置,避免 Job 长期 Pending。

  6. 执行 gitlab-runner list 与 gitlab-runner verify,确认本地只保留两个正常的 Runner。

七、总结

这次故障不是 Runner 程序异常,而是GitLab 服务端 Runner 生命周期与本地 config.toml 管理没有同步完成。

  • gitlab-runner list:看本地登记了哪些 Runner。

  • gitlab-runner verify:验证本地 Runner 是否能与服务端交互。

  • gitlab-runner unregister --name xxx:尝试通过 API 注销指定 Runner。

  • gitlab-runner verify --delete:清理已删除的失效 Runner 本地配置,执行前先备份。

  • 手动编辑 /etc/gitlab-runner/config.toml:明确只需移除少数已在服务端删除的 Runner 时,更方便精准控制。

最终架构从 7 个专用 Runner 收敛为 2 个通用 Runner:Docker(docker)和 Shell(shell、release)。最终体会:Tag 是 Job 调度条件,Runner 才是执行资源;按执行环境和权限边界组织 Runner,比机械地让每个 Job 对应一个 Runner 更容易维护。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

评论