見出し画像

CodeBuild-hosted GitHub Actions Runnerの設定例とTips

note株式会社のSREチームのショウゴです。
本記事は、note株式会社 Advent Calendar 2024の10日目の記事です。

noteのSREチームでは、今年サポートが発表されたCodeBuild-hosted GitHub Actions Runnerを検証・一部導入を行いました。導入したことにより、低コストでスケーラブルなGitHub Actionsの実行環境を確保することができました。

本記事では、CodeBuild-hosted GitHub Actions RunnerのTerraformとCloudFormationによる設定例と検証した際に得られたTipsを紹介します。


CodeBuild-hosted GitHub Actions Runnerとは

CodeBuild-hosted GitHub Actions Runner は、AWS CodeBuildをGitHubのself-hosted runnerとして利用することができ、AWS CodeBuild上でGitHub Actionsを動かすことができる仕組みです。

アップデートの変遷

今年の4月にAWS CodeBuildがGitHub Actions Runnerをサポート開始するというアップデートが発表されました。

また、初期のサポートではGitHubのリポジトリ単位のwebhookのみに対応していましたが、今年の6月にGitHubのOrganization単位のwebhookが作成できるようになったというアップデートが発表されました。

AWS CodeBuildを使う利点

AWS CodeBuildを使う利点としては、コンピューティングにLambdaが使えることが挙げられます。厳密な比較ではありませんが、おおよそのコスト比較は以下となります。armのLambdaを利用した場合が最も低価格で利用できます。

  • GitHubの場合

    • Linux 2コア:0.008[ドル/分]

  • CodeBuildの場合

    • EC2

      • x86_64, 3GB, 2vCPU:0.005[ドル/分]

      • arm, 3GB, 2vCPU:0.00425[ドル/分]

    • Lambda

      • x86_64, 1GB:0.00002[ドル/秒] = 0.0012[ドル/分]

      • arm, 1GB:0.00001[ドル/秒] = 0.0006[ドル/分]

設定例

次にAWS側のリポジトリ単位とOrganization単位の2種類の設定例とGithubのワークフローの設定例を紹介します。

AWS側の設定(リポジトリ単位)

リポジトリ単位のCodeBuildプロジェクトを作成するTerraformのコードの例が以下となります。

# main.tf

# ---------------------------------
# CodeBuild Project

resource "aws_codebuild_project" "app_project" {
  name         = "sample-repository-codebuild-project"
  description  = "GitHub Actions Runner in CodeBuild for sample repository"
  service_role = aws_iam_role.app_role.arn

  artifacts {
    type = "NO_ARTIFACTS"
  }

  source {
    buildspec       = file("${path.module}/buildspec.yaml")
    git_clone_depth = 1
    location        = "https://github.com/org/sample_repository.git"
    type            = "GITHUB"

    git_submodules_config {
      fetch_submodules = false
    }
  }

  environment {
    compute_type = "BUILD_GENERAL1_SMALL"
    image        = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
    type         = "LINUX_CONTAINER"
  }

  logs_config {
    cloudwatch_logs {
      status     = "ENABLED"
      group_name = "/aws/codebuild/github-actions-runner-sample-repository-logs"
    }
  }

  # VPCに配置する場合
  vpc_config {
    vpc_id             = "vpc_id"
    subnets            = ["subnet_id"]
    security_group_ids = ["security_group_id"]
  }
}

# ---------------------------------
# CodeBuild Webhook

resource "aws_codebuild_webhook" "app_webhook" {
  project_name = "sample-repository-codebuild-project"
  filter_group {
    filter {
      pattern = "WORKFLOW_JOB_QUEUED"
      type    = "EVENT"
    }
  }
}

# ---------------------------------
# Data: IAM Policy Document

data "aws_iam_policy_document" "codebuild_role" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["codebuild.amazonaws.com"]
    }
  }
}


data "aws_iam_policy_document" "codebuild_role_policy" {
  statement {
    sid    = "CodeBuildPolicy"
    effect = "Allow"
    actions = [
      "logs:CreateLogGroup",
      "logs:CreateLogStream",
      "logs:PutLogEvents",
      "s3:GetObject",
      "s3:GetObjectVersion",
      "s3:PutObject"
    ]
    resources = ["*"]
  }
}

# ---------------------------------
# IAM

resource "aws_iam_role" "app_role" {
  name               = "github-actions-runner-sample-repository-role"
  assume_role_policy = data.aws_iam_policy_document.codebuild_role.json
}

resource "aws_iam_role_policy" "app_role_policy" {
  name     = "github-actions-runner-sample-repository-role-policy"
  role   = aws_iam_role.app_role.id
  policy = data.aws_iam_policy_document.codebuild_role_policy.json
}

buildspec.ymlはデフォルトだと上書きされるため、以下の内容のように書きます。

# buildspec.yml

# このbuildspec.yamlはCodeBuildで上書きされる。
version: 0.2

phase:
  build:
    commands:
      - echo "Hello, CodeBuild!"

AWS側の設定(Organization単位)

Organization単位のCodeBuildプロジェクトを作成するTerraformとCloudFormationのコードの例が以下となります。

# main.tf

resource "aws_cloudformation_stack" "app_project" {
  name             = "sample-organization-codebuild-project"
  template_body    = file("${path.module}/cfn_template.yaml")
  disable_rollback = false

  parameters = {
    ProjectName            = "sample-organization-codebuild-project"
    Description            = "GitHub Actions Runner in CodeBuild for sample organization"
    ServiceRole            = aws_iam_role.app_role.arn
    EnvironmentType        = "LINUX_CONTAINER"
    EnvironmentComputeType = "BUILD_GENERAL1_SMALL"
    EnvironmentImage       = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
    ExcludeReposPattern    = "^(?!${local.exclude_repos_pattern}).*$" # 特定のリポジトリを除外したい場合
  }
}

# IAMはリポジトリ単位の場合と同様に設定します。
# cfn_template.yml

AWSTemplateFormatVersion: "2010-09-09"
Description: "GitHub Actions Runner in CodeBuild"

Parameters:
  ProjectName:
    Description: Project Name
    Type: String
  Description:
    Description: Description
    Type: String
  ServiceRole:
    Description: Service Role
    Type: String
  EnvironmentType:
    Description: Environment Type
    Type: String
  EnvironmentComputeType:
    Description: Environment Compute Type
    Type: String
  EnvironmentImage:
    Description: Environment Image
    Type: String
  ExcludeReposPattern:
    Description: Exclude Repos Pattern
    Type: String

Resources:
  CodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Ref ProjectName
      Description: !Ref Description
      ServiceRole: !Ref ServiceRole
      Artifacts:
        Type: NO_ARTIFACTS
      Environment:
        Type: !Ref EnvironmentType
        ComputeType: !Ref EnvironmentComputeType
        Image: !Ref EnvironmentImage
      Source:
        Type: GITHUB
        Location: CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION
        BuildSpec: buildspec.yml
      Triggers:
        Webhook: true
        ScopeConfiguration:
          Name: sample-organization
        FilterGroups:
          - - Type: EVENT
              Pattern: WORKFLOW_JOB_QUEUED
            - Type: REPOSITORY_NAME
              Pattern: !Ref ExcludeReposPattern
      LogsConfig:
        CloudWatchLogs:
          Status: ENABLED
          GroupName: !Sub /aws/codebuild/${ProjectName}
        S3Logs:
          Status: DISABLED
      Tags:
        - Key: Name
          Value: !Ref ProjectName

検証を行った2024年6月当時は、terraform-provider-awsの公式ドキュメントにOrganization単位での設定の仕方が明確に言及されていなかったため、CloudFormationで設定を行いましたが、v5.59.0でドキュメントが更新されているため、現在はTerraformでも定義が可能です。

terraform-provider-awsの公式ドキュメントより引用

GitHub側の設定

runs-onの箇所を以下のように設定します。ビルドプロジェクト名に「codebuild-」というprefixを付ける必要があります。このprefixをつけ忘れることがよくあるため注意が必要です。

  • リポジトリ単位

# org/repository/.github/workflows/sample.yml

name: Test
on: [push]
jobs:
  test:
    runs-on:
      - codebuild-sample-repository-codebuild-project-${{ github.run_id }}-${{ github.run_attempt }}
    steps:
      - run: echo "Hello World!"
  • Organization単位

# org/repository/.github/workflows/sample.yml

name: Test
on: [push]
jobs:
  test:
    runs-on:
      - codebuild-sample-organization-codebuild-project-${{ github.run_id }}-${{ github.run_attempt }}
    steps:
      - run: echo "Hello World!"

また、以下のようにimageの種類やインスタンスサイズを上書きすることができます。

# org/repository/.github/workflows/samlple.yml

name: Test
on: [push]
jobs:
  Test:
    runs-on:
      - codebuild-sample-repository-codebuild-project-${{ github.run_id }}-${{ github.run_attempt }}
      - image:arm-3.0 # 👈️ image指定の上書き
      - instance-size:large # 👈️ インスタンスタイズの指定の上書き
    steps:
      - run: echo "Hello World!"

アップデート発表直後は下記のように1行のラベルで書く方法が公式の書き方でしたが、現在はこの書き方はレガシーとなっているため上記の書き方が推奨されています。またFleetの指定や、buildspecを使えるようにするフラグの設定なども追加されています。

# org/repository/.github/workflows/legacy-sample.yml

name: Test
on: [push]
jobs:
  Test:
    // 1行のラベルで書く方法は非推奨の書き方です
    runs-on: codebuild-sample-repository-codebuild-project-${{ github.run_id }}-${{ github.run_attempt }}-arm-3.0-large
    steps:
      - run: echo "Hello World!"

Tipsの紹介

次に検証の過程で得られたTipsを紹介します。

1. GitHubの環境変数の活用

例えば、「aws/codebuild/amazonlinux2-aarch64-standard:3.0」のイメージを利用したい場合、ラベルの値として「arm-3.0」を使用します。Organization単位で利用し、今後新しいバージョンのイメージが出た場合に、複数のリポジトリのラベルを全て修正するのはかなり苦労する可能性があるため、GitHubのOrganizationの変数に最新版のイメージのラベル名を「GHA_ARM」のように設定しておくと一斉置換できるので良いのではないかと考えています。

# EC2利用の場合

GHA_ARM = arm-3.0

name: Test
on: [push]
jobs:
  Test:
    runs-on:
      - codebuild-sample-org-codebuild-project-${{ github.run_id }}-${{ github.run_attempt }}
      - image:${{ vars.GHA_ARM }}
      - instance-size:large
    steps:
      - run: echo "Hello World!"
# Lambda利用の場合

GHA_LINUX_LAMBDA_NODE = linux-lambda-nodejs20

name: Test
on: [push]
jobs:
  Test:
    runs-on:
      - codebuild-sample-org-codebuild-project-${{ github.run_id }}-${{ github.run_attempt }}
      - image:${{ vars.GHA_LINUX_LAMBDA_NODE }}
      - instance-size:large
    steps:
      - run: echo "Hello World!"

2. Webhook作成時の注意点

TerraformでWebhookを作成する際に以下のようなエラーが出ることがあります。この場合は、AWSのマネージメントコンソール上で、GitHubへの接続を行ってから再実行すると解消されます。

Error: creating CodeBuild Webhook (sample-repository-codebuild-project): operation error CodeBuild: CreateWebhook, https response error StatusCode: 400, RequestID: xxxxxx-xxxx-xxxx-xxxxx, ResourceNotFoundException: Access token not found in CodeBuild project for server type github
│ 
│   with aws_codebuild_webhook.app_webhook,
│   on config_codebuild.tf line XX, in resource "aws_codebuild_webhook" "app_webhook":
│   XX: resource "aws_codebuild_webhook" "app_webhook" {
AWS CodeBuildのコンソール画面

3. 構築時のデバッグ

ラベルを指定してワークフローを動かしてもCodeBuild側でビルドが開始されないことがあります。その場合は、GitHub側のWebhooksのRecent Deliveriesのページの実行履歴で、エラーなどの発生有無が確認できます。

Github > Settings > Webhooks
Webhooks > Edit > Recent Deliveries


4. Lambdaコンピューティングの制約

Lambdaコンピューティングには以下の制約などがあります。

  • root権限が必要なツール(yum, rpm)などが使えない。

  • docker build/runが非対応

  • 15分以上の処理実行ができない。

noteのSREチームでは、PRのAuthorの自動設定や、AWS CLIなどを実行する定期実行ワークフローなど短い処理時間で実行頻度の多いワークフローなどでLambdaコンピューティングを利用しています。

5. actions/setup-python利用時のイメージの制約

actions/setup-pythonを利用する場合は、以下のIssueに記載されているUbuntuのイメージを使用する必要があります。

I think it is an expected behaviour because Python versions 3.x was built for Ubuntu 18.04 - 22.04 that is why the action can't find the proper version.

https://github.com/actions/setup-python/issues/750#issuecomment-1774883493

なお、Amazon Linux 2とAmazon Linux 2023をサポートしているOSSのActionも公開されています。

Since this setup-python github action is not supporting amazon linux and has no foreseeable plans of doing so, i have created an open source action to support the same

https://github.com/kishaningithub/setup-python-amazon-linux

https://github.com/actions/setup-python/issues/460#issuecomment-1794412859

6. GitHub CLIのセルフインストール

CodeBuildで動かす場合にGitHub CLIを使う場合は、GitHub CLIをセルフインストールする必要があります。検証当時、arm64とx86_64の両方に対応している良さそうなカスタムアクションがなかったため、自作しました。

以上がCodeBuild-hosted GitHub Actions Runnerの設定例と検証時に得られたTipsの紹介となります。利用を検討している方の参考になると嬉しいです。

▼ noteエンジニアアドベントカレンダーはこちら

▼さらにnoteの技術記事が読みたい方はこちら

いいなと思ったら応援しよう!