GitHub Actions Runner Controller (ARC) - セルフホストなランナーを Kubernetes でオンデマンド実行する

| 9 min read
Author: masahiro-kondo masahiro-kondoの画像

先日 GitHub Actions の Runner Controller (ARC) のパブリックベータがアナウンスされました。

GitHub Actions - Actions Runner Controller Public Beta | GitHub Changelog

ARC 導入の利点

#

これまで GitHub Actions のセルフホストランナーは、ローカルの PC や VM に構築するものでした。そのため、CI/CD ワークフローの実行時に Runner 用のマシンをプロビジョニングしてワークフロー終了時にマシンを破棄するということが難しく、常に起動状態にしておく必要がありました。

Information

GitHub Actions のセルフホストランナーをローカルマシンで実行する方法については以下の記事を参照してください。

GitHub Actions のセルフホストランナーを M1 Mac で動かす

Kubernetes クラスターに ARC をデプロイすると、セルフホストな GitHub Actions Runner (実体は Pod) がオートスケーリングで起動して実行できます。ワークフロー開始時にランナーの Pod が起動されワークフロー終了後に破棄されるため、専用の VM を用意する必要がなくランニングコストを下げることが可能です。

ARC のドキュメントは以下にあります。

Quickstart for Actions Runner Controller - GitHub Docs

ARC のデプロイ

#

ARC は Azure の マネージド Kubernetes である AKS で使えるとのことです[1]

今回はローカルの Docker Desktop for Mac + kind の構成で試してみました。

Information

ローカル実行では Minikube と kind が利用可能とのことです。筆者は Apple Silicon の MacBook を使っているのですが、Intel Mac のように Hyperkit がドライバーとして使用できません。QEMU ドライバーサポートは未だ安定せず docker ドライバー が必要です。Docker Desktop をドライバーとして利用する場合、Minikube よりも、コンテナをノードとして利用することに特化した kind の方がオーバーヘッドが少ないのではないかと思っています。Minikube の QEMU ドライバーについては以下の記事で紹介しています。

Minikube に QEMU ドライバーサポートが追加 - M1 Mac で利用可能になったけど...

ARC の GitHub リポジトリは以下です。コンテナイメージも Package Registry で公開されています。

GitHub - actions/actions-runner-controller: Kubernetes controller for GitHub Actions self-hosted runners

ARC は、arc と arc-runner-set の2つの Helm Chart をデプロイすることで利用可能になります。ドキュメントに倣って namespace arc-systems に arc をインストールしました。

helm install arc \
--namespace arc-systems \
--create-namespace \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

これで、ARC の実行に必要な CRD (Custom Resource Definition) と Controller がデプロイされます。

$ kubectl get po -n arc-systems
NAME READY STATUS RESTARTS AGE
arc-gha-runner-scale-set-controller-755f574df6-5hx9z 1/1 Running 0 25s

ARC で Runner の Pod を実行してリポジトリ用の Runner として登録するには PAT (Personal Access Token) の作成が必要になります[2]。PAT の scope としては、repoworkflow を指定しておけばよさそうです。

GitHub リポジトリと PAT を指定して arc-runner-set の Helm chart をインストールします。以下の例では、GITHUB_CONFIG_URL でリポジトリの URL を、GITHUB_PAT で PAT を helm install 時の環境変数として指定しています。arc とは別の runners 用の namespace (ここでは arc-runners) にインストールするのがよさそうです。

GITHUB_CONFIG_URL="https://github.com/<your_account/repo>"
GITHUB_PAT="<PAT>"
helm install arc-runner-set \
--namespace arc-runners \
--create-namespace \
--set githubConfigUrl="${GITHUB_CONFIG_URL}" \
--set githubConfigSecret.github_token="${GITHUB_PAT}" \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

インストールすると、ターゲットのリポジトリに Runner が arc-runner-set ラベルでオンライン状態になります。

GitHub Runners

この時点で、namespace arc-runners 配下には Pod は作成されていません。

$ kubectl get pods -n arc-runners
No resources found in arc-runners namespace.

ARC のランナーでワークフローを動かす

#

それでは、ARC のランナーを指定してワークフローを実行してみます。ドキュメント通り以下のようなワークフローファイルをリポジトリの .github/workflows 配下に作成しました。runs-onarc-runner-set ラベルを指定しています。

  • arc-demo.yml
name: Actions Runner Controller Demo
on:
workflow_dispatch:

jobs:
Explore-GitHub-Actions:
# You need to use the INSTALLATION_NAME from the previous step
runs-on: arc-runner-set
steps:
- run: echo "🎉 This job uses runner scale set runners!"

このワークフローを手動実行。ARC の Runner が作成されてジョブが実行されました。

workflow log

ワークフローが実行されている間に arc-runner-set をインストールした namespace arc-runners で Pod 一覧を watch しました。

kubectl get pods -n arc-runners -w
NAME                                READY   STATUS    RESTARTS   AGE
arc-runner-set-t9flz-runner-9q6k5   0/1     Pending   0          0s
arc-runner-set-t9flz-runner-9q6k5   0/1     Pending   0          0s
arc-runner-set-t9flz-runner-9q6k5   0/1     ContainerCreating   0          0s
arc-runner-set-t9flz-runner-9q6k5   1/1     Running             0          1s
arc-runner-set-t9flz-runner-9q6k5   0/1     Completed           0          27s
arc-runner-set-t9flz-runner-9q6k5   0/1     Terminating         0          27s
arc-runner-set-t9flz-runner-9q6k5   1/1     Terminating         1 (1s ago)   28s
arc-runner-set-t9flz-runner-9q6k5   0/1     Terminating         1 (2s ago)   29s
arc-runner-set-t9flz-runner-9q6k5   0/1     Terminating         1 (2s ago)   29s
arc-runner-set-t9flz-runner-9q6k5   0/1     Terminating         1 (2s ago)   29s

ワークフローの起動に応じて runner-set の Pod が作成され Running になり、ワークフローの終了とともに Terminating になり破棄されました。

namespace arc-systems にデプロイされた Listener の Pod のログを見てみると、ワークフローの実行に応じて Runner の起動がリクエストされて Runner が起動されジョブにアサインされる様子を追うことができました。

kubectl logs arc-runner-set-754b578d-listener -n arc-systems
2023-05-14T11:01:19Z	INFO	refreshing token	{"githubConfigUrl": "https://github.com/kondoumh/iac-dev"}
2023-05-14T11:01:19Z	INFO	getting runner registration token	{"registrationTokenURL": "https://api.github.com/repos/kondoumh/iac-dev/actions/runners/registration-token"}
2023-05-14T11:01:19Z	INFO	getting Actions tenant URL and JWT	{"registrationURL": "https://api.github.com/actions/runner-registration"}
2023-05-14T11:01:21Z	INFO	auto_scaler	current runner scale set statistics.	{"statistics": "{\"totalAvailableJobs\":0,\"totalAcquiredJobs\":0,\"totalAssignedJobs\":0,\"totalRunningJobs\":0,\"totalRegisteredRunners\":0,\"totalBusyRunners\":0,\"totalIdleRunners\":0}"}
2023-05-14T11:01:21Z	INFO	service	waiting for message...
2023-05-14T11:23:28Z	INFO	service	process message.	{"messageId": 1, "messageType": "RunnerScaleSetJobMessages"}
2023-05-14T11:23:28Z	INFO	service	current runner scale set statistics.	{"available jobs": 1, "acquired jobs": 0, "assigned jobs": 0, "running jobs": 0, "registered runners": 0, "busy runners": 0, "idle runners": 0}
2023-05-14T11:23:28Z	INFO	service	process batched runner scale set job messages.	{"messageId": 1, "batchSize": 1}

# 中略

2023-05-14T11:24:49Z	INFO	service	job completed message received.	{"RequestId": 174, "Result": "succeeded", "RunnerId": 23, "RunnerName": "arc-runner-set-t9flz-runner-nz4z8"}
2023-05-14T11:24:49Z	INFO	auto_scaler	acquiring jobs.	{"request count": 0, "requestIds": "[]"}
2023-05-14T11:24:49Z	INFO	service	try scale runner request up/down base on assigned job count	{"assigned job": 0, "decision": 0, "min": 0, "max": 2147483647, "currentRunnerCount": 1}
2023-05-14T11:24:49Z	INFO	KubernetesManager	Created merge patch json for EphemeralRunnerSet update	{"json": "{\"spec\":{\"replicas\":null}}"}
2023-05-14T11:24:49Z	INFO	KubernetesManager	Ephemeral runner set scaled.	{"namespace": "arc-runners", "name": "arc-runner-set-t9flz", "replicas": 0}
2023-05-14T11:24:50Z	INFO	auto_scaler	deleted message.	{"messageId": 4}
2023-05-14T11:24:50Z	INFO	service	waiting for message...

サンプルのワークフローはメッセージをエコーするだけの簡単なものでしたので、もう少し現実的なワークフローを動かしてみます。以下のワークフローでは次のステップでジョブを実行しています。

  • Electron アプリのソースコードをチェックアウト
  • Node.js 環境を setup
  • npm install を実行
  • electron-builder バイナリーをビルド
  • ビルドしたバイナリーを成果物としてアップロード
name: Build electron app Linux installer with Actions Runner Controller

on:
workflow_dispatch:

jobs:
build:

runs-on: arc-runner-set

steps:
- uses: actions/checkout@v3
with:
repository: 'mamezou-tech/electron-example-browserview'
path: electron-example-browserview
- name: Setup nodejs
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: |
cd electron-example-browserview
npm install

- name: Package
run: |
cd electron-example-browserview
npx electron-builder --dir

env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: electron-example-browserview
path: electron-example-browserview/dist/**

runs-on には arc-runner-set を指定しています。このワークフローを実行すると、無事にビルドが完了して、成果物が Summary ページにアップロードされました。

build electron app

Information

このワークフローでビルドしているのは、以下の記事で作成した Electron アプリです。

Electron - WebView から BrowserView に移行する

最後に

#

以上、パブリックベータになった GitHub Actions Runner Controller の紹介でした。ワークフローの実行時にセルフホストランナーをオンデマンドでプロビジョニングして終了時に破棄でき、オートスケーリングも可能なことから、CI/CD 用のランナーの VM を常時起動させる必要がなくなり、ランニングコストの削減が期待できます。

VM に構築したセルフホストランナーはイミュータブルではなくクリーンアップが課題でしたが、Pod で起動するランナーはその課題も解決されています。

セルフホストランナーを多用しているプロジェクトでは正式公開が待ち遠しい機能ではないかと思います。


  1. 他のパブリッククラウドのマネージド Kubernetes については明記されていませんが使えるのではないかと思います。 ↩︎

  2. 正式公開時には OIDC 認証も使えるようになるといいですね。 ↩︎

豆蔵デベロッパーサイト - 先週のアクセスランキング
  1. 基本から理解するJWTとJWT認証の仕組み (2022-12-08)
  2. AWS認定資格を12個すべて取得したので勉強したことなどをまとめます (2022-12-12)
  3. Nuxt3入門(第4回) - Nuxtのルーティングを理解する (2022-10-09)
  4. Nuxt3入門(第1回) - Nuxtがサポートするレンダリングモードを理解する (2022-09-25)
  5. Nuxt3入門(第8回) - Nuxt3のuseStateでコンポーネント間で状態を共有する (2022-10-28)
  6. Jest再入門 - 関数・モジュールモック編 (2022-07-03)
  7. 自然言語処理初心者が「GPT2-japanese」で遊んでみた (2022-07-08)
  8. IoT を使ってみる(その6:MQTTブローカー Mosquitto編) (2022-10-08)
  9. Nuxt3入門(第3回) - ユニバーサルフェッチでデータを取得する (2022-10-06)
  10. 統計学で避けて通れない自由度の話 (2022-06-20)