GitLab Runner 三种类型详解:从共享到项目专属的CI/CD执行器部署指南

GitLab Runner 三种类型详解:从共享到项目专属的CI/CD执行器部署指南
1. 从手动到自动为什么我们需要 GitLab Runner如果你用过 GitLab肯定知道它的 CI/CD 功能很强大能把代码提交、测试、构建、部署这些繁琐的活儿串起来。但很多刚开始接触的朋友会卡在一个地方我写的.gitlab-ci.yml流水线脚本到底是在哪里执行的呢答案就是GitLab Runner。你可以把它理解成一个“工人”GitLab 这个“调度中心”把任务流水线作业派发给它它领了任务就去干活。那么问题来了这个“工人”住在哪里用什么姿势干活这就是标题里说的“三种 Runner”要解决的问题。在 GitLab 的世界里Runner 主要分三种共享 Runner、群组 Runner和项目 Runner。它们的区别核心在于“管辖范围”和“资源隔离”。用个不太严谨但好懂的比喻共享 Runner 像公司公用的那几台高配电脑谁有急活都能用但可能得排队环境也杂群组 Runner 像你们部门自己买的一台服务器只有部门里的人能用配置和环境可以按部门需求来项目 Runner 就更私密了像是给你这个项目单独配的一台虚拟机完全独享想怎么折腾都行。搞清楚这三种 Runner 的创建和使用你才能真正掌控 CI/CD 的执行环境避免“在我机器上好好的一上流水线就挂”的尴尬。接下来我们就抛开官方文档那套理论从实际运维和开发的角度把这三种 Runner 的创建、配置、使用场景和那些容易踩的坑给你掰开揉碎了讲清楚。2. 三种 Runner 的本质区别与选型指南在动手创建之前我们必须先彻底理解这三种 Runner 在设计上的根本差异。这决定了你该在什么场景下用哪一种而不是拍脑袋随便选。2.1 共享 Runner公司的“公共服务”共享 Runner 是注册在 GitLab 实例级别对于自托管 GitLab或是在 GitLab.com 上由 GitLab 官方提供的 Runner。它的最大特点是对所有拥有访问权限的项目可见。核心特性与适用场景全局可见性一旦管理员创建并注册了一个共享 Runner该 GitLab 实例下的所有项目在流水线设置里都能看到它并可以选择使用。资源池化它非常适合用来跑一些通用的、轻量级的任务比如代码风格检查Lint、依赖安装、单元测试等。这些任务对运行环境要求不高且执行频繁。管理成本低由平台或运维团队统一维护、升级、打补丁项目开发者无需关心 Runner 本身的状态。潜在问题因为是共享的所以可能面临资源竞争。如果某个项目提交了一个非常耗时的构建任务它可能会阻塞其他项目的流水线。此外环境隔离是个挑战。虽然 Docker 执行器可以提供一定的隔离但宿主机资源如网络、磁盘I/O仍然是共享的。注意对于安全性要求高的项目例如需要访问内部数据库、特定密钥通常不建议使用共享 Runner因为你无法完全控制其运行环境和其他可能并行的任务。2.2 群组 Runner部门的“专有资产”群组 Runner 注册在某个特定的 GitLab 群组下。这个群组下的所有项目以及其子群组下的所有项目都可以使用这个 Runner。核心特性与适用场景范围可控完美解决了共享 Runner 范围过广的问题。你可以为“前端组”、“后端微服务组”或“移动端组”分别创建各自的群组 Runner。环境定制可以为特定群组的工作流定制 Runner 环境。例如为 Android 开发群组配置一个安装了完整 Android SDK、NDK 和特定版本 Gradle 的 Runner为数据科学群组配置一个带有 Python 科学计算库和 GPU 支持的 Runner。资源隔离与成本分摊资源在群组内共享避免了公司级别的资源竞争同时又比给每个项目单独配置 Runner 更经济。运维团队可以将群组 Runner 部署在对应的部门网络或VPC内方便访问内部资源。权限管理群组管理员负责 Runner 的维护项目开发者只有使用权。这实现了运维和开发的权责分离。2.3 项目 Runner项目的“私人订制”项目 Runner顾名思义只属于某一个特定的 GitLab 项目。只有这个项目能使用它。核心特性与适用场景最高隔离度资源完全独享不受其他任何项目干扰。这对于构建耗时极长、资源消耗巨大例如编译大型C项目、训练机器学习模型或对执行环境有极端定制化需求的任务至关重要。极致定制化你可以为这个 Runner 安装任何项目所需的特殊软件、驱动、证书而不用担心影响其他项目。安全边界清晰Runner 可以部署在项目专属的安全域内甚至与项目生产环境网络互通方便进行集成测试或部署同时最大程度减少暴露面。管理成本最高每个项目 Runner 都需要单独维护、监控。如果公司有上百个项目每个都配一个专属 Runner运维复杂度会指数级上升。选型决策流程图心智模型任务是否需要访问敏感信息如生产数据库密钥、内部包仓库凭证是 - 考虑项目 Runner或部署在安全区域的群组 Runner。否 - 进入下一步。任务是否非常耗时30分钟或消耗大量资源CPU/内存/GPU是 - 强烈建议使用项目 Runner避免阻塞他人。否 - 进入下一步。是否有多个项目需要相似的特殊环境如特定的编译器版本、运行时是 -群组 Runner是最佳选择一次配置多处使用。否 - 进入下一步。任务是否通用、轻量且频繁如 lint, unit test是 -共享 Runner足矣管理最省心。3. 实战手把手创建与注册三种 Runner理论懂了我们直接上实操。这里假设你有一个自托管的 GitLab 实例版本 14.x并且你拥有相应级别的管理员或维护者权限。我们将从准备 Runner 主机开始一步步完成注册。3.1 第一步准备 Runner 主机与安装 gitlab-runner无论哪种 Runner其本体都是一个叫做gitlab-runner的可执行程序。我们需要先找一台机器物理机、虚拟机、容器均可来安装它。1. 主机选择考量操作系统Linux 是最常见和最佳支持的选择如 Ubuntu, CentOS, RHEL。macOS 和 Windows 也可但通常用于特定平台的项目构建。资源根据你预计要运行的流水线作业来配置 CPU、内存和磁盘。例如前端构建可能需要大内存C编译需要多核CPU。网络Runner 主机必须能访问你的 GitLab 服务器用于接收任务和上报结果并且可能需要访问互联网下载依赖或内网访问私有仓库。2. 安装 gitlab-runner以 Ubuntu 22.04 为例# 1. 添加官方仓库 sudo curl -L --output /usr/share/keyrings/gitlab-runner-archive-keyring.gpg https://packages.gitlab.com/runner/gitlab-runner/gpgkey echo deb [signed-by/usr/share/keyrings/gitlab-runner-archive-keyring.gpg] https://packages.gitlab.com/runner/gitlab-runner $(lsb_release -cs) main | sudo tee /etc/apt/sources.list.d/gitlab-runner.list /dev/null # 2. 更新并安装 sudo apt-get update sudo apt-get install gitlab-runner # 3. 验证安装 gitlab-runner --version安装完成后gitlab-runner会作为一个系统服务运行。你可以用sudo systemctl status gitlab-runner查看状态。3.2 第二步获取注册令牌注册 Runner 时需要一个“密码”来向 GitLab 证明身份这就是注册令牌。三种 Runner 的令牌获取位置不同。共享 Runner 令牌 进入 GitLab 管理区域Admin Area。路径左上角菜单 - Admin - Overview - Runners。在页面右侧你会看到“Register an instance runner”按钮下方显示Registration token。这个令牌需要 GitLab 实例的管理员权限才能看到。群组 Runner 令牌 进入目标群组的主页。路径进入该群组 - 左侧边栏 Settings - CI/CD。展开Runners部分你会看到“Register a group runner”区域其中包含Registration token。需要群组所有者Owner或维护者Maintainer权限。项目 Runner 令牌 进入目标项目的主页。路径进入该项目 - 左侧边栏 Settings - CI/CD。展开Runners部分在 “Specific runners” 区域找到Registration token。需要项目维护者Maintainer及以上权限。实操心得务必妥善保管这些令牌它们相当于给 Runner 发放进入对应范围的“门票”。建议在注册完成后可以考虑在 GitLab 界面上点击“重置令牌”使旧的令牌失效这是一个好的安全实践。3.3 第三步执行注册命令在安装好gitlab-runner的主机上执行注册命令。这是一个交互式过程但我们可以用一条命令带全参数来完成。以下示例假设你的 GitLab 地址是https://gitlab.mycompany.com。注册一个共享 Runnersudo gitlab-runner register \ --non-interactive \ --url https://gitlab.mycompany.com/ \ --registration-token YOUR_SHARED_RUNNER_TOKEN \ --executor docker \ --docker-image alpine:latest \ --description docker-shared-runner-01 \ --tag-list shared,linux,docker \ --run-untaggedtrue--executor “docker”指定使用 Docker 执行器这是最常用、隔离性最好的方式。每个 CI 作业都会在一个全新的容器中运行。--docker-image “alpine:latest”指定默认的 Docker 镜像。如果项目中的.gitlab-ci.yml没有指定image则会使用这个。--description给你的 Runner 一个易于识别的描述。--tag-list给 Runner 打上标签。标签是用于在流水线中定向选择 Runner 的关键机制。这里我们打了shared, linux, docker。--run-untagged“true”这是共享 Runner 的关键设置。意味着那些没有指定任何标签tags的作业也会被这个 Runner 执行。这确保了所有项目最基本的流水线都能跑起来。注册一个群组 Runnersudo gitlab-runner register \ --non-interactive \ --url https://gitlab.mycompany.com/ \ --registration-token YOUR_GROUP_RUNNER_TOKEN \ --executor shell \ --description “group-runner-for-android” \ --tag-list “android, heavy-build” \ --run-untagged“false”这里我们使用了--executor “shell”。对于 Android 构建有时需要直接使用宿主机环境因为涉及 USB 调试、硬件加速等Shell 执行器更合适。你需要确保宿主机上已安装好所有 Android 构建工具。--tag-list我们打了android, heavy-build。--run-untagged“false”这是群组和项目 Runner 的典型设置。这意味着只有明确指定了android或heavy-build标签的作业才会被这个 Runner 执行。这实现了资源的精准调度。注册一个项目 Runnersudo gitlab-runner register \ --non-interactive \ --url https://gitlab.mycompany.com/ \ --registration-token YOUR_PROJECT_RUNNER_TOKEN \ --executor “docker” \ --docker-image “ubuntu:22.04” \ --docker-volumes “/var/run/docker.sock:/var/run/docker.sock” \ --docker-volumes “/cache” \ --description “project-runner-for-data-pipeline” \ --tag-list “data-pipeline, gpu” \ --run-untagged“false”--docker-volumes这里我们挂载了两个卷。第一个docker.sock是著名的 “Docker in Docker” 模式允许 CI 作业在容器内继续运行 Docker 命令用于构建镜像等。这是一个需要谨慎评估安全性的操作。第二个/cache是挂载一个缓存卷用于在不同作业之间缓存依赖如 pip packages, npm modules可以显著加速流水线。--tag-list我们打了>android-build: stage: build tags: - android # 这个标签匹配我们之前注册的群组 Runner - heavy-build script: - ./gradlew assembleRelease only: - main这个android-build作业只会被那些打了android和heavy-build标签的 Runner 执行也就是我们之前注册的那个群组 Runner。示例 2使用项目 Runner 进行数据训练train-model: stage: train tags: ->lint: stage: test # 不指定 tags或者指定一个非常通用的标签 tags: - shared script: - npm run lint这个作业没有特殊标签要求或者指定了共享 Runner 也拥有的通用标签shared。因此它会被配置为run-untaggedtrue的共享 Runner或者任何拥有shared标签的 Runner 执行。4.2 配置详解与高级用法1. Runner 锁定与解锁在 Runner 的管理页面你可以看到一个Lock to current projects的选项。这对于项目 Runner是自动且强制的它只能被锁定的项目使用。对于群组 Runner你可以选择锁定它这样只有当前群组下的项目能使用即使其他项目知道了它的标签也无法使用增加了安全性。2. 执行器选择与配置我们之前用了docker和shell。还有其他执行器kubernetes在 K8s 集群中动态创建 Pod 来运行作业弹性极佳。ssh在远程机器上通过 SSH 执行命令。virtualbox,parallels用于需要完整虚拟机的场景。 选择哪种取决于你对隔离性、启动速度、环境依赖的要求。Docker 是目前最均衡和流行的选择。3. 缓存与制品为了让流水线更快需要合理配置cache和artifacts。cache用于存储作业间的中间文件如依赖包artifacts用于传递作业生成的最终文件如构建好的二进制包。Runner 的配置如我们之前挂载的/cache卷和.gitlab-ci.yml中的定义需要配合使用。cache: key: “$CI_COMMIT_REF_SLUG” # 按分支缓存 paths: - node_modules/ - .gradle/wrapper/ build-job: stage: build script: - npm install # 如果缓存命中这步会极快 - npm run build artifacts: paths: - dist/ # 将构建产物传递给后续的部署作业5. 运维、监控与避坑指南Runner 创建好并跑起来只是开始长期的稳定运行更需要运维功夫。5.1 日常维护操作查看 Runner 状态sudo gitlab-runner list # 列出本机所有已注册的 Runner sudo gitlab-runner verify # 检查 Runner 与 GitLab 的连接状态 sudo systemctl status gitlab-runner # 查看服务状态更新 gitlab-runner定期更新以获得新功能和安全补丁。sudo apt-get update sudo apt-get install gitlab-runner。查看日志日志是排错的生命线。sudo journalctl -u gitlab-runner.service -f可以实时跟踪日志。5.2 常见问题与排查思路问题 1作业一直处于pending状态。可能原因 1没有符合条件的 Runner。检查作业的tags是否与任何在线 Runner 的标签匹配。检查 Runner 是否被禁用Settings - CI/CD - Runners页面查看。可能原因 2Runner 虽然在线但并发数已满。每个 Runner 有一个concurrent配置在/etc/gitlab-runner/config.toml中表示它能同时执行多少个作业。如果所有槽位都被占用新作业就会排队。排查命令在 Runner 主机上sudo gitlab-runner run以调试模式运行可以查看详细的作业拉取和执行日志。问题 2作业失败报错“准备环境失败”或“拉取镜像失败”。可能原因 1网络问题。Runner 主机无法访问 Docker Hub 或内部镜像仓库。检查网络连接和 DNS。可能原因 2对于 Shell 执行器可能是宿主机缺少必要的命令或权限。确保gitlab-runner用户通常是gitlab-runner有权限执行脚本中所需的命令。可能原因 3Docker 执行器权限问题。如果使用docker.sock绑定确保gitlab-runner用户属于docker用户组。问题 3缓存没有生效每次都要重新下载依赖。可能原因 1cache:key配置不合理。如果 key 设置成了“$CI_COMMIT_REF_NAME”默认那么不同分支的缓存是隔离的。可以尝试使用key: “global-cache”来全局共享但要注意不同项目或分支的依赖冲突。可能原因 2Runner 配置的缓存目录没有正确挂载或权限不足。检查 Runner 配置文件中[runners.docker]或[runners.cache]部分的volumes和cache_dir设置。5.3 安全最佳实践最小权限原则为 Runner 配置的服务账户如gitlab-runner用户只赋予其执行任务所需的最小权限。避免使用 root 用户。谨慎使用 Docker Socket 绑定docker.sock绑定赋予了容器内几乎等同于宿主机的 Docker 控制权。如果必须使用应确保该 Runner 为受信任的、隔离的项目专用并且 CI 脚本来源可信。使用私有镜像仓库避免从公共仓库拉取不可信的镜像。配置 Runner 使用内部私有仓库并设置镜像拉取策略。定期轮换注册令牌如前所述定期在 GitLab 界面上重置 Runner 的注册令牌。隔离网络将 Runner 部署在独立于核心生产环境的网络区域通过防火墙策略严格控制其网络访问。6. 性能调优与成本控制当你的 CI/CD 流水线越来越多Runner 的性能和成本就成了必须考虑的问题。6.1 Runner 并发与资源分配在 Runner 的配置文件/etc/gitlab-runner/config.toml中concurrent参数控制全局并发作业数。[[runners]]部分下的limit参数可以限制单个 Runner 的并发数。concurrent 10 # 这台主机上所有 Runner 最多同时运行 10 个作业 [[runners]] name “my-docker-runner” limit 4 # 这个特定的 Runner 最多同时运行 4 个作业 executor “docker” [runners.docker] ...设置时需要平衡宿主机的资源CPU、内存、磁盘 I/O。过高的并发会导致所有作业都变慢甚至因内存不足而失败。一个经验法则是并发数不要超过宿主机的 CPU 核心数并预留足够的内存给每个作业和宿主机系统。6.2 基于 Kubernetes 的弹性伸缩对于云原生环境使用kubernetes执行器是解决资源弹性问题的最佳方案。你可以配置 GitLab Runner 在 K8s 集群中运行并利用autoscaler根据待处理的作业队列长度动态地创建和销毁运行作业的 Pod。这实现了真正的“用多少起多少”极大优化了资源利用率和成本。配置相对复杂需要在config.toml中定义[runners.kubernetes]部分并指定命名空间、服务账户、Pod 模板等。但一旦配置成功你将获得一个几乎无限弹性、按需付费的 Runner 池。6.3 分层 Runner 策略一个成熟的做法是采用分层 Runner 策略第一层共享 Runner使用轻量级、无状态的 Docker 镜像如alpine处理海量的代码检查、单元测试等短时任务。可以部署在公有云 Spot 实例上以降低成本。第二层群组 Runner使用定制化镜像处理中等耗时的构建和集成测试。根据群组负载采用固定数量的虚拟机或 K8s 节点池。第三层项目 Runner使用高性能专用主机或带特殊硬件如 GPU的实例处理耗时极长的编译、训练或部署任务。按需启动任务完成后可以关机。通过这种分层你可以确保快速反馈的流水线阶段如 lint不被重型任务阻塞同时又能为特殊任务提供充足的资源。7. 从配置管理到自动化部署手动在一台台机器上安装和注册 Runner 在规模小时可行但一旦 Runner 数量增多就需要配置管理工具。7.1 使用 Ansible 批量部署 RunnerAnsible 是自动化 Runner 部署的利器。你可以编写一个 Playbook完成从安装软件、配置到注册的全过程。# gitlab-runner-deploy.yml - hosts: runner_hosts become: yes vars: gitlab_url: “https://gitlab.mycompany.com” runner_token: “{{ vault_runner_token }}” # 令牌应从加密的 vault 中读取 runner_description: “{{ inventory_hostname }}-docker-runner” runner_tags: “docker, linux” tasks: - name: Add GitLab Runner repository key apt_key: url: https://packages.gitlab.com/runner/gitlab-runner/gpgkey state: present - name: Add GitLab Runner repository apt_repository: repo: “deb https://packages.gitlab.com/runner/gitlab-runner {{ ansible_distribution_release }} main” state: present update_cache: yes - name: Install GitLab Runner apt: name: gitlab-runner state: latest update_cache: yes - name: Register GitLab Runner (non-interactive) command: gitlab-runner register --non-interactive --url “{{ gitlab_url }}” --registration-token “{{ runner_token }}” --executor “docker” --docker-image “alpine:latest” --description “{{ runner_description }}” --tag-list “{{ runner_tags }}” --run-untagged “false” args: creates: /etc/gitlab-runner/config.toml # 避免重复注册 notify: restart gitlab-runner handlers: - name: restart gitlab-runner systemd: name: gitlab-runner state: restarted daemon_reload: yes这样你只需要维护一个主机清单和令牌变量就能一键部署几十上百台 Runner。7.2 容器化 Runner 与动态注册更云原生的做法是将gitlab-runner本身也容器化并在 Kubernetes 中作为 DaemonSet 或 Deployment 运行。GitLab 官方提供了gitlab/gitlab-runner镜像。结合 K8s 的 Secrets 来管理注册令牌可以实现 Runner 的动态伸缩和故障自愈。这种模式下Runner 的生命周期完全由 K8s 管理。当 Pod 被调度到新节点上时它会自动从 Secret 中读取令牌并向 GitLab 注册自己。当 Pod 被销毁时GitLab 也会自动清理掉离线的 Runner。这实现了最高级别的自动化和弹性。无论是选择传统的配置管理还是云原生的容器化方案目标都是将 Runner 的部署和管理变成一种可重复、可审计、自动化的基础设施即代码流程从而让你能从繁琐的运维中解放出来更专注于流水线逻辑和业务代码本身。