【入門編】GitHub Actionsの「Ephemeral runner」をクラウドで自動拡張!KEDAでスケールするAuto-scaling戦略 – バージョン管理・CI/CD活用バイブル

こんにちは!日々のCI/CDパイプラインの遅延や、使っていない時も立ち上がったままの「お留守番ランナー」の維持費に頭を悩ませていませんか?

「ジョブが走っていない時はリソースを完全にゼロにし、巨大なPRが同時に100個飛んできたら一瞬でスケールアウトし、仕事が終わったら跡形もなく消え去る」

そんな、DevOpsエンジニアなら誰もが夢見る究極のサーバーレスCIインフラを、KubernetesとKEDA(Kubernetes Event-driven Autoscaling)、そしてGitHub Actionsの「Ephemeral runner(使い捨てランナー)」を組み合わせて構築する方法を解説します。

これをマスターすれば、インフラ管理のストレスから解放され、チームの開発スピードが劇的に加速しますよ。さあ、一緒に次世代のCI基盤へ足を踏み入れましょう!

—

1. なぜ「Ephemeral runner × KEDA」なのか?

従来のGitHub Actionsの運用では、常時起動の仮想マシンをランナーとして登録しておくのが一般的でした。しかし、これには大きな課題があります。

  • セキュリティリスク: 同じランナーで複数のジョブを連続して実行すると、前のジョブが残したファイルやキャッシュが次のジョブに悪影響を与える(あるいはセキュリティ上の脆弱性になる)可能性があります。
  • コストの無駄: 夜間や週末など、誰もコード書いていない時間でもサーバー代が発生します。

そこで登場するのが、Ephemeral(エフェメラル=はかない、使い捨ての)ランナーです。これは「1つのジョブを実行したら、使い捨てる」という思想のランナーです。

さらに、これをKubernetes上で動かし、KEDAを使ってGitHubのAPI(ジョブのキュー数)を監視させます。「お、今キューにジョブが3つ溜まったな?」と検知したらKEDAがKubernetesのPodを自動で3つ生やし、ジョブが終わってPodが消滅すればコストもゼロになる。この美しく高効率な仕組みをこれから作っていきます。

—

2. 全体像と必要な前提

今回構築するアーキテクチャの流れはこうです。

1. 開発者がGitHubにコードをプッシュし、ワークフローがトリガーされる。
2. GitHubのAPI上に「実行待ちのジョブ(Queue)」が発生する。
3. Kubernetes上の KEDA (GitHub Trigger) がそのキューを検知する。
4. KEDAが Kubernetes Deployment (ランナーのPod) をスケールアップする。
5. 立ち上がったEphemeral runnerがGitHubからジョブを取得して実行する。
6. ジョブが完了すると、ランナー自体が消滅し、KubernetesのPodもスケールダウンする。

前提条件

  • Kubernetesクラスタ(EKS, GKE, AKS, あるいはMinikubeなど)が手元にあること
  • `kubectl` がクラスタに接続されていること
  • Helm (v3以降) がインストールされていること
  • 対象のGitHubリポジトリ(または組織)の管理者権限があること

—

3. ステップ1:KEDAのインストール

まずは、イベント駆動オートスケーリングの頭脳である「KEDA」をKubernetesクラスタにインストールします。一瞬で終わりますよ。

Helmリポジトリを追加
helm repo add kedacore https://kedacore.github.io/charts
helm repo update

KEDAをデプロイする名前空間を作成してインストール
kubectl create namespace keda
helm install keda kedacore/keda –namespace keda

正しくPodが起動しているか確認しておきましょう。

kubectl get pods -n keda

`keda-operator` や `keda-metrics-apiserver` が `Running` になっていればOKです!

—

4. ステップ2:GitHub認証情報の準備(Personal Access Token)

KEDAがGitHubのAPIを叩いて「今、何個ジョブが溜まってる?」と確認するために、GitHubの権限(PAT: Personal Access Token)が必要です。

1. GitHubの Settings > Developer settings > Personal access tokens (Classic) に移動します。
2. 以下のスコープにチェックを入れてトークンを発行します。

  • `repo` (プライベートリポジトリの場合) または `public_repo`
  • `admin:org` (組織レベルでランナーを管理する場合。リポジトリ単体なら不要)

3. 発行されたトークンを、KubernetesのSecretとしてクラスタに登録します。

kubectl create secret generic github-runner-secret \
–namespace=default \
–from-literal=github-token=”YOUR_GITHUB_PERSONAL_ACCESS_TOKEN”

—

5. ステップ3:KEDAの `ScaledObject` とランナーマニフェストの作成

ここが一番ワクワクする核心部分です。
GitHub Actionsのランナーを動かすKubernetesのDeploymentと、それをKEDAで自動拡張するための `ScaledObject` を定義します。

以下のファイルを作成してください(例: `runner-deployment.yaml`)。

apiVersion: apps/v1
kind: Deployment
metadata:
name: github-runner
namespace: default
spec:
replicas: 0 #普段は0!リクエストが来た時だけ立ち上がります
selector:
matchLabels:
app: github-runner
template:
metadata:
labels:
app: github-runner
spec:
containers:

  • name: runner

# 公式またはコミュニティの軽量なランナーイメージを使用
image: ghcr.io/actions/actions-runner:latest
command: [“/bin/bash”, “-c”]
# –ephemeral フラグが肝心!1回ジョブをこなしたら自動消滅します
args:

  • |

./config.url –url https://github.com/YOUR_ORG/YOUR_REPO \
–token $(cat /secrets/github-token) \
–ephemeral \
–unattended
./run.sh
env:

  • name: GITHUB_TOKEN

valueFrom:
secretKeyRef:
name: github-runner-secret
key: github-token
volumeMounts:

  • name: secret-volume

mountPath: /secrets
readOnly: true
volumes:

  • name: secret-volume

secret:
secretName: github-runner-secret
—
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: github-runner-scaler
namespace: default
spec:
scaleTargetRef:
name: github-runner
minReplicaCount: 0 # ジョブがない時はPod数 0
maxReplicaCount: 10 # 同時実行数の上限(お財布とインフラの相談で変更してください)
cooldownPeriod: 300 # ジョブ終了後、スケールダウンするまでの猶予時間(秒)
triggers:

  • type: github

metadata:
# 対象のリポジトリを指定
owner: “YOUR_ORG”
repository: “YOUR_REPO”
# ジョブのキュー数が「1」を超えたらスケール開始
runnerStatus: “queued”
targetWorkflowQueueLength: “1”
authenticationRef:
name: github-trigger-auth
—
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: github-trigger-auth
namespace: default
spec:
secretTargetRef:

  • parameter: personalAccessToken

name: github-runner-secret
key: github-token

> 先輩からのワンポイントアドバイス:
> `args` 内にある `./config.url` の部分は、組織全体(Organization)にランナーを登録する場合は `–url https://github.com/YOUR_ORG` に変更し、必要な権限を持つ登録用トークン(Registration Token)を動的に取得する仕組みにするとさらにスケーラブルになります。今回はシンプルにリポジトリ単位で解説しています。

このマニフェストをKubernetesに適用します。

kubectl apply -f runner-deployment.yaml

—

6. ステップ4:動作確認(Hello World)

さあ、魔法が動くところを確認しましょう!
手元のリポジトリに、以下のようなテスト用のGitHub Actionsワークフロー(`.github/workflows/test.yml`)を作成してプッシュします。

name: KEDA Runner Test

on:
push:
branches: [ “main” ]

jobs:
build:
# Kubernetes側で設定したラベルを指定
runs-on: self-hosted

steps:

  • name: Checkout code

uses: actions/checkout@v3

  • name: Run a hello command

run: |
echo “こんにちは!KEDAとEphemeral runnerの世界へようこそ!”
hostname
uptime

コードをプッシュしたら、直ちに以下のコマンドを別ターミナルで実行してみてください。

kubectl get pods -w

【見どころ】
GitHubのActionsタブを見るとジョブが「Queued(キューイング中)」になります。その瞬間、KEDAがそれを察知し、Kubernetes上で `github-runner-xxxxx` というPodが `Pending` から `Running` へ一瞬で立ち上がります!

ワークフローが無事に完了すると、Ephemeral runnerは自身を破棄するため、Podもスルスルと消滅して再び `0` の状態に戻ります。
……どうですか?この無駄のない美しさ、鳥肌が立ちませんか?

—

7. トラブルシューティングと現場の知見

この構成を本番運用するにあたり、知っておくべき「現場の知見」をいくつか授けます。

  • Podが立ち上がらないときは?

`kubectl describe pod github-runner-xxxxx` や `kubectl logs deployment/github-runner` を確認してください。大抵はGitHubのPATの権限不足か、URLのタイポです。

  • Docker in Docker (DinD) を使いたい場合

コンテナ内でさらにDockerビルドをしたい場合は、Kubernetes側の権限(SecurityContextやボリュームマウント)の調整が必要になります。セキュアな環境を目指すなら、ビルドにはKanikoなどのコンテナレスビルドツールを組み合わせるのがモダンです。

  • スケールダウンの罠

KEDAの `cooldownPeriod` は短すぎると、ジョブとジョブのわずかな隙間にPodが消えてしまい、長すぎるとコストが無駄になります。ワークフローの平均実行時間に合わせてチューニングしましょう。

—

まとめ

今回は、GitHub Actionsの「Ephemeral runner」と「KEDA」を組み合わせて、Kubernetes上で完全に自動拡張するサーバーレスCIインフラの構築方法を解説しました。

これをマスターすれば、もう重いCIサーバーのサイジングや、夜間のリソース課金に怯える必要はなくなります。必要なときに、必要な分だけリソースを爆発させ、用が済んだら綺麗に消える――そんなスマートなDevOpsライフを今日から始めてみませんか?

あなたの毎日の開発体験が、劇的に快適になることを心から応援しています!

タイトルとURLをコピーしました