GitHub Actions と AWS CodeBuild による Self-hosted runners の連携

記事タイトルとURLをコピーする

こんにちは。
アプリケーションサービス部、DevOps担当の兼安です。

本記事はこれらの記事の続きです。

blog.serverworks.co.jp

前回、GitLab Self-Managed と AWS CodeBuild を連携して、Self-managed runners を利用する方法について紹介したので、今回は GitHub と AWS CodeBuild を連携してSelf-hosted runners を動かしてみようと思います。

本記事のターゲット

本記事はGitHubの上級者向けの記事となります。
GitHub、CI/CD、AWS CodeBuildに関する基本的な知識をお持ちの方を対象としています。

CodeBuild がホストする GitHub Actions ランナーを作成する

GitHub用のAWS CodeBuildランナーを作成するには、下記の公式ドキュメントに従って設定を行います。
実際にやってみることにします。

docs.aws.amazon.com

CodeBuildのプロジェクトの作成画面に移動し、プロジェクト名を入力、「プロジェクトの種類」で「ランナープロジェクト」を選択します。
ここで入力したプロジェクト名は、後ほどGitHub Actions ワークフロー側の設定で使用します。

プロジェクトタイプでランナープロジェクトを選択

ランナープロバイダーでGitHubを選択して、リポジトリURLを入力します。
この際、GitHubとの接続認証が必要となりますので、事前に認証設定を済ませておきます。

ランナープロバイダーでGitHubを選択し、リポジトリURLを入力

ランナーの環境の設定を行います。
GitHub ActionsのGitHubホステッドランナーは、OSはデフォルトでUbuntuなので、Ubuntuを選択するのが無難と思います。
IAMロールはデフォルトのままとしました。

環境の設定

OSの設定

プロジェクトの作成を完了する時のメッセージが表示されます。

プロジェクトの作成完了メッセージ

プロジェクトの作成が完了すると、プロジェクト作成時に指定したGitHubリポジトリにWebhookが自動的に作成されます。
これは、GitHub Actions ワークフローがCodeBuildランナーを起動するのにWebhookを使用するためです。

GitHubリポジトリにWebhookができている

GitHub Actions ワークフロー側の設定

GitHub Actions ワークフロー側の設定も行います。
.github/workflowsディレクトリに作成したYAMLファイルで、runs-onに以下のように指定します。

runs-on: codebuild-<project-name>-${{ github.run_id }}-${{ github.run_attempt }}

<project-name>には、先ほど作成したCodeBuildのプロジェクト名を指定します。

テストしたワークフローは以下の通りです。
Lint、ユニットテスト、SemgrepによるSAST、そしてTrivyによるSCAを実行する内容となっています。
キャッシュと並列実行が動くかも試したいので、それらの要素も入れています。

GitHub Actions のワークフローの画面

name: CodeBuild GitHub Runner CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  # キャッシュ作成ジョブ
  prepare:
    name: Prepare
    runs-on: codebuild-github-runner-project-${{ github.run_id }}-${{ github.run_attempt }}
    environment: production
    outputs:
      cache-key: ${{ steps.cache-key.outputs.key }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v5

      - name: Generate cache key
        id: cache-key
        run: |
          CACHE_KEY="cache-$(sha256sum .tool-versions uv.lock | sha256sum | cut -d' ' -f1)"
          echo "key=$CACHE_KEY" >> $GITHUB_OUTPUT
          echo "Cache key: $CACHE_KEY"

      - name: Cache dependencies
        id: cache
        uses: actions/cache@v4
        with:
          path: |
            ~/.asdf
          key: ${{ steps.cache-key.outputs.key }}
          restore-keys: |
            cache-
    
      - name: Setup asdf
        uses: asdf-vm/actions/setup@v4

      - name: Install tools via asdf
        run: |
          asdf plugin add python
          asdf install

  lint_test_sast:
    name: Lint Test SAST
    runs-on: codebuild-github-runner-project-${{ github.run_id }}-${{ github.run_attempt }}
    needs: prepare
    environment: production
    steps:
      - name: Checkout code
        uses: actions/checkout@v5

      - name: Restore cache
        uses: actions/cache@v4
        with:
          path: |
            ~/.asdf
          key: ${{ needs.prepare.outputs.cache-key }}
          restore-keys: |
            cache-

      - name: Setup environment
        run: |
          echo "$HOME/.asdf/bin:$HOME/.asdf/shims:$HOME/.local/bin" >> $GITHUB_PATH
          echo "UV_PYTHON=$HOME/.asdf/shims/python" >> $GITHUB_ENV
          curl -LsSf https://astral.sh/uv/install.sh | sh
          export PATH="$HOME/.asdf/bin:$HOME/.asdf/shims:$HOME/.local/bin:$PATH"
          echo "Using Python: $HOME/.asdf/shims/python"
          $HOME/.asdf/shims/python --version
          UV_PYTHON=$HOME/.asdf/shims/python uv sync --group dev
          
      - name: Run linting
        run: |
          uv run ruff check .
          uv run ruff format --check .

      - name: Run tests
        run: |
          uv run pytest modules/api/tests/ -v --cov=modules/api --cov-report=xml

      - name: Run Semgrep SAST scan
        run: |
          uv run semgrep scan --config=auto --error modules/

  # SCAチェックジョブ(Trivy)
  sca_trivy:
    name: SCA (Trivy)
    runs-on: codebuild-github-runner-project-${{ github.run_id }}-${{ github.run_attempt }}
    needs: prepare
    steps:
      - name: Checkout code
        uses: actions/checkout@v5

      - name: Run Trivy vulnerability scanner in fs mode
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: "fs"
          scan-ref: "."
          trivy-config: trivy-config.yaml

CodeBuildのランナーの動作確認結果と注意事項

上述のワークフローを実行した結果、CodeBuildのランナーが起動し、ジョブが正常に完了することを確認しました。
また、キャッシュ、並列・直列実行が問題なく動作することも確認できました。

ただし、動作させる際にいろいろと微調整をしています。
上述のワークフローは元々は、GitHubホステッドランナーで動作させていたものですが、CodeBuildランナーで動作させるにあたり、以下の点を修正しています。

  • キャッシュ対象の変更
    • CodeBuildランナーの場合、uvのインストール先ディレクトリが異なるため、キャッシュ対象を修正
  • pytestsemgrepなど、一部のツールが動作しなかったため依存関係を微調整

パスや依存関係の変更の内容は、細かい話になるので割愛します。
列挙するよりも、実際に試してみてデバッグ出力しながら動作確認するのが早いかと思います。
この他に懸念していた点として、aquasecurity/trivy-action@masterなどのサードパーティ製のActionsがCodeBuildランナーで動作するかどうかがありましたが、取り急ぎ今回の例では問題なく動作しました。
CodeBuildのランナーで世の中にあるActionsが全て動くかは私の方ではわかりません。
おそらく個別に事情が異なると思われます。

総括すると、GitHubホステッドランナーからCodeBuildランナーへの移行、またはその逆の移行をする場合は、必ず動作確認と微調整が必要になると理解しています。

Amazon EC2によるSelf-hosted runnersとの比較

こちらの記事で、Amazon EC2上にSelf-hosted runnersを構築する方法について紹介しています。

blog.serverworks.co.jp

その際に、ランナーの指定方法に曖昧さがあると述べましたが、CodeBuildランナーの場合はプロジェクト名で指定できるため、曖昧さについては解消されます。
ただし、突き詰めるとコストパフォーマンスはEC2の方が有利だと思うので、使い分けですね。

今回は以上です。

参考

本記事の中でテストしたワークフローは、こちらの発表をした時の検証コードをベースにしています。

speakerdeck.com

兼安 聡(執筆記事の一覧)

アプリケーションサービス部 DS3課所属
2025 Japan AWS Top Engineers (AI/ML Data Engineer)
2025 Japan AWS All Certifications Engineers
2025 AWS Community Builders
Certified ScrumMaster
PMP
広島在住です。今日も明日も修行中です。
X(旧Twitter)