GitHub Actionsのセルフホステッド ランナーをLambda  MicroVMs上で実行してみた

GitHub Actionsのセルフホステッド ランナーをLambda MicroVMs上で実行してみた

Construct Hubで公開されているcdk-github-microvm-runnersを使ってLambda MicroVMs上にGitHub Actionsのセルフホステッドランナーを構築してみました。 高速に起動してスケールアウトも容易なので、セルフホステッドランナーの実行基盤としてはなかなか良い選択肢ではないでしょうか? GitHub Actionsの処理時間やコストに悩みを抱えている方は参考にしてみてください。
2026.08.27

リテールアプリ共創部@大阪の岩田です。

先日公開されていた以下の記事を読み、Lambda MicroVMs上にGitHub Actionsのセルフホステッドランナーを構築する構成を試してみたくなりました。

https://dev.classmethod.jp/articles/self-hosted-runner-with-mbp/

ECSでRunnerを都度起動するアーキテクチャのデメリットとして挙げられている起動時間やスケールアウト速度の問題をLambda MicroVMsを利用することで解消できないか?と考えてのことです。

さっそく検証用の環境を作ってみようと思ったところ...

すでに先人による成果物がConstruct Hubで公開されていました。今回はこちらのConstructを使ってLambda MicroVMs上にセルフホステッドランナーを構築して検証していきます。

環境

今回利用した環境は以下の通りです。

  • cdk-github-microvm-runners: 0.1.7
  • aws-cdk-lib: 2.266.0

構成概要

詳細はcdk-github-microvm-runnersのGitHubリポジトリにて解説されています。

https://github.com/schuettc/cdk-github-microvm-runners/blob/main/docs/architecture.md

https://github.com/schuettc/cdk-github-microvm-runners/blob/2aa10fffea58b20c88166f2afefb032cdbef39c6/docs/architecture.md?plain=1#L17-L28

ざっくりした流れは以下の通りです。

  • GitHub Actionsのワークフローが起動するとWebHookでLambdaを呼び出す
  • Lambdaは呼び出し元を検証し、問題なければSQSにメッセージを投入
  • SQSからLambaが起動、LambdaがMicroVMを起動してGitHubにJust-in-time runnerとしてセルフホステッドランナーを登録
  • セルフホステッドランナーがワークフローを実行する

起動したMicroVMの情報はDynamoDBで管理されており、ジョブの重複実行などが発生しないように制御されているとのことです。

やってみる

それでは早速セルフホステッドランナー on Lambda MicroVMsの環境を作っていきましょう。

CDKで必要なリソース一式をデプロイ

まず npx cdk init --language typescript を実行してCDKコードを準備、続いてnpm install cdk-github-microvm-runnersでcdk-github-microvm-runnersをインストールします。

準備できたらGetting startedにならってCDKのコードを書きます。

import { App, CfnOutput, Stack } from 'aws-cdk-lib';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
import {
  GithubAppId,
  GithubAppKey,
  GithubAuth,
  GithubMicrovmRunners,
  MicrovmSize,
  RunnerScope,
} from 'cdk-github-microvm-runners';

const app = new App();
const stack = new Stack(app, 'Runners', { env: { region: 'us-east-1' } });

const appId = Secret.fromSecretNameV2(
  stack,
  'AppId',
  'microvm-runner/dev/app-id',
);
const privateKey = Secret.fromSecretNameV2(
  stack,
  'AppKey',
  'microvm-runner/dev/app-private-key',
);
const webhookSecret = Secret.fromSecretNameV2(
  stack,
  'WebhookSecret',
  'microvm-runner/dev/webhook-secret',
);

const runners = new GithubMicrovmRunners(stack, 'Runners', {
  github: GithubAuth.app({
    appId: GithubAppId.fromSecret(appId),
    privateKey: GithubAppKey.fromSecret(privateKey),
    webhookSecret,
  }),
  scope: RunnerScope.org('<ここにセルフホステッドランナーを使いたいOrgの名前を設定>'),
});

// A runner class: the `microvm` label, on 4 GB VMs.
runners.addRunnerClass('microvm', { size: MicrovmSize.GB4 });

new CfnOutput(stack, 'WebhookUrl', { value: runners.webhookUrl });
new CfnOutput(stack, 'SetupCommand', { value: runners.setupCommand });

RunnerScope.orgにセルフホステッドランナーを登録したいGitHubのOrg名を設定するのがポイントです。

コードの準備ができたので、npm cdk deployでスタックをデプロイします。しばらく待つと各種のリソースが作成されます。

作成されたCloudFormationのスタック

MicroVMのイメージも登録されています。

登録されたMicroVMイメージ

ちなみに上記のMicroVMのイメージをビルドするのに利用されたDockerfileをS3からDLすると、中身は以下の通りでした。

FROM public.ecr.aws/lambda/microvms:al2023-minimal
RUN dnf install -y libicu bash git docker jq tar zip unzip nodejs22 sudo shadow-utils && dnf clean all
RUN dnf install -y gh --repofrompath gh-cli,https://cli.github.com/packages/rpm || true
RUN curl -fsSL https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip -o /tmp/awscliv2.zip && unzip -q /tmp/awscliv2.zip -d /tmp && /tmp/aws/install && rm -rf /tmp/aws /tmp/awscliv2.zip
RUN useradd -m runner && usermod -aG docker runner || groupadd docker && usermod -aG docker runner
RUN mkdir -p /opt/runner && cd /opt/runner && curl -fsSLo r.tgz https://github.com/actions/runner/releases/download/v2.335.1/actions-runner-linux-arm64-2.335.1.tar.gz && tar xzf r.tgz && rm r.tgz && chown -R runner:runner /opt/runner
COPY microvm-runner/agent.mjs /opt/microvm-runner/agent.mjs
COPY microvm-runner/entrypoint.sh /opt/microvm-runner/entrypoint.sh
RUN chmod +x /opt/microvm-runner/entrypoint.sh
ENTRYPOINT ["/opt/microvm-runner/entrypoint.sh"]

イメージをカスタマイズしたい場合はCDKのコードで調整も可能です。例えばRunnerImage.fromInlineを利用した以下のような書き方が用意されています。

runners.addRunnerClass('custom', {
  size: MicrovmSize.GB4,
  image: RunnerImage.fromInline(`
FROM public.ecr.aws/lambda/microvms:al2023-minimal
...

`),
});

詳細については以下のリンクで解説されています。

https://github.com/schuettc/cdk-github-microvm-runners/blob/main/docs/images.md

GitHub Appのセットアップ

スタックがデプロイできたのでGitHub Appを作成します。セットアップ用のコマンドは先程デプロイされたCFnスタックのOutputにも出力されていますが、以下のようなコマンドになります。

npx cdk-github-microvm-runners@0.1 setup --org <対象のGitHub Org名> --stack Runners --region us-east-1

--stackオプションはCFnのスタック名と、--regionはスタックがデプロイされたリージョンに合わせてください。

上記のコマンドを実行するとGitHub Appを作成する画面が開くので、ユニークになるように名前を指定します。

GitHub Appの作成画面

作成したGitHub AppをOrgにインストールして良いか確認されるので、インストールをクリックします。GitHub Appのパーミッション許可画面

インストールが完了したらブラウザは閉じておきましょう。GitHub Appの作成完了画面

これで必要な準備は完了です。一連の処理の裏側でCDKのコードで指定したmicrovm-runner/dev/app-id等のシークレット値も登録されています。

自動登録されたシークレット値

セルフホステッドランナーを使うワークフローを動かしてみる

ここまででセルフホステッドランナーを使う準備ができたので、適当にリポジトリを作成してGitHub Actionsのワークフローを起動してみます。まず以下のようなシンプルなワークフローを試してみました。

on:
  workflow_dispatch
jobs:
  test:
    runs-on: [self-hosted, microvm]
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: test
        run: |
          echo test
          sleep 120

ワークフローを起動したあと、Orgの設定からRunnersを確認すると、セルフホステッドランナーが登録されたことが分かります。

セルフホステッドランナーが自動登録された画面

さらに数秒待つと、登録されたセルフホステッドランナー上でワークフローが実行開始されます。セルフホステッドランナー上でワークフローが実行されている

ログでは以下の通りです。ちゃんと実行できていることが分かります。

セルフホステッドランナー上で実行されたジョブのログ

ログの詳細を確認すると、ランナーにジョブがピックされるまで約20秒程度かかっているようです。

2026-08-27T02:56:56.9760000Z Waiting for a runner to pick up this job...
2026-08-27T02:57:15.4380000Z Job is about to start running on the runner: microvm-runner-ba2f8119-bc1605de

cdk-github-microvm-runnersのドキュメントによると、各フェーズの所要時間は以下の通りになるそうです。ワークフローがトリガーされてから実際に処理が始まるまで、全体で約25秒程度の待ち時間になります。

Segment Time What happens
Queued → the VM is running 6.8 s webhook delivery, the queue, the launcher, VM boot
VM running → the runner's first log line 8.2 s run.sh, the .NET host starting, assemblies loading
Runner starting → connected to GitHub 9.0 s reading its configuration, registering
Connected → the job begins 1.0 s GitHub assigns the queued job to it
Total 25 s

※ 表は https://github.com/schuettc/cdk-github-microvm-runners/blob/main/docs/architecture.md より引用

せっかくなのでDynamoDBのテーブルの中身も確認しておきましょう。テーブルをScanすると、以下のようなアイテムが登録されていました。

管理用のDynamoDBのテーブルをスキャンした結果

実際にデータを見ると、裏側で動いている処理がよりイメージしやすいですね。

せっかくなので別のワークフローを作ってDockerが使えることを確認してみましょう。ワークフローの定義は以下です。

on:
  workflow_dispatch
jobs:
  test:
    runs-on: [self-hosted, microvm]
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: test
        run: |
          docker run --rm  hello-world
    services:
      postgres:
        image: postgres
        env:
          POSTGRES_PASSWORD: postgres

runの中でdockerコマンドを実行しているのと、servicesの指定でpostgresのコンテナを起動しています。

このワークフローを実行した際のログは以下の通りでした。

セルフホステッドランナー上で実行したジョブのログ その2

セルフホステッドランナー上で実行したジョブのログ その3

ちゃんとLambda MicroVMs上でDockerが利用できていますね。CodeBuildの実行環境にLambdaを選択した場合はDockerが利用できませんが、セルフホステッドランナー on Lambda MicroVMsであればDockerも問題なく利用可能です。やったぜ!

考察

とりあえずLambda MicroVMs上でGitHub Actionsのセルフホステッドランナーを稼働させてワークフローが実行できましたが、果たしてこの構成は有効な構成なのか考察してみます。

処理時間が同じという前提で料金を比較

まずセルフホステッドランナーを利用した場合もワークフローの実行時間が変わらないという前提のもと単純な料金だけで比較してみます。

GitHub-hosted runnersの場合、SKU linux_2_core_armの料金は$0.005/分となります。linux_2_core_arm相当のインスタンスをLambda MicroVMsで起動するとバージニアリージョンの場合でも以下の試算になります。

  • vCPU price per second: 2vCPU × $0.0000276944
  • Memory price per second (per GB): 8GB × $0.0000036667

1分あたりの料金は(2×0.0000276944 + 8 * 0.0000036667) × 60で$0.00508334となります。簡略化のために割愛していますが、実際にはさらにスナップショットを保存するためのストレージ料金なども発生します。そのため、単純な試算ではGitHub-hosted runnersの方が安いという試算になりますが、Saving Plan等を活用することでコストメリットが出せる可能性はあります。

あとはLambda MicorVMsの場合は課金が分単位ではなく秒単位という点もメリットと言えます。GitHub-hosted runnersの場合は課金が分単位に丸められるため、Lambda MicroVMsなら1分31秒の課金で済むところがGitHub-hosted runnersの場合は2分の課金になるというケースも考えられます。

処理時間が同じになるのか?

先程の試算はワークフローの実行時間が変わらないという前提のもとでの試算でしたが、実際はどうなんでしょうか?

今回利用したcdk-github-microvm-runnersはJust-in-time runnerとしてセルフホステッドランナーを登録し、MicroVMを毎回Terminateする設計になっていますが、Lambda MicroVMsは一時停止と再開に対応しています。ワークフローの実行完了後にMicroVMを一時停止し、次回同じワークフローが起動した時は一時停止中のMicroVMを再開するようなアーキテクチャで実装しなおせば、ステートを保持できるというLambda MicroVMsの特徴を最大限に活かせそうです。

例えばワークフロー内でapt installを実行する処理があったとします。MicroVMの初回起動時はパッケージのインストール処理がフルに実行されるため処理時間が長くなりますが、2回目以後のワークフロー実行時はパッケージインストール済のMicroVMを再開するようにすればインストール処理はスキップされてコマンドの実行は高速に完了するはずです。

さらに進んで、自身のワークロードに最適化したMicroVMのイメージをビルドするというアプローチも考えられます。ワークフローの中でapt installを実行するのではなく、MicroVMのイメージビルド時にapt installするようにすればワークフローの処理時間をさらに削減できます。

特定のプロジェクトだけでセルフホステッドランナーを利用したいのか、それとも組織の基盤として複数のチームにセルフホステッドランナーを提供したいのか?要件次第でMicroVMのイメージにどこまで手を加えられるのかの考え方は変わってきそうですが、セルフホステッドランナーという特性を活かしたワークフローの処理時間削減は一考の価値がありそうです。

まとめ

Lambda MicroVMs上にGitHub Actionsのセルフホステッドランナーを構築して試してみました。作り込めばなかなか面白いことができそうじゃないでしょうか?

GitHub Actionsの処理時間やコストに悩みを抱えている方は一度セルフホステッドランナー on Lambda MicroVMsという構成を検討してみてはいかがでしょうか?

参考

この記事をシェアする

関連記事