找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3464

积分

0

好友

474

主题
发表于 4 天前 | 查看: 14| 回复: 0

在私有化环境里稳定运行 GitHub Actions 自托管 Runner,难的不是让它启动,而是如何长期、有序地管理。手动执行几次 config.sh 脚本将 Runner 挂载到仓库或组织,在早期可能可行。但当你需要同时维护多个 Runner、不同标签和多个仓库,尤其是在内网宿主机或容器环境中运行时,管理上的挑战会迅速凸显:

  • Token 分散各处,其过期时间、复用性及权限范围需要不断确认。
  • 宿主机或容器重启后,Runner 可能无法自动恢复,导致 Workflow 在队列中等待可用的 Runner。
  • Runner 进程看起来还在,但就是不接收任务,排查时需要同时检查目录、进程状态并对照 GitHub 界面。
  • 多个 Runner 共享宿主机环境可能导致互相污染,一个任务对环境变量的修改可能影响另一个任务。
  • Docker 任务与非 Docker 任务的边界变得模糊,不该使用 Docker 的任务可能意外调用,而需要 Docker 的任务又可能无法运行。

这些痛点促使我将管理 GitHub 自托管 Runner 的经验沉淀下来,整理成了一个名为 Runner Fleet 的开源工具。它的核心目标是:将 Runner 视为服务来管理,而非一堆零散的目录

我希望它能减少手动操作和踩坑,将 1~5 台机器上的自托管 Runner 部署、扩容与维护工作,收敛为一套可复用的工程流程。前期易于上手,后期在需要提高并发度、提升资源利用率或扩大吞吐量时,也不至于失去控制。

Runner Fleet开源项目主页截图

Runner Fleet 的功能非常明确:提供一个轻量级的 Web 管理界面,将 Runner 的安装、注册、启动/停止、编辑、状态监控以及自愈巡检,统一收归到一套可重复的流程中。

同时,它提供两种运行模式:既可以直接在宿主机上运行多个 Runner(非容器模式),以最大化复用宿主机环境;也可以实现“一个 Runner 一个容器”(容器模式),在同一台物理机上通过容器实现环境隔离。你可以根据私有化环境的具体需求灵活选择。如果你也在使用自托管 Runner 来运行 CI/CD,并希望提升其管理效率,欢迎在 开源实战 板块探索更多类似的项目与最佳实践。

构建一套可复用的管理范式

自托管 Runner 容易失控的根源往往不在于规模大小,而在于缺乏一套稳定的管理范式。我推荐将 Runner 的划分逻辑从“按仓库”改为“按职责”,让每个 Runner 的环境边界足够清晰。这样在出现问题时可以快速定位,迁移或扩容也会更加容易。

通常可以按职责进行如下拆分:

  • 通用型 (General): 仅负责拉取代码、编译、单元测试、代码检查 (Lint) 等任务,尽量不依赖 Docker 或复杂的宿主环境。
  • 容器型 (Docker): 专门用于运行容器构建与镜像发布任务,明确且可控地提供 Docker 能力。
  • 重型任务型 (Heavy): 处理重型编译或大内存消耗的任务,将其独立出来可以避免拖慢整体 CI/CD 队列的速度。

Runner Fleet 的设计天然适配这种范式,因为它将每一个 Runner 都视为一个具有完整生命周期的配置对象,而不仅仅是你记忆中的某台机器上的某个目录。

本文将重点介绍在 容器场景 下的使用方法,其他模式(如裸金属部署、DinD 模式)我们将在后续有机会时分享。

服务器环境准备

服务器配置可参考相关环境搭建指南。核心步骤是更新 Ubuntu 系统,安装 Docker Engine 与 Compose 插件,并配置当前用户免 sudo 使用 Docker:

sudo apt update && sudo apt upgrade -y

sudo apt install -y ca-certificates curl gnupg lsb-release

curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/ \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

sudo gpasswd -a ${USER} docker

安装完成后,系统可能会提示需要重启:

*** System restart required ***

执行 sudo reboot 完成重启,一个能够运行 GitHub Actions Runner 的基础服务器环境就准备就绪了。

Runner Fleet 全容器环境部署

选择容器模式并非为了追求“云原生”的外观,而是为了解决以下三个实际痛点:

  1. 避免不同 Runner 之间的环境互相污染。
  2. 避免在同一台机器上升级或回滚多个 Runner 时,误操作彼此的目录。
  3. 为运行可能不完全可信的 Job 提供更严格的隔离边界。

部署的第一步是从 GitHub Release 页面获取最新版本的镜像并下载:

docker pull ghcr.io/soulteary/runner-fleet:v1.1.0
docker pull ghcr.io/soulteary/runner-fleet:v1.1.0-runner

创建独立的 Docker 网络:

docker network create runner-net

创建两个必要的目录并设置正确的权限:

mkdir -p config && chown 1001:1001 config
mkdir -p runners && chown 1001:1001 runners

接着,创建容器使用的基础环境变量配置文件 .env

# 此处账号/密码换成自己需要的,仅为示例
BASIC_AUTH_USER=soulteary
BASIC_AUTH_PASSWORD=soulteary

# 使用镜像
MANAGER_IMAGE=ghcr.io/soulteary/runner-fleet:v1.1.0
RUNNER_IMAGE=ghcr.io/soulteary/runner-fleet:v1.1.0-runner   

# 固定配置
CONTAINER_MODE=true
CONTAINER_NETWORK=runner-net
JOB_DOCKER_BACKEND=host-socket
RUNNERS_BASE_PATH=/app/runners
SERVER_PORT=8080
SERVER_ADDR=0.0.0.0

然后,执行命令将宿主机的 Docker 组 ID (GID) 添加到 .env 文件中:

DOCKER_GID=$(getent group docker 2>/dev/null | cut -d: -f3)
[ -n "$DOCKER_GID" ] && echo "DOCKER_GID=$DOCKER_GID" >> .env

再将宿主机上 runners 目录的绝对路径也写入 .env

VOLUME_HOST_PATH=$(realpath runners 2>/dev/null || (cd runners 2>/dev/null && pwd -P))
echo "VOLUME_HOST_PATH=$VOLUME_HOST_PATH" >> .env

最后,编写核心的 docker-compose.yml 文件:

services:
  runner-manager:
    # 默认使用稳定版 v1.0.0;开发/尝鲜可设 .env:MANAGER_IMAGE=ghcr.io/soulteary/runner-fleet:main
    image: ${MANAGER_IMAGE:-ghcr.io/soulteary/runner-fleet:v1.0.0}
    container_name: runner-manager
    # 使用 job_docker_backend: dind 时请先启动 DinD:docker compose --profile dind up -d
    # 容器模式下必须用宿主机 socket,否则 Manager 无法创建 Runner 容器;默认 unix,勿改为 tcp://runner-dind:2375
    # 全容器时可通过 .env 传入以下变量覆盖 config/config.yaml,无需改 config 文件:CONTAINER_MODE、VOLUME_HOST_PATH、JOB_DOCKER_BACKEND、CONTAINER_NETWORK、RUNNER_IMAGE、RUNNERS_BASE_PATH 等
    environment:
      DOCKER_HOST: ${DOCKER_HOST:-unix:///var/run/docker.sock}
      BASIC_AUTH_USER: ${BASIC_AUTH_USER:-}
      BASIC_AUTH_PASSWORD: ${BASIC_AUTH_PASSWORD:-}
      # 以下可选:覆盖 config/config.yaml,全容器时无需改 config 文件(见 .env.example)
      CONTAINER_MODE: ${CONTAINER_MODE:-}
      VOLUME_HOST_PATH: ${VOLUME_HOST_PATH:-}
      JOB_DOCKER_BACKEND: ${JOB_DOCKER_BACKEND:-}
      CONTAINER_NETWORK: ${CONTAINER_NETWORK:-}
      RUNNER_IMAGE: ${RUNNER_IMAGE:-}
      RUNNERS_BASE_PATH: ${RUNNERS_BASE_PATH:-}
    ports:
      - "${MANAGER_PORT:-8080}:8080"
    # 仅挂载 config 目录(勿挂载 config/config.yaml 单文件,否则宿主机无该文件时 Docker 会创建空文件导致启动失败)
    volumes:
      - ./config:/app/config
      - ./runners:/app/runners
      - /var/run/docker.sock:/var/run/docker.sock
    # 容器模式需 Manager 访问宿主机 Docker:加入宿主机 docker 组(GID 用 getent group docker 查看,常见为 999)
    group_add:
      - "${DOCKER_GID:-999}"
    networks:
      - runner-net
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8080/health"]
      interval: 30s
      timeout: 5s
      start_period: 15s
      retries: 3

# runner-net 设为 external,避免 compose down 时删除网络导致已注册的 Runner 容器
#(由 Manager 动态创建、未在 compose 中定义)无法启动。首次使用请执行:docker network create runner-net
networks:
  runner-net:
    external: true

所有配置就绪后,执行 docker compose up -d 即可完成部署。这种将复杂环境封装为可编排配置的方式,正是 云原生/IaaS 理念的典型实践。默认情况下,访问 http://<你的IP或域名>:8080,使用上面 .env 文件中定义的账号密码登录。

Runner Fleet登录验证界面

登录成功后,即可进入 Runner 的管理界面。

Runner Fleet管理主界面

跑通第一个私有化的 GitHub Action

在 GitHub 项目中获取 Runner Token

部署好 Runner Fleet 后,需要为它配置要服务的 GitHub 仓库或组织。访问 GitHub 上任意项目的设置页面,进入 Actions 菜单下的 Runners 配置页面。

GitHub仓库的Runners配置页面

点击 “New self-hosted runner” 按钮,选择操作系统和架构。在官方提供的配置示例中,找到包含 --token 参数的那条 ./config.sh 命令,点击该命令,它会自动复制到剪贴板。

GitHub提供的Runner配置命令,包含Token

在 Fleet 中初始化 Runner

回到 Runner Fleet 的管理界面,将刚才复制的命令粘贴到“快速添加 Runner”区域的解析框中,然后点击“解析并填充”按钮。

将GitHub命令粘贴到Fleet解析器中

解析器会自动提取命令中的 URL 和 Token 等信息,并填充到下方的表单中。你可以在此处调整 Runner 的名称、标签等细节。

解析后自动填充的表单

确认无误后,点击“添加 Runner”按钮,系统将在后台自动创建并注册该 Runner。

添加Runner成功的提示

添加完成后,刷新页面即可在 Runner 列表中看到新添加的 Runner 及其状态。

Fleet中显示已添加的Runner列表

同时,在 GitHub 对应仓库或组织的 Runners 设置页面,也能看到这个自托管的 Runner 已处于可用状态。

GitHub界面显示自托管Runner已在线

修改 GitHub CI 配置,启用私有 Runner

至此,私有化 Runner 的服务端已完全就绪。要真正使用它,只需最后一步:修改项目中的 GitHub Actions Workflow 配置文件,指定其使用自托管的 Runner。

找到项目 .github/workflows/ 目录下的任意 CI 配置文件,修改 jobs 部分。例如,将原先使用 GitHub 托管 Runner 的配置:

jobs:
  # Code formatting check
  fmt:
    name: Code Formatting Check
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v6

runs-on: ubuntu-latest 修改为 runs-on: self-hosted。你还可以通过标签进行更精确的匹配,例如 runs-on: [self-hosted, linux, docker] 来指定使用带有 docker 标签的 Runner。

使用自托管Runner成功运行CI任务的结果

再次触发 CI/CD 流水线时,任务就会被分发到你刚刚部署的私有化 Runner 上执行了。通过这种方式,你可以将 CI/CD 的负载从 GitHub 的共享基础设施转移到可控的私有环境中,这在涉及敏感代码或需要特定依赖的场景下尤为重要。有效的 Runner 管理是保障 运维 & 测试 流程稳定与高效的关键一环。

最后

Runner Fleet 还支持裸金属部署、Docker-in-Docker (DinD) 模式等更多进阶玩法,这些内容我们将在后续分享。

在设计这个工具时,我希望能通过清晰、简单的配置来规避掉大量隐晦的错误,也希望所有 UI 操作都能被记录和追溯,让问题排查有据可依。当然,考虑到环境的复杂性,错误信息也应该被及时、清晰地反馈在界面上。或许,这才是一个工程化工具应有的样子。

云栈社区 ,我们持续关注和分享这类能提升开发与运维效率的工具与实践。




上一篇:段永平Q4调仓复盘:减仓苹果,重仓算力与连接技术,新入三家AI公司
下一篇:Python函数入门指南:从零开始掌握定义与调用
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 11:43 , Processed in 0.800948 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表