- Authors
- Name
- なぜSelf-Hosted Runnerなのか
- GitHub-Hosted vs Self-Hosted vs ARC 比較
- ARC(Actions Runner Controller)アーキテクチャ
- ARCのインストールと構成
- カスタムRunnerイメージのビルド
- セキュリティハードニング
- キャッシュ戦略
- モニタリングと可観測性
- 障害ケースとリカバリ手順
- 大規模運用の最適化
- 運用チェックリスト
- まとめ
- References

なぜSelf-Hosted Runnerなのか
GitHub-hostedランナーは素早く始められるが、組織の規模が大きくなると限界にぶつかる。ビルド時間が30分を超え、GPUが必要になり、内部ネットワークリソースにアクセスする必要があり、コストが月数十万円を超え始めたら、self-hostedランナーの導入を検討すべきだ。
2026年3月からGitHubはself-hostedランナーに対しても分あたり$0.002のコントロールプレーン費用を課金し始めた(パブリックリポジトリとGitHub Enterprise Serverの顧客は除外)。しかし大規模組織では依然としてGitHub-hostedランナーと比較して60〜80%のコスト削減が可能であり、何よりインフラカスタマイズの自由度が圧倒的だ。
Self-hostedランナーを導入すると以下が可能になる:
- 内部ネットワークのプライベートレジストリ、データベース、シークレットマネージャーへの直接アクセス
- GPU、ARM、Apple Siliconなどの特殊ハードウェアでのビルドとテスト
- ビルドキャッシュをローカルストレージに維持し、依存関係のインストール時間を90%以上短縮
- 組織のセキュリティポリシーに準拠したネットワーク分離と監査ログの実装
GitHub-Hosted vs Self-Hosted vs ARC 比較
ランナーの選択はチーム規模、セキュリティ要件、運用能力によって異なる。以下の比較表を判断基準として活用する。
| 項目 | GitHub-Hosted | Self-Hosted(VM) | ARC(Kubernetes) |
|---|---|---|---|
| 初期設定の難易度 | なし | 中程度 | 高い |
| オートスケーリング | 自動 | 自前で実装が必要 | ネイティブ対応 |
| コスト(月1000時間基準) | 約$480(Linux 2コア) | EC2費用+運用人件費 | K8sクラスター費用+運用 |
| ビルドキャッシュ | 10GB制限、Azure Blob | ローカルディスク無制限 | PVCまたはS3 |
| 内部ネットワークアクセス | 不可 | 可能 | 可能 |
| セキュリティ分離 | GitHub管理 | 手動ハードニング | Pod単位の分離 |
| GPUサポート | 限定的(ラージランナー) | 完全対応 | NVIDIA Device Plugin |
| 最大同時ランナー数 | プランにより制限 | インフラの限界 | クラスターノードの限界 |
| メンテナンス負荷 | なし | 高い(OSパッチ、バージョン管理) | 中程度(Helmアップグレード) |
| Ephemeralサポート | デフォルト | --ephemeralフラグ | デフォルト |
判断基準:月間CI/CD時間が500時間以下で内部ネットワークアクセスが不要ならGitHub-hostedが合理的。500〜2000時間でK8s運用能力がなければVMベースのself-hostedを推奨。2000時間以上またはK8sクラスターがすでにあればARCが最善だ。
ARC(Actions Runner Controller)アーキテクチャ
Actions Runner ControllerはGitHubが公式に管理するKubernetesオペレーターだ。コミュニティプロジェクトとして始まったが、2023年からGitHubが直接開発し、Runner Scale Setsという新しいアーキテクチャに進化した。レガシーモードのwebhookベースのオートスケーリングとは異なり、Runner Scale SetsはGitHub APIと直接通信し、ジョブキューをリアルタイムで検知する。
ARCの動作原理
┌─────────────────┐ ┌──────────────────────┐
│ GitHub.com │ │ Kubernetes Cluster │
│ │ │ │
│ Job Queue │◄───►│ ARC Controller │
│ (workflow_job) │ │ │ │
│ │ │ ▼ │
│ Scale Set API │◄───►│ ScaleSet Listener │
│ │ │ │ │
└─────────────────┘ │ ▼ │
│ EphemeralRunnerSet │
│ │ │
│ ├─► Runner Pod 1 │
│ ├─► Runner Pod 2 │
│ └─► Runner Pod N │
└──────────────────────┘
- ScaleSet ListenerがGitHubのジョブキューをLong Polling方式で監視する
Job Availableメッセージを受信すると、現在のランナー数とmaxRunners設定を比較する- スケールアップが可能であればメッセージをACKし、Kubernetes APIを通じてEphemeralRunnerSetのreplica数をパッチする
- 新しいRunner Podが作成され、JIT(Just-In-Time)トークンでGitHubに登録される
- ジョブ実行が完了するとPodは即座に削除される(ephemeralのデフォルト動作)
ARCのインストールと構成
前提条件
- Kubernetes 1.27以上
- Helm 3.x
- GitHub AppまたはPersonal Access Token(orgレベル
admin:org、repoレベルrepoスコープ) - cert-manager(TLS証明書の自動管理用、オプション)
Step 1:Controllerのインストール
# ARCコントローラー用の名前空間を作成
kubectl create namespace arc-systems
# Helmリポジトリの追加
helm install arc \
--namespace arc-systems \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller \
--version 0.10.1
Step 2:GitHub App認証の設定
Personal Access TokenよりもGitHub App認証を強く推奨する。PATはユーザーに紐づくため退社時に問題が発生し、権限の範囲が広い。GitHub Appは組織レベルで管理され、必要最小限の権限のみ付与できる。
# GitHub Appシークレットの作成
kubectl create secret generic github-app-secret \
--namespace arc-runners \
--from-literal=github_app_id=12345 \
--from-literal=github_app_installation_id=67890 \
--from-file=github_app_private_key=./private-key.pem
Step 3:Runner Scale Setのデプロイ
# values.yaml - Runner Scale Set設定
githubConfigUrl: 'https://github.com/my-org'
githubConfigSecret: github-app-secret
# オートスケーリング設定
minRunners: 2 # 最小待機ランナー(コールドスタート防止)
maxRunners: 30 # 最大ランナー数(クラスターリソースを考慮)
# ランナーグループの指定(Enterprise/Orgレベル)
runnerGroup: 'production-runners'
# コンテナモード設定
containerMode:
type: 'kubernetes'
kubernetesModeWorkVolumeClaim:
accessModes: ['ReadWriteOnce']
storageClassName: 'gp3'
resources:
requests:
storage: 50Gi
# Podテンプレートのカスタマイズ
template:
spec:
containers:
- name: runner
image: ghcr.io/actions/actions-runner:latest
resources:
requests:
cpu: '2'
memory: '4Gi'
limits:
cpu: '4'
memory: '8Gi'
env:
- name: RUNNER_GRACEFUL_STOP_TIMEOUT
value: '60'
# ノード選択(ビルド専用ノードプール)
nodeSelector:
workload-type: ci-runner
tolerations:
- key: 'ci-runner'
operator: 'Equal'
value: 'true'
effect: 'NoSchedule'
# Runner Scale Setのデプロイ
helm install arc-runner-set \
--namespace arc-runners \
--create-namespace \
-f values.yaml \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set \
--version 0.10.1
カスタムRunnerイメージのビルド
デフォルトのRunnerイメージにはビルドツールが含まれていない。組織で使用するツールを事前にインストールしたカスタムイメージをビルドすることで、ワークフロー実行時間を大幅に短縮できる。
Dockerfile作成の原則
- ベースイメージはできるだけslimバリアントを使用する
- Runnerバイナリバージョンはv2.329.0以上を使用する(2026年3月16日から以前のバージョンは登録がブロックされる)
- 不要なパッケージのインストールを避け、マルチステージビルドでイメージサイズを最小化する
- rootではなく専用ユーザーでRunnerプロセスを実行する
# Dockerfile.runner - カスタムGitHub Actions Runnerイメージ
FROM ubuntu:22.04 AS base
# システムパッケージのインストール(最小限に維持)
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
git \
jq \
unzip \
zip \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Runner専用ユーザーの作成
RUN useradd -m -d /home/runner -s /bin/bash runner
# Runnerバイナリのインストール
ARG RUNNER_VERSION=2.321.0
RUN curl -fsSL -o runner.tar.gz \
"https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz" \
&& mkdir -p /home/runner/actions-runner \
&& tar xzf runner.tar.gz -C /home/runner/actions-runner \
&& rm runner.tar.gz \
&& /home/runner/actions-runner/bin/installdependencies.sh
# Node.js 22 LTSのインストール
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*
# Docker CLIのインストール(DinDではなくCLIのみ)
RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker.gpg \
&& echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable" \
> /etc/apt/sources.list.d/docker.list \
&& apt-get update && apt-get install -y docker-ce-cli \
&& rm -rf /var/lib/apt/lists/*
# 自動更新の無効化(イメージビルドでバージョン管理)
ENV RUNNER_MANUALLY_TRAP_SIG=1
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
# 権限設定とユーザー切替
RUN chown -R runner:runner /home/runner
USER runner
WORKDIR /home/runner/actions-runner
ENTRYPOINT ["./run.sh"]
# イメージのビルドとプッシュ
docker build -t ghcr.io/my-org/actions-runner:v2.321.0-custom -f Dockerfile.runner .
docker push ghcr.io/my-org/actions-runner:v2.321.0-custom
注意:Runnerの自動更新を無効化しているため、新しいRunnerバージョンがリリースされたらイメージを再ビルドし、ARCのRunner Scale Setイメージタグを更新する必要がある。GitHubは最小バージョン要件を定期的に引き上げるため、リリースノートを監視すること。
セキュリティハードニング
Self-hostedランナーは組織のインフラ上で外部コードを実行する。ハードニングなしで運用すると、サプライチェーン攻撃、シークレット漏洩、ネットワーク侵入の経路となる。
Ephemeral Runnerの必須化
Persistentランナーでは、以前のジョブのファイル、環境変数、プロセスが次のジョブに影響を与える可能性がある。攻撃者が悪意あるワークフローを通じてランナーにバックドアをインストールすると、以降のすべてのジョブが汚染される。Ephemeralランナーはジョブ完了後に即座に破棄されるため、このリスクを根本的に遮断する。
# ワークフローでephemeralランナーの使用を確認
runs-on: arc-runner-set # ARCはデフォルトでephemeral
# VMベースのself-hostedランナーの場合
# ./config.sh --ephemeralフラグで登録
ネットワーク分離
Runner Podはインターネットと内部ネットワークの両方にアクセスできるため、NetworkPolicyで必要なトラフィックのみを許可する必要がある。
# network-policy.yaml - Runner Podネットワーク制限
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: runner-network-policy
namespace: arc-runners
spec:
podSelector:
matchLabels:
app.kubernetes.io/component: runner
policyTypes:
- Egress
- Ingress
ingress: [] # 外部からRunnerへのインバウンドを遮断
egress:
# GitHub APIとActionsサービス
- to:
- ipBlock:
cidr: 140.82.112.0/20 # github.com
- ipBlock:
cidr: 185.199.108.0/22 # GitHub Pages/CDN
ports:
- protocol: TCP
port: 443
# 内部コンテナレジストリ
- to:
- namespaceSelector:
matchLabels:
name: registry
ports:
- protocol: TCP
port: 5000
# DNS
- to: []
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
RBAC最小権限の原則
Runner PodのServiceAccountには最小限の権限のみを付与する。特にKubernetes APIへのアクセスを制限する必要がある。
# rbac.yaml - Runner ServiceAccount最小権限
apiVersion: v1
kind: ServiceAccount
metadata:
name: runner-sa
namespace: arc-runners
automountServiceAccountToken: false # K8s APIトークンの自動マウントを無効化
---
# 必要な場合にのみ最小権限のRoleをバインド
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: runner-minimal
namespace: arc-runners
rules: [] # デフォルトで権限なし
ワークフローレベルのセキュリティ
# 安全なワークフロー作成パターン
name: Secure CI Pipeline
on:
pull_request:
branches: [main]
# 最小権限トークン
permissions:
contents: read
packages: read
jobs:
build:
runs-on: arc-runner-set
steps:
# SHA固定でActionを使用(タグではない)
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# 環境変数にシークレットを直接公開しない
- name: Build
run: |
echo "Building..."
env:
# シークレットは必要なステップでのみ注入
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
Harden-Runnerを活用したランタイムセキュリティ
StepSecurityのHarden-RunnerはGitHub Actions専用のEDR(Endpoint Detection and Response)で、ネットワークエグレスモニタリング、ファイル整合性チェック、プロセスアクティビティ追跡を提供する。
jobs:
build:
runs-on: arc-runner-set
steps:
- uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
with:
egress-policy: audit # まずauditでトラフィックパターンを把握
# パターン把握後にblockに切替
# egress-policy: block
# allowed-endpoints: >
# github.com:443
# registry.npmjs.org:443
# ghcr.io:443
パブリックリポジトリでの注意事項
パブリックリポジトリでは絶対にself-hostedランナーを使用してはならない。外部の攻撃者がFork PRを通じて任意のコードをランナー上で実行できる。pull_request_targetイベントとself-hostedランナーの組み合わせは特に危険だ。必ずプライベートリポジトリまたは組織内部のリポジトリでのみ使用すること。
キャッシュ戦略
Ephemeralランナーの最大の欠点は、ジョブごとにキャッシュが消えることだ。効率的なキャッシュ戦略がなければ、毎回のビルドで依存関係のダウンロードから始めなければならない。
戦略1:PersistentVolumeClaim(PVC)ベースのキャッシュ
# ARC Runner Scale Set values.yamlにPVCを追加
template:
spec:
containers:
- name: runner
image: ghcr.io/my-org/actions-runner:latest
volumeMounts:
- name: cache-volume
mountPath: /opt/cache
env:
- name: RUNNER_TOOL_CACHE
value: /opt/cache/tool-cache
- name: npm_config_cache
value: /opt/cache/npm
- name: GOPATH
value: /opt/cache/go
volumes:
- name: cache-volume
persistentVolumeClaim:
claimName: runner-cache-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: runner-cache-pvc
namespace: arc-runners
spec:
accessModes:
- ReadWriteMany # 複数のRunner Podが同時アクセス
storageClassName: efs # AWS EFSまたはNFS
resources:
requests:
storage: 100Gi
注意:ReadWriteManyモードはEFS、NFS、GlusterFSなどのネットワークファイルシステムが必要だ。EBSのようなブロックストレージはReadWriteOnceのみサポートし、一度に1つのPodのみアクセス可能だ。
戦略2:S3互換キャッシュサーバー
GitHubのデフォルトキャッシュ(actions/cache)はAzure Blob Storageを使用する。AWS環境ではリージョン間の遅延が発生する。S3互換のキャッシュサーバーをself-hostedで運用すればネットワーク遅延を最小化できる。
# MinIOベースのキャッシュサーバーデプロイ(同じVPC/リージョン内)
apiVersion: apps/v1
kind: Deployment
metadata:
name: actions-cache-server
namespace: arc-systems
spec:
replicas: 1
selector:
matchLabels:
app: actions-cache
template:
spec:
containers:
- name: minio
image: minio/minio:latest
args: ['server', '/data', '--console-address', ':9001']
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: minio-credentials
key: user
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: minio-credentials
key: password
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: minio-data-pvc
戦略3:Dockerレイヤーキャッシュ(BuildKit)
コンテナイメージのビルドがCIの主要ワークロードなら、BuildKitのキャッシュバックエンドをレジストリに設定してレイヤーキャッシュを共有する。
# ワークフローでBuildKitレジストリキャッシュを活用
- name: Build and Push
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6
with:
push: true
tags: ghcr.io/my-org/my-app:${{ github.sha }}
cache-from: type=registry,ref=ghcr.io/my-org/my-app:buildcache
cache-to: type=registry,ref=ghcr.io/my-org/my-app:buildcache,mode=max
モニタリングと可観測性
Self-hostedランナーを運用していて最も多い質問は「ランナーがなぜ起動しないのか」だ。モニタリングなしでは答えを出せない。
Prometheus + Grafanaメトリクス
ARCはデフォルトでPrometheusメトリクスを公開する。重要なメトリクスは以下の通り:
gha_runner_scale_set_desired_replicas:現在要求されているランナー数gha_runner_scale_set_running_replicas:実行中のランナー数gha_runner_scale_set_registered_replicas:GitHubに登録完了したランナー数gha_runner_scale_set_idle_replicas:アイドルランナー数
# Prometheus ServiceMonitor設定
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: arc-controller-monitor
namespace: arc-systems
spec:
selector:
matchLabels:
app.kubernetes.io/name: gha-runner-scale-set-controller
endpoints:
- port: metrics
interval: 30s
path: /metrics
重要なアラートルール
# Alertmanagerルール
groups:
- name: arc-runner-alerts
rules:
# ランナープール枯渇の警告
- alert: RunnerPoolExhausted
expr: |
gha_runner_scale_set_desired_replicas
>= gha_runner_scale_set_max_replicas * 0.9
for: 5m
labels:
severity: warning
annotations:
summary: 'ランナープールが90%以上使用中'
description: 'maxRunnersの増設またはワークフローの最適化が必要'
# ランナー登録失敗の検知
- alert: RunnerRegistrationFailed
expr: |
rate(gha_runner_scale_set_registration_failures_total[5m]) > 0
for: 2m
labels:
severity: critical
annotations:
summary: 'ランナー登録失敗が発生'
description: 'GitHub App認証またはネットワークの確認が必要'
# Pending Pod長時間待機
- alert: RunnerPodPending
expr: |
kube_pod_status_phase{namespace="arc-runners", phase="Pending"} > 0
for: 10m
labels:
severity: warning
annotations:
summary: 'Runner Podが10分以上Pending状態'
description: 'ノードリソース不足またはPVCバインディング失敗の可能性'
障害ケースとリカバリ手順
障害1:ScaleSet Listener CrashLoopBackOff
症状:Listener Podが繰り返し再起動し、ランナーが全くスケールアップしない。
原因分析の順序:
# 1. Listener Podのログ確認
kubectl logs -n arc-systems -l app.kubernetes.io/component=runner-scale-set-listener --tail=100
# 2. よくある原因:GitHub App認証の期限切れ
# - 秘密鍵ファイルの確認
# - Appインストレーションの状態確認(org settings > GitHub Apps)
# 3. ネットワーク問題:GitHub APIにアクセスできない
kubectl exec -n arc-systems deploy/arc-gha-runner-scale-set-controller -- \
curl -s https://api.github.com/meta | jq '.actions[]'
復旧:GitHub Appの秘密鍵を更新し、シークレットを更新する。
kubectl create secret generic github-app-secret \
--namespace arc-runners \
--from-literal=github_app_id=12345 \
--from-literal=github_app_installation_id=67890 \
--from-file=github_app_private_key=./new-private-key.pem \
--dry-run=client -o yaml | kubectl apply -f -
# Controllerの再起動
kubectl rollout restart deployment -n arc-systems arc-gha-runner-scale-set-controller
障害2:Runner PodがPending状態で停止
症状:ジョブはキューに溜まるが、Runner Podが作成されないかPending状態のままになる。
# Podイベントの確認
kubectl describe pod -n arc-runners -l actions.github.com/scale-set-name=arc-runner-set
# よくある原因別の対応
# 1. ノードリソース不足
kubectl top nodes
# -> Cluster Autoscalerが動作しているか確認、またはmaxRunnersを下方修正
# 2. PVCバインディング待ち
kubectl get pvc -n arc-runners
# -> StorageClass設定、アベイラビリティゾーンの不一致を確認
# 3. イメージPull失敗
kubectl get events -n arc-runners --sort-by='.lastTimestamp' | grep -i pull
# -> イメージタグ、レジストリ認証を確認
障害3:ジョブがRunnerに割り当てられない
症状:GitHub UIでジョブが「Queued」状態で無期限に待機する。
# Runner登録状態の確認
kubectl get ephemeralrunner -n arc-runners
# Runnerラベルの確認(runs-onと一致する必要がある)
kubectl get autoscalingrunnersets -n arc-runners -o yaml | grep -A5 labels
# GitHubでRunnerグループ設定の確認
# Settings > Actions > Runner groups > 該当グループにリポジトリが含まれているか確認
復旧:ワークフローのruns-onラベルがARC Runner Scale Setの名前と正確に一致しているか確認する。Runnerグループが設定されている場合、該当リポジトリがグループに含まれているかも検証する。
障害4:Runnerバージョン互換性の問題
2026年3月16日からv2.329.0未満のRunnerの登録がブロックされる。カスタムイメージを使用中なら必ずRunnerバージョンを確認すること。
# 現在のRunnerバージョンの確認
kubectl exec -n arc-runners -it <runner-pod> -- ./config.sh --version
# イメージの更新(values.yaml修正後)
helm upgrade arc-runner-set \
--namespace arc-runners \
-f values.yaml \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set
大規模運用の最適化
ランナーグループ分離戦略
ワークロード特性に応じてRunner Scale Setを分離運用する。単一のScale Setですべてのワークロードを処理すると、リソース競合とノイジーネイバー問題が発生する。
# 用途別Runner Scale Setの分離
# 1. 一般CI(軽量テスト、リント)
# values-ci-light.yaml
minRunners: 2
maxRunners: 20
template:
spec:
containers:
- name: runner
resources:
requests:
cpu: "1"
memory: "2Gi"
# 2. ビルド専用(コンパイル、Dockerビルド)
# values-ci-build.yaml
minRunners: 1
maxRunners: 10
template:
spec:
containers:
- name: runner
resources:
requests:
cpu: "4"
memory: "8Gi"
# 3. GPUワークロード(MLモデルテスト)
# values-gpu.yaml
minRunners: 0
maxRunners: 4
template:
spec:
containers:
- name: runner
resources:
limits:
nvidia.com/gpu: 1
nodeSelector:
accelerator: nvidia-a10g
Graceful Shutdownの処理
ランナーがジョブを実行中にノードドレインやスケールダウンが発生するとジョブが失敗する。RUNNER_GRACEFUL_STOP_TIMEOUTを設定して、進行中のジョブが完了するまで待機するようにする。
template:
spec:
terminationGracePeriodSeconds: 3600 # 最大1時間待機
containers:
- name: runner
env:
- name: RUNNER_GRACEFUL_STOP_TIMEOUT
value: '3500' # terminationGracePeriodSecondsより少し短く
ノードオートスケーラーとの連携
ARCがRunner Podを作成してもノードが不足するとPodはPending状態のままになる。Cluster AutoscalerまたはKarpenterを併せて構成する。
# Karpenter NodePool例(CIランナー専用)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: ci-runners
spec:
template:
metadata:
labels:
workload-type: ci-runner
spec:
taints:
- key: ci-runner
value: 'true'
effect: NoSchedule
requirements:
- key: kubernetes.io/arch
operator: In
values: ['amd64']
- key: karpenter.sh/capacity-type
operator: In
values: ['on-demand', 'spot']
- key: node.kubernetes.io/instance-type
operator: In
values: ['m7i.xlarge', 'm7i.2xlarge', 'm6i.xlarge', 'm6i.2xlarge']
limits:
cpu: 200
memory: 400Gi
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 60s
Spotインスタンスを活用すれば、さらに50〜70%のコスト削減が可能だ。ただし、Spotインタラプション時にジョブが失敗する可能性があるため、重要度の低いCIワークロードにのみ適用すること。本番デプロイパイプラインにはOn-Demandインスタンスを使用する。
運用チェックリスト
Self-hostedランナーの導入前後で以下のチェックリストを点検する。
初期構築チェックリスト
- GitHub App認証の構成完了(PATではなくGitHub Appを使用)
- Runnerイメージに必要なツールの事前インストール完了
- Runnerバージョンv2.329.0以上の確認
- Ephemeralモードの有効化確認
- NetworkPolicyの適用(最小限のエグレスのみ許可)
- ServiceAccountに
automountServiceAccountToken: falseを設定 - Runner Podにリソースrequests/limitsを設定
- ノードセレクター(nodeSelector)またはTaint/Tolerationでビルドノードを分離
- キャッシュ戦略の決定と実装(PVC、S3、レジストリキャッシュ)
セキュリティハードニングチェックリスト
- パブリックリポジトリでのself-hostedランナー使用をブロック
- ランナーグループでリポジトリアクセス範囲を制限
- ワークフローで
permissions最小権限を宣言 - Action参照時にcommit SHAを固定(タグではなく)
- Dockerソケットマウントの禁止(コンテナモードを使用)
- シークレットスキャニングおよび漏洩防止ツールの適用
- RunnerホストOSのハードニング(不要なサービスの削除、ファイアウォール設定)
- OIDCを活用した短期トークンベースのクラウド認証
運用モニタリングチェックリスト
- Prometheusメトリクス収集とGrafanaダッシュボードの構成
- ランナープール枯渇アラートの設定(maxRunnersの90%閾値)
- ランナー登録失敗アラートの設定
- Pod Pending長時間待機アラートの設定
- Runnerバージョン更新アラート(GitHub Changelogを購読)
- 月次セキュリティ監査スケジュールの確立(ネットワークポリシーレビュー、シークレットローテーション)
まとめ
Self-hostedランナーの運用は、単にVMやPodを立ち上げることではない。セキュリティ、スケーリング、キャッシュ、モニタリング、障害対応までを包括するプラットフォームエンジニアリング領域だ。ARCとRunner Scale Setsの登場によりKubernetes上での運用は大幅に安定化したが、結局は組織のワークロードに合わせたチューニングと継続的なモニタリングが必要だ。
ポイントを改めて整理すると:
- Ephemeralは選択ではなく必須だ。 セキュリティと再現性を同時に保証する。
- ARC Runner Scale Setsが現時点で最善のオートスケーリング方式だ。 レガシーのwebhookベースモードは使用しないこと。
- セキュリティハードニングをDay 0で適用せよ。 NetworkPolicy、RBAC、SHA固定、パブリックリポジトリのブロックは基本だ。
- キャッシュ戦略のないephemeralランナーは遅いランナーに過ぎない。 PVC、S3、レジストリキャッシュを必ず構成すること。
- モニタリングとアラートは運用の生命線だ。 ランナープールの枯渇と登録失敗を即座に検知できなければならない。
References
- GitHub Docs - Actions Runner Controller - ARC公式ドキュメントとアーキテクチャ説明
- GitHub Docs - Deploying Runner Scale Sets with ARC - Runner Scale Setデプロイチュートリアル
- GitHub Docs - Self-Hosted Runners - Self-hostedランナー公式ガイド
- GitHub Docs - Secure Use Reference - GitHub Actionsセキュリティ参照ドキュメント
- GitHub Actions Runner Controller Repository - ARCソースコードとHelm chart values参照
- AWS Blog - Best Practices for Self-Hosted Runners at Scale - AWS環境での大規模ランナー運用
- StepSecurity Harden-Runner - GitHub Actionsランタイムセキュリティモニタリングツール
- GitHub Blog - Self-Hosted Runner Minimum Version Enforcement - 2026年ランナー最小バージョン要件の変更