理系学生日記

おまえはいつまで学生気分なのか

GitHub ActionsのSelf-hosted RunnerをCodeBuildで構成する

GitHubで開発を行う際、CI/CDには通常GitHub Actionsを利用することが多い。プライベートリポジトリであっても同様だが、プランによっては無償で利用できるRunnerの上限が月2,000分までという制限がある。

For private repositories, each GitHub account receives a certain amount of free minutes and storage for use with GitHub-hosted runners, depending on the account's plan. Any usage beyond the included amounts is controlled by spending limits.

About Billing for GitHub Actions

今回、この制限を超えてしまったリポジトリがあり、代替案を検討したところ、AWSアカウントを利用してCodeBuildをRunnerとして活用する方法を採用することにした。

GitHub ActionsからのSelf-hosted Runner on CodeBuildの利用

GitHub ActionsからCodeBuildをSelf-hosted Runnerとして利用する方法は大きく2つある。

  1. runs-onにCodeBuild上のRunnerを指定し、ワークフロー全体を実行する。
  2. GitHub Actionsの特定のステップとして、aws-codebuild-run-buildアクションを利用しCodeBuild上のプロジェクトを実行する。

前者はGitHub Actionsのワークフロー全体をCodeBuild上で実行する方式であり、後者は一部のステップのみをCodeBuildで実行する方式である。しかし、今回はGitHub Actionsの無償枠を超過しているため、後者の方法ではRunnerを利用できない。そのため、前者の方式を前提に構築を進めた。

AWS環境の構築(Terraform)

AWS環境はTerraformを用いて構築する。必要なリソースは以下の通り。

  • CodeBuildプロジェクト
  • GitHubと接続するためのCodeConnections
  • Webhookの設定

特に注意が必要なのは、CodeBuildとGitHubの関連付けを行わないとWebhookの作成に失敗する点である。そのため、Terraformの適用は2フェーズに分ける必要がある。

1. CodeBuildの構築

CodeBuildの構築はシンプルで、以下のようにTerraformで定義する。

resource "aws_codebuild_project" "github_runner" {
  name               = "github-runner"
  description        = "GitHub Runner for GitHub organization"
  service_role       = aws_iam_role.codebuild.arn
  build_timeout      = "60" # minutes
  badge_enabled      = false
  project_visibility = "PRIVATE"

  environment {
    # see: https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types
    compute_type = "BUILD_GENERAL1_SMALL" # 4 GiB、2 vCPU

    # see: https://docs.aws.amazon.com/codebuild/latest/userguide/ec2-compute-images.html
    image = "aws/codebuild/standard:5.0" # Amazon Linux 2023
    type  = "LINUX_CONTAINER"
  }

  logs_config {
    cloudwatch_logs {
      status     = "ENABLED"
      group_name = aws_cloudwatch_log_group.codebuild.name
    }
  }

  source {
    type                = "GITHUB"
    buildspec           = var.build_spec_content # optionalなので不要な気もする
    git_clone_depth     = 1
    location            = var.github_repository_url
    report_build_status = true
  }

  artifacts {
    type = "NO_ARTIFACTS"
  }
}

# 関連リソースは略

2. CodeBuildとGitHubとの関連付け

CodeConnectionsを利用してGitHubとの接続を設定する。これはGitHub Appsを組織やリポジトリに導入することを意味する。

resource "aws_codeconnections_connection" "github_connection" {
  name          = var.connection_name
  provider_type = "GitHub"
}

Terraformを適用すると、AWS管理コンソールの「接続」メニューに保留状態のレコードが作成される。

接続

この「保留中の接続を更新」ボタンを押し、GitHubとのOAuth認証を完了させると、AWS Connector for GitHubがインストールされる。 ちょっとわかりづらい遷移だと思うが、このあたりの操作はエントリがわかりやすい。

3. Webhookの構築

GitHubのPull Request作成などのイベントをトリガーに、CodeBuildがRunnerを起動する。Webhookは以下のTerraformコードで作成可能。

webhook

applyしたら上の画像のように、GitHub側にWebhookが構成される。

resource "aws_codebuild_webhook" "github_runner" {
  project_name = aws_codebuild_project.github_runner.name
  build_type   = "BUILD"

  filter_group {
    filter {
      pattern = "WORKFLOW_JOB_QUEUED"
      type    = "EVENT"
    }
  }
}

GitHub Actions側

GitHub Actions側では、RunnerとしてCodeBuildを指定する。以下のサンプルは、AWSの公式ドキュメントLabel overrides supported with the CodeBuild-hosted GitHub Actions runnerから引用したものである。

name: Hello World
on: [push]
jobs:
  Hello-World-Job:
    runs-on:
      - codebuild-myProject-${{ github.run_id }}-${{ github.run_attempt }}
      - image:${{ matrix.os }}
      - instance-size:${{ matrix.size }}
      - fleet:myFleet
      - buildspec-override:true
    strategy:
      matrix:
        include:
          - os: arm-3.0
            size: small
          - os: linux-5.0
            size: large
    steps:
      - run: echo "Hello World!"

runs-onでのRunner指定ルールはcodebuild-<project-name>-${{github.run_id}}-${{github.run_attempt}}というルールになっている。 また、CodeBuildのプロジェクト側にデフォルトのイメージやインスタンスサイズを設定できるが、ワークフロー側でimageinstance-sizeを上書きすることも可能である。

結果

この設定により、GitHub ActionsのワークフローがCodeBuild上で実行されるようになった。これでプライベートリポジトリのActions実行時間上限を気にせずに済むようになった。

参考文献