GitHub Actions Runner Controller (ARC) 部署与问题排查指南

GitHub Actions Runner Controller (ARC) 部署与问题排查指南

记录从零开始在 Kubernetes 上部署 ARC 0.12.1 遇到的问题及解决方案

背景

在 Kubernetes 集群上部署 GitHub Actions Runner Controller (ARC),用于在自托管的 Kubernetes 环境中运行 GitHub Actions workflows,支持 Docker 镜像构建。

环境信息:

  • Kubernetes 版本:v1.32.5
  • ARC 版本:0.12.1
  • GitHub 组织:免费计划
  • 认证方式:Personal Access Token (PAT)

部署过程

1. 安装 ARC Controller

1
2
3
4
helm install arc \\
  --namespace action \\
  --create-namespace \\
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

2. 创建 GitHub PAT Secret

1
2
3
kubectl create secret generic github-pat-secret \\
  --namespace=action \\
  --from-literal=github_token='your-pat-token'

PAT 所需权限:

  • Actions: Read and write
  • Administration: Read and write

核心问题:minRunners 配置导致 Ephemeral Runners 无法工作

问题现象

使用以下配置时,workflows 一直卡在 “Waiting for a runner to pick up this job…” 状态:

1
2
3
4
5
6
# ❌ 错误配置
githubConfigUrl: "<https://github.com/your-org>"
githubConfigSecret: github-pat-secret
runnerScaleSetName: "your-runners"
minRunners: 1  # 这是问题所在!
maxRunners: 5

观察到的症状:

  1. Runner pods 快速启动后在 1-3 秒内完成并删除

  2. Pod 状态:Pending → ContainerCreating → Running → Completed → Terminating

  3. Listener 日志显示:

    1
    2
    3
    4
    5
    
    {
      "totalAssignedJobs": 2,
      "totalRegisteredRunners": 2,
      "totalIdleRunners": 0
    }
    
  4. GitHub API 显示 runners 已注册但状态为 “offline”,且 labels 为空 []

  5. Workflows 永远停留在队列中

问题根因分析

ARC 0.12.x 使用的是 Ephemeral Runners(临时运行器)架构,基于 JIT (Just-In-Time) 配置:

  1. 每个 ephemeral runner 在创建时必须绑定到特定的 job
  2. Runner 通过 JIT configuration token 获取其应该执行的 job 信息
  3. Runner 生命周期:启动 → 注册 → 执行 job → 上传结果 → 注销 → 退出

当设置 minRunners: 1 时:

  1. ARC 为了满足最小数量要求,创建 1 个"待命" runner
  2. 这个 runner 没有绑定任何 job(因为它不是为特定 job 创建的)
  3. Runner 启动后发现 JIT token 中没有 job 信息,认为任务已完成
  4. Runner 立即标记为 “Succeeded” 并退出
  5. 当真正的 workflow job 到来时:
    • GitHub 尝试将 job 分配给这个 runner scale set
    • 但所有 runners 都已经 offline(已完成并删除)
    • Job 无法被分配,永远停留在队列中

解决方案

minRunners 设置为 0****:

1
2
3
4
5
6
# ✅ 正确配置
githubConfigUrl: "<https://github.com/your-org>"
githubConfigSecret: github-pat-secret
runnerScaleSetName: "your-runners"
minRunners: 0  # 关键配置!
maxRunners: 5

正确的工作流程:

  1. Workflow job 进入队列
  2. GitHub 通知 ARC listener:“有 job 分配给这个 runner scale set”
  3. Listener 触发 ARC controller 创建新的 ephemeral runner
  4. 新创建的 runner 的 JIT token 已经绑定了这个 job
  5. Runner 启动 → 执行 job → 完成 → 退出
  6. 整个过程流畅,job 通常在 15-20 秒内完成

验证解决方案

应用正确配置:

1
2
3
4
helm upgrade shiliuzi-runners \\
  --namespace action \\
  -f arc-values-minimal.yaml \\
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

触发测试 workflow:

1
2
3
4
5
6
7
8
9
name: Test ARC Runner
on: workflow_dispatch

jobs:
  test:
    runs-on: your-runners  # 使用 runner scale set 名称
    steps:
      - name: Test
        run: echo "SUCCESS!"

结果:✅ 成功执行,15 秒完成

问题 2:启用 Docker-in-Docker (DinD) 支持

需求

需要在 runners 中构建 Docker 镜像,这需要 Docker daemon。

配置 DinD

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
githubConfigUrl: "<https://github.com/your-org>"
githubConfigSecret: github-pat-secret
runnerScaleSetName: "your-runners"
minRunners: 0
maxRunners: 5

containerMode:
  type: "dind"  # 启用 Docker-in-Docker

template:
  spec:
    containers:
    - name: runner
      image: ghcr.io/actions/actions-runner:latest
      command: ["/home/runner/run.sh"]
      env:
      - name: DOCKER_HOST
        value: unix:///var/run/docker.sock
      resources:
        requests:
          cpu: "250m"
          memory: "512Mi"
        limits:
          cpu: "2"
          memory: "4Gi"
      volumeMounts:
      - name: work
        mountPath: /home/runner/_work

    - name: dind
      image: docker:dind
      securityContext:
        privileged: true
      volumeMounts:
      - name: work
        mountPath: /home/runner/_work
      - name: dind-storage
        mountPath: /var/lib/docker

    volumes:
    - name: work
      emptyDir: {}
    - name: dind-storage
      emptyDir: {}

DinD 架构说明

每个 runner pod 包含 2 个容器:

  1. runner 容器:执行 GitHub Actions workflows
  2. dind 容器:运行 Docker daemon(privileged 模式)

两个容器通过共享的 Unix socket (/var/run/docker.sock) 通信。

验证 Docker 功能

测试 workflow:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
name: Test Docker Build
on: workflow_dispatch

jobs:
  test-docker:
    runs-on: your-runners
    steps:
      - name: Check Docker
        run: docker --version

      - name: Build Image
        run: |
          cat > Dockerfile <<EOF
          FROM alpine:latest
          RUN echo "Hello from Docker!"
          EOF
          docker build -t test:latest .

      - name: Run Container
        run: docker run --rm test:latest echo "Success!"

结果:✅ 成功构建和运行容器

输出示例:

1
2
3
Docker version 28.5.1, build e180ab8
Successfully built image
Success!

其他发现与注意事项

1. Runner Labels 为空

通过 GitHub API 查询时,ephemeral runners 的 labels 字段为空 []

1
2
gh api /orgs/your-org/actions/runners
# 输出:"labels": []

这是正常的! 这是 ARC 0.12.x 的设计行为(参见 GitHub Issue #3330)。

Workflows 应该使用 runner scale set 名称来匹配:

1
2
3
4
5
# ✅ 正确
runs-on: your-runners

# ❌ 错误(在 ARC 0.12.x 中不工作)
runs-on: self-hosted

2. Runner Group 配置

确保 runner group 允许你的仓库访问:

1
2
3
4
5
6
7
8
9
# 检查 runner group
gh api /orgs/your-org/actions/runner-groups/1

# 输出应包含:
{
  "name": "Default",
  "visibility": "all",
  "allows_public_repositories": false  # private 仓库可以访问
}

3. 版本兼容性

  • ARC 0.9.3 不兼容 Kubernetes 1.32.5(API validation errors)
  • ARC 0.12.1 与 Kubernetes 1.32.5 完全兼容
  • 建议使用最新的 ARC 版本

最终配置文件

基础配置(无 Docker)

1
2
3
4
5
6
# arc-values-minimal.yaml
githubConfigUrl: "<https://github.com/your-org>"
githubConfigSecret: github-pat-secret
runnerScaleSetName: "your-runners"
minRunners: 0
maxRunners: 5

完整配置(带 DinD)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# arc-values-with-dind.yaml
githubConfigUrl: "<https://github.com/your-org>"
githubConfigSecret: github-pat-secret
runnerScaleSetName: "your-runners"
minRunners: 0
maxRunners: 5

containerMode:
  type: "dind"

template:
  spec:
    containers:
    - name: runner
      image: ghcr.io/actions/actions-runner:latest
      command: ["/home/runner/run.sh"]
      env:
      - name: DOCKER_HOST
        value: unix:///var/run/docker.sock
      resources:
        requests:
          cpu: "250m"
          memory: "512Mi"
        limits:
          cpu: "2"
          memory: "4Gi"
      volumeMounts:
      - name: work
        mountPath: /home/runner/_work
    - name: dind
      image: docker:dind
      securityContext:
        privileged: true
      volumeMounts:
      - name: work
        mountPath: /home/runner/_work
      - name: dind-storage
        mountPath: /var/lib/docker
    volumes:
    - name: work
      emptyDir: {}
    - name: dind-storage
      emptyDir: {}

部署命令总结

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 1. 安装 ARC Controller
helm install arc \\
  --namespace action \\
  --create-namespace \\
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

# 2. 创建 GitHub PAT Secret
kubectl create secret generic github-pat-secret \\
  --namespace=action \\
  --from-literal=github_token='your-pat-token'

# 3. 安装 Runner Scale Set(带 DinD)
helm install your-runners \\
  --namespace action \\
  -f arc-values-with-dind.yaml \\
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

# 4. 验证部署
kubectl get pods -n action
kubectl get ephemeralrunners -n action

问题排查清单

如果 workflows 卡在队列中,按以下步骤排查:

1. 检查 minRunners 配置

1
helm get values your-runners -n action

确保 minRunners: 0

2. 检查 listener 状态

1
kubectl logs -n action -l app.kubernetes.io/component=runner-set-listener --tail=50

查找 "totalAssignedJobs""totalIdleRunners"

3. 检查 ephemeral runners

1
kubectl get ephemeralrunners -n action

应该看到 status 为 “Running” 的 runners

4. 检查 pod 状态

1
kubectl get pods -n action -w

Pods 应该保持 Running 状态直到 job 完成

5. 验证 GitHub API

1
gh api /orgs/your-org/actions/runners

Runners 应该显示在列表中(即使 labels 为空)

6. 检查 workflow 配置

确保使用正确的 runs-on 值:

1
runs-on: your-runners  # 使用 runner scale set 名称

监控和日志

查看 listener 日志

1
kubectl logs -n action deployment/your-runners-listener -f

查看 controller 日志

1
kubectl logs -n action deployment/arc-gha-rs-controller -f

查看 runner pod 日志(运行时)

1
kubectl logs -n action <runner-pod-name> -c runner -f

查看 DinD 容器日志

1
kubectl logs -n action <runner-pod-name> -c dind -f

性能调优建议

1. 资源配置

根据实际工作负载调整资源限制:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 轻量级任务
resources:
  requests:
    cpu: "250m"
    memory: "512Mi"
  limits:
    cpu: "1"
    memory: "2Gi"

# Docker 构建任务
resources:
  requests:
    cpu: "500m"
    memory: "1Gi"
  limits:
    cpu: "4"
    memory: "8Gi"

2. 并发控制

1
2
minRunners: 0  # 按需启动,节省资源
maxRunners: 10  # 根据集群容量和并发需求调整

3. DinD 存储优化

对于频繁的 Docker 构建,考虑使用持久化存储:

1
2
3
4
volumes:
- name: dind-storage
  persistentVolumeClaim:
    claimName: dind-storage-pvc

总结

关键要点

  1. ARC 0.12.x 必须使用 minRunners: 0 - Ephemeral runners 需要在创建时就绑定到特定的 job
  2. Workflows 使用 runner scale set 名称 - 不要使用 self-hosted label
  3. DinD 需要 privileged 模式 - 确保 Kubernetes 集群允许 privileged containers
  4. Runner labels 为空是正常的 - 这是 ARC 0.12.x 的设计行为

成功指标

  • ✅ Workflows 在 15-30 秒内开始执行
  • ✅ Runner pods 保持 Running 状态直到 job 完成
  • ✅ Docker 命令在 workflows 中正常工作
  • ✅ Listener 日志显示 job 被正确分配和执行

参考资源


文档日期: 2025-10-15 ARC 版本: 0.12.1 Kubernetes 版本: v1.32.5 状态: ✅ 生产就绪

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计