Zenn
🏗️

GitHub Actions から AWS CodeBuild を Self-hosted Runner として使う-Terrafrom使用

2024/12/25に公開

AWS CodeBuild とは?

一言で言えば、AWS上のGitHub Actionsのような存在です。ユニットテストやデプロイのためのアーティファクトを生成します。リソースをAWS上で完結できるのでCodePipelineやCodeDeployと併用されることが多いです。

https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/welcome.html

なぜ CodeBuild を使用するのか?

GitHub Actions でのランナーがスペック不足

GitHub のデフォルトランナーでは、CPU 2、RAM 7GB、ストレージ 14GB という制限があります。しかし CodeBuild を利用すると、GPU 付きや ARM、CPU が 32、メモリが 244 GiB といった巨大なランナーを使用できます。

なお、使用できるランナーの種類はリージョンによって異なります。私は GPU 付きランナーを東京リージョン(ap-northeast-1)で試したところ、10 分以上待ってもビルドが進みませんでした。そこでオレゴン(us-west-2)を使用したら問題なく動作しました。

https://aws.amazon.com/jp/codebuild/pricing/

https://docs.github.com/ja/actions/using-github-hosted-runners/using-larger-runners/about-larger-runners

AWS内のリソースにアクセスしたい

例えば、RDB など、外部アクセスを制限しているリソースに対してテストを実行したい場合に、AWS 環境内からビルドを行う CodeBuild を活用するとアクセスが簡単です。

実装

CodeBuildのリソースをTerraformで構築する

1. 手動でAWSとGitHubを連携する

AWS コンソールの「デベロッパー用ツール>設定>接続」画面から「接続を作成」ボタンを押し、AWSとGitHubの連携を行います。ここはTerraformではできないので手動で対応します。

CodeBuildのTerraform

Terraformのコード
main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

## ap-northeast-1ではGPUによるCodeBuildができない(時間がかかる)ので、us-west-2でCodeBuildを行う。プッシュ先のECRはap-northeast-1にある。
locals {
  build_region = "us-west-2"
}

# 「手動でAWSとGitHubを連携する」で作成したコネクションをTerraformで管理する場合はimportする
# resource "aws_codestarconnections_connection" "github_connection" {
#   name          = ""
#   provider_type = "GitHub"
# }

resource "aws_codebuild_project" "docker_build" {
  name           = "${var.project_name}-docker-build-gpu"
  description    = "Docker build project with GPU support"
  build_timeout  = "30"
  queued_timeout = "30"
  service_role   = aws_iam_role.codebuild_service_role.arn

  artifacts {
    type = "NO_ARTIFACTS"
  }

  environment {
    compute_type                = "BUILD_GENERAL1_SMALL"
    image                       = "aws/codebuild/amazonlinux-x86_64-standard:5.0"
    type                        = "LINUX_GPU_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"
    privileged_mode             = true
  }

  source {
    type      = "GITHUB"
    location  = "https://github.com/YOUR_ORGANIZATION/REPOSITORY"
    buildspec = var.buildspec_content

    git_clone_depth = 1
    git_submodules_config {
      fetch_submodules = false
    }
  }

  logs_config {
    cloudwatch_logs {
      group_name  = "/aws/codebuild/${var.project_name}-docker-build-gpu"
      stream_name = "build-log"
      status      = "ENABLED"
    }
  }
}

resource "aws_iam_role" "codebuild_service_role" {
  name = "${var.project_name}-service-role-docker-build-gpu"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "codebuild.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy" "codebuild_policy" {
  name = "${var.project_name}-service-policy-docker-build-gpu"
  role = aws_iam_role.codebuild_service_role.name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Resource = [
          "arn:aws:logs:${local.build_region}:${var.aws_account_id}:log-group:/aws/codebuild/${var.project_name}-docker-build-gpu",
          "arn:aws:logs:${local.build_region}:${var.aws_account_id}:log-group:/aws/codebuild/${var.project_name}-docker-build-gpu:*"
        ]
        Action = [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ]
      },
      {
        Effect   = "Allow"
        Resource = "*"
        Action = [
          "ecr:BatchCheckLayerAvailability",
          "ecr:BatchGetImage",
          "ecr:CompleteLayerUpload",
          "ecr:GetAuthorizationToken",
          "ecr:GetDownloadUrlForLayer",
          "ecr:InitiateLayerUpload",
          "ecr:PutImage",
          "ecr:UploadLayerPart"
        ]
      },
      {
        Effect   = "Allow"
        Resource = ["*"]
        Action = [
          "ec2:CreateNetworkInterface",
          "ec2:DescribeDhcpOptions",
          "ec2:DescribeNetworkInterfaces",
          "ec2:DeleteNetworkInterface",
          "ec2:DescribeSubnets",
          "ec2:DescribeSecurityGroups",
          "ec2:DescribeVpcs",
          "ec2:CreateNetworkInterfacePermission"
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "codebuild:CreateReportGroup",
          "codebuild:CreateReport",
          "codebuild:UpdateReport",
          "codebuild:BatchPutTestCases",
          "codebuild:BatchPutCodeCoverages"
        ]
        Resource = [
          "arn:aws:codebuild:${local.build_region}:${var.aws_account_id}:report-group/${var.project_name}-*"
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "codeconnections:GetConnectionToken",
          "codeconnections:GetConnection"
        ]
        Resource = ["*"]
      }
    ]
  })
}
variable.tf
variable "project_name" {
  description = "Name of the CodeBuild project"
  type        = string
}

variable "aws_region" {
  description = "AWS region"
  type        = string
}

variable "aws_account_id" {
  description = "AWS account ID"
  type        = string
}

variable "buildspec_content" {
  description = "Buildspec content for CodeBuild project"
  type        = string
  default     = <<EOF
version: 0.2

phases:
  pre_build:
    commands:
      - echo "Hello, World"
EOF
}
    
output.tf

output "project_name" {
  description = "Name of the CodeBuild project"
  value       = aws_codebuild_project.docker_build.name
}

output "project_arn" {
  description = "ARN of the CodeBuild project"
  value       = aws_codebuild_project.docker_build.arn
}

output "service_role_arn" {
  description = "ARN of the CodeBuild service role"
  value       = aws_iam_role.codebuild_service_role.arn
}
    
    ```

解説

  environment {
    compute_type                = "BUILD_GENERAL1_SMALL"
    image                       = "aws/codebuild/amazonlinux-x86_64-standard:5.0"
    type                        = "LINUX_GPU_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"
    privileged_mode             = true
  }
  • compute_type: ビルドに使用するリソースの種類を選べます。CodeBuild 料金のコンピューティングタイプ に対応しています。

    有効な値: build_general1_small, build_general1_medium, build_general1_large, build_general1_2xlarge, build_lambda_1GB, build_lambda_2GB, build_lambda_4GB, build_lambda_8GB, build_lambda_10GB

    注意点として、typeLINUX_GPU_CONTAINER の場合、compute_typeBUILD_GENERAL1_LARGE でなければなりません(※ドキュメント参照)。

  • typeでは、ビルドに関連する環境を選択します。今回はGPUを使用したいのでLINUX_GPU_CONTAINERを使用します。

    有効な値: LINUX_CONTAINER, LINUX_GPU_CONTAINER, WINDOWS_CONTAINER (deprecated), WINDOWS_SERVER_2019_CONTAINER, ARM_CONTAINER, LINUX_LAMBDA_CONTAINER, ARM_LAMBDA_CONTAINER

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project
https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html

  source {
    type      = "GITHUB"
    location  = "https://github.com/YOUR_ORGANIZATION/REPOSITORY"
    buildspec = var.buildspec_content

    git_clone_depth = 1
    git_submodules_config {
      fetch_submodules = false
    }
  }

ビルドするソースコードとして GitHub を指定しています。AWS と GitHub の連携が完了していれば、location にリポジトリを指定するだけで OK です。参考にした記事にはGitHubとの連携をせずに、NO RESOURCEでもできると記述があったのですができませんでした。

buildspecには、ビルド時に実行するコマンドを記述していきます。今回はGitHub Actions側で定義をオーバーライドするので、echo "Hello, World”しか記述していません。

GitHub Actions

今回は、main ブランチに push されたら GitHub Actions が起動 → CodeBuild でビルド → ECR に Push → ECS タスク定義を更新 → ECS でデプロイ、という手順を踏む想定です。

aws-codebuild-run-build という AWS 公式の GitHub Actions を利用することで、GitHub Actions のフローから CodeBuild を実行します。

https://github.com/aws-actions/aws-codebuild-run-build

GitHub Actions
build.yaml
name: Build and Deploy

on:
  push:
    branches:
      - main
  workflow_dispatch:

permissions:
  id-token: write
  contents: read

env:
  CODEBUILD_AWS_REGION: us-west-2          # CodeBuild のリージョン。GPUありでのビルドを行うためにus-west-2を指定
  PROJECT_NAME: XXX    # CodeBuild プロジェクト名
  PROJECT_AWS_REGION: ap-northeast-1  # プロジェクトの本体があるリージョン
  ECR_REPOSITORY_URI: XXX.dkr.ecr.ap-northeast-1.amazonaws.com/XXX ## ECRレポジトリー
  ECS_CLUSTER: XXX ## ECSクラスター
  ECS_SERVICE: XXX ## ECSサービス名
  ECS_TASK_DEFINITION: XXX ## ECSタスク定義名
  CONTAINER_NAME: sbv2 ## ECSタスク定義内のコンテナ名

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image_uri: ${{ steps.get_image_arn.outputs.IMAGE_URI }}
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-region: ${{ env.CODEBUILD_AWS_REGION }}
        role-to-assume: ${{ secrets.CODEBUILD_ROLE_ARN }}
    - name: Run CodeBuild
    id: run_codebuild
    uses: aws-actions/aws-codebuild-run-build@v1
    with:
      project-name: ${{ env.PROJECT_NAME }}
      env-vars-for-codebuild : |
        ECR_REGION,
        ECR_REPOSITORY_URI
      buildspec-override: |
        version: 0.2
        phases:
          pre_build:
            commands:
              - echo Logging in to Amazon ECR...
              - aws ecr get-login-password --region $ECR_REGION | docker login --username AWS --password-stdin $ECR_REPOSITORY_URI
              - IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION
              - echo $IMAGE_TAG
          build:
            commands:
              - echo Build started on `date`
              - echo Building the Docker image...
              - docker buildx create --name mybuilder --driver docker-container --use
              - DOCKER_BUILDKIT=1 docker buildx build --builder mybuilder --build-arg BUILDKIT_INLINE_CACHE=1 
                --cache-from=type=registry,ref=$ECR_REPOSITORY_URI:cache 
                --cache-to=mode=max,image-manifest=true,oci-mediatypes=true,type=registry,ref=$ECR_REPOSITORY_URI:cache 
                -t $ECR_REPOSITORY_URI:$IMAGE_TAG .
          post_build:
            commands:
              - echo Build completed on `date`
              - export IMAGE_URI=$ECR_REPOSITORY_URI:$IMAGE_TAG
        env: 
          exported-variables:
            - IMAGE_URI
    env:
      ECR_REGION: ${{ env.PROJECT_AWS_REGION }}
      ECR_REPOSITORY_URI: ${{ needs.set-vars.outputs.ecr_repository_uri }}

  - name: Get Image ARN
    id: get_image_arn
    run: |
      IMAGE_URI=$(aws codebuild batch-get-builds \
        --ids ${{ steps.run_codebuild.outputs.aws-build-id }} \
        --query 'builds[0].exportedEnvironmentVariables[?name==`IMAGE_URI`].value' \
        --output text)
      if [ -z "$IMAGE_URI" ]; then
        echo "Failed to get IMAGE_URI from build output"
        exit 1
      fi
      echo "IMAGE_URI=$IMAGE_URI" >> $GITHUB_OUTPUT

  - name: Print Image URI
    run: |
      echo "IMAGE_URI: ${{ steps.get_image_arn.outputs.IMAGE_URI }}"

  deploy:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ${{ env.PROJECT_AWS_REGION }}
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}

      - name: Download task definition
        run: |
          aws ecs describe-task-definition --task-definition ${{ env.ECS_TASK_DEFINITION }} \
          --query taskDefinition > task-definition.json

      - name: Fill in the new image ID in the Amazon ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ needs.build.outputs.image_uri }}

      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

      - name: Output the image URL and task definition ARN
        run: |
          echo "Task Definition ARN: ${{ steps.task-def.outputs.task-definition-arn }}"

envに自分の環境に合った値をセットしてください。今回は、**CodeBuildを実行するリージョンとプロジェクトのリージョンが異なってることに注意してください。**プロジェクトのリージョン(ap-northeast-1)ではGPU付きのランナーを使用するのに待ち時間が長かったため、CodeBuildを実行するリージョンではオレゴン(us-west-2)を使用しています。

aws-actions/aws-codebuild-run-build@v1

  • project-nameで、CodeBuildを実行するビルドプロジェクト名を指定します。
  • このステップのenvにCodeBuildに渡したい環境変数を定義し、env-vars-for-codebuildでその環境を定義することで、ECRのuriを渡しています。
  • buildspec-overrideで、ビルド時に実行するコマンドを定義しています。docker buildではキャッシュを効かせたかったため、docker buildxのキャッシュ機能を使用してます。(CodeBuildはデフォルトではキャッシュが効きません。CodeBuildでキャッシュを効かせる の章で解説します)
  • env: exported-variables:で出力したい環境変数を指定することで、ECRにpushしたイメージのuriを取得しています。Get Image ARNでIMAGE_URIを取得できています。

https://dev.classmethod.jp/articles/output-build-result-to-manual-actions-review-url/l/

最後の deploy ジョブは、取得した最新イメージを ECS タスク定義に反映させ、ECS サービスを更新する流れです。

その他試したこと

CodeBuildでキャッシュを効かせる

ローカルキャッシュ

Docker を使う場合、ローカルキャッシュを有効にするとビルド時間短縮が期待できます。ただし CodeBuild のローカルキャッシュは同一ホストが割り当てられた場合のみ機能するため、安定して効かせるのは難しいです。そのため今回は採用しませんでした。

リザーブドキャパシティフリートを利用して専用インスタンスを確保することで、安定的にローカルキャッシュを効かせる手法もあります(AWS サポート公式回答)

Terraformにて
resource "aws_codebuild_project" "docker_build" {
  name           = "${var.project_name}-${var.env}-docker-bui

~~省略~~

  cache {
    type  = "LOCAL"
    modes = ["LOCAL_DOCKER_LAYER_CACHE", "LOCAL_SOURCE_CACHE", "LOCAL_CUSTOM_CACHE"]
  }
}

https://dev.classmethod.jp/articles/codebuild-local-cache/

https://qiita.com/tokino/items/2601e4b52ab167c52ba0

https://blog.serverworks.co.jp/codebuild_local_cache

s3キャッシュ

s3 にキャッシュする方法もありますが、Docker イメージのキャッシュには不向きです。設定に手間がかかるうえ、エラーが発生しやすいことから非推奨と公式にあります

https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-caching.html#caching-s3

試した結果

  • buildspecのcache:paths:/var/lib/docker/overlay2/**/*を指定→キャッシュが効かない。
  • buildspecのcache:paths:/var/lib/docker/**/*を指定→以下のようにDockerがアーティファクトを見つけられないエラーとなる。
ログ
```bash
[Container] 2024/12/23 07:45:56.519163 Running on CodeBuild On-demand
[Container] 2024/12/23 07:45:56.519377 Waiting for agent ping
[Container] 2024/12/23 07:45:57.025634 Waiting for DOWNLOAD_SOURCE
[Container] 2024/12/23 07:46:00.759951 Phase is DOWNLOAD_SOURCE
[Container] 2024/12/23 07:46:00.884292 CODEBUILD_SRC_DIR=/codebuild/output/src594681857/src/github.com/tomocode/run-one-task-ecs
[Container] 2024/12/23 07:46:00.885045 YAML location is /codebuild/readonly/buildspec.yml
[Container] 2024/12/23 07:46:00.889679 Setting HTTP client timeout to higher timeout for Github and GitHub Enterprise sources
[Container] 2024/12/23 07:46:00.889773 Processing environment variables
[Container] 2024/12/23 07:46:01.270858 No runtime version selected in buildspec.
[Container] 2024/12/23 07:46:01.443063 Moving to directory /codebuild/output/src594681857/src/github.com/tomocode/run-one-task-ecs
[Container] 2024/12/23 07:46:01.484980 Expanded cache path /var/lib/docker/**/*
[Container] 2024/12/23 07:46:01.489767 Downloading S3 cache...
[Container] 2024/12/23 07:46:05.658032 Configuring ssm agent with target id: codebuild:311d1064-7361-4a97-967f-990bf7625008
[Container] 2024/12/23 07:46:05.696325 Successfully updated ssm agent configuration
[Container] 2024/12/23 07:46:05.696698 Registering with agent
[Container] 2024/12/23 07:46:05.874531 Phases found in YAML: 3
[Container] 2024/12/23 07:46:05.874561  PRE_BUILD: 6 commands
[Container] 2024/12/23 07:46:05.874566  BUILD: 4 commands
[Container] 2024/12/23 07:46:05.874570  POST_BUILD: 4 commands
[Container] 2024/12/23 07:46:05.874859 Phase complete: DOWNLOAD_SOURCE State: SUCCEEDED
[Container] 2024/12/23 07:46:05.874872 Phase context status code:  Message: 
[Container] 2024/12/23 07:46:06.045270 Entering phase INSTALL
[Container] 2024/12/23 07:46:06.186852 Phase complete: INSTALL State: SUCCEEDED
[Container] 2024/12/23 07:46:06.186873 Phase context status code:  Message: 
[Container] 2024/12/23 07:46:06.221533 Entering phase PRE_BUILD
[Container] 2024/12/23 07:46:06.353990 Running command echo Logging in to Amazon ECR...
Logging in to Amazon ECR...

[Container] 2024/12/23 07:46:06.360920 Running command aws ecr get-login-password --region $ECR_REGION | docker login --username AWS --password-stdin $ECR_REPOSITORY_URI
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

[Container] 2024/12/23 07:46:20.774946 Running command IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION

[Container] 2024/12/23 07:46:20.782425 Running command echo $IMAGE_TAG
6ec8d819eb1d076720b91ece8ee32961c9d0e658

[Container] 2024/12/23 07:46:20.789005 Running command ls -la /var/lib/docker
total 8
drwx--x--- 13 root root  189 Dec 23 07:45 .
drwxr-xr-x  1 root root   20 Dec 23 07:45 ..
drwx--x--x  5 root root  149 Dec 23 07:46 buildkit
drwx--x--x  3 root root   20 Dec 23 07:45 containerd
drwx--x---  2 root root    6 Dec 23 07:45 containers
-rw-------  1 root root   36 Dec 23 07:45 engine-id
drwx------  3 root root   22 Dec 23 07:45 image
drwxr-x---  3 root root   19 Dec 23 07:45 network
drwx--x--- 59 root root 4096 Dec 23 07:46 overlay2
drwx------  4 root root   32 Dec 23 07:45 plugins
drwx------  2 root root    6 Dec 23 07:45 runtimes
drwx------  2 root root    6 Dec 23 07:45 swarm
drwx------  2 root root    6 Dec 23 07:45 tmp
drwx-----x  2 root root   50 Dec 23 07:45 volumes

[Container] 2024/12/23 07:46:20.797584 Running command ls -la /var/lib/docker/overlay2
total 8
drwx--x--- 59 root root   4096 Dec 23 07:46 .
drwx--x--- 13 root root    189 Dec 23 07:45 ..
drwxr-xr-x  7 root root     72 Dec 23 07:46 0123d1c2d973dcfb59184ff03f978a9d6c3ed34ad11ca2306b5bc3b39057783b
drwxr-xr-x  7 root root     72 Dec 23 07:46 09306168d60f5145e51b8156c38041a8d3d10d80ce685adf6a226363d68cc390
drwxr-xr-x  7 root root     72 Dec 23 07:46 1k9aaw3tc1fyyf4b5w1oupzmj
drwxr-xr-x  6 root root     55 Dec 23 07:46 20kbrx95kfrjh9oyww3t1vb34
drwxr-xr-x  7 root root     72 Dec 23 07:46 2821f63976f5debf6138b6c79eb463a748e5a503debccd64a2f034260d9c4ca8
drwxr-xr-x  5 root root     47 Dec 23 07:46 390d6b819b302f3aa8e7b341c485d901fdfd53e17ca75fbf7050b6a7f940b5ea
drwxr-xr-x  7 root root     72 Dec 23 07:46 4n71g5y5o941wouk1jc0heacz
drwxr-xr-x  7 root root     72 Dec 23 07:46 50ed3170d8660b534ae16d406800bad1cb57a2f73614a7b8dc9f8f1936211ec5
drwxr-xr-x  7 root root     72 Dec 23 07:46 5bf25e01344bb820ca54f49592725f829627aa40732cec2e6bfd89403df256a1
drwxr-xr-x  7 root root     72 Dec 23 07:46 5wbt3m3nnjt0ax95qy7qpc89s
drwxr-xr-x  7 root root     72 Dec 23 07:46 638b3f6ffea086fd2075aa339ea0de6752e7fc004d705e95b3deb3c14d6e6605
drwxr-xr-x  7 root root     72 Dec 23 07:46 674ffdd183c4f693fc0afd5e6a428c23ff48047242d1b95f5f2ebdfed1c2af5e
drwxr-xr-x  7 root root     72 Dec 23 07:46 6d2dca96bcf595faff16f179b6a4edc2d2bb0481a8c8d292b91f7853253a06d2
drwxr-xr-x  5 root root     47 Dec 23 07:46 6ffc50ab2918de04258a785ebbe1bd89fcb94441ddacb2fa67abe8cc95260000
drwxr-xr-x  7 root root     72 Dec 23 07:46 73f68c4bb0746f90e10b5ed438f9ef9e8f905fa1e6c6711992b4de23029144c5
drwxr-xr-x  7 root root     72 Dec 23 07:46 7480491f562bb5b035977616547dfa09468a768c0bdfe6af2c659bbf692e0b7e
drwxr-xr-x  4 root root     30 Dec 23 07:46 75c1x9tb4i7g891sxp9a8xciu
drwxr-xr-x  7 root root     72 Dec 23 07:46 77e4247204b04095e49da5452a1b370d7519a07e0f3c56fa86aca43ff1625864
drwxr-xr-x  6 root root     55 Dec 23 07:46 7l11ra0llqni5bhdpuy7g6lsh
drwxr-xr-x  5 root root     47 Dec 23 07:46 8b0a662e9973eaea0063a81abc4343d9d4c133bb8d06e7747814f86fbd4a59d3
drwxr-xr-x  7 root root     72 Dec 23 07:46 8d221ad007b535adf83a3e3e2a82659c5b3c534485d09ee479cdca26d41a939b
drwxr-xr-x  7 root root     72 Dec 23 07:46 8d6511971d0c35c7bce511cf24e38de491f7fc95a5ff239dba53fe8a62037915
drwxr-xr-x  5 root root     47 Dec 23 07:46 91f94def8ef081262e09239e89ecd53a4a80c46a16399a467d930f4dd83b3f76
drwxr-xr-x  7 root root     72 Dec 23 07:46 9b7709b8bfff4ed3d9782c2014c9a8489b7c113ed942022350b18467808de0aa
drwxr-xr-x  4 root root     30 Dec 23 07:46 ahl8x1p2sw7e9dg78tl98n4n6
brw-------  1 root root 259, 2 Dec 23 07:45 backingFsBlockDev
drwxr-xr-x  7 root root     72 Dec 23 07:46 bbgrruxz152k4wcrs2twbv1xu
drwxr-xr-x  4 root root     30 Dec 23 07:46 cte5pwyp4avvgld0ynby5096l
drwxr-xr-x  7 root root     72 Dec 23 07:46 d0e3a5d64bb99fe29e68794945d2ce75045e1b7f1105784be8c8414a79adebbd
drwxr-xr-x  6 root root     55 Dec 23 07:46 etq8ycoy2zucgd6l5s6o9gpxu
drwxr-xr-x  7 root root     72 Dec 23 07:46 f360a08434693cdd4cb15d88b0b762d5b75a0cd47664a4540d3e26bc291f4028
drwxr-xr-x  7 root root     72 Dec 23 07:46 ihs56uyzt98ou0stcnsp6c3df
drwx------ 58 root root   4096 Dec 23 07:46 l
drwxr-xr-x  7 root root     72 Dec 23 07:46 mz1o01zehsbxhd8lvh0rcqnlk
drwxr-xr-x  7 root root     72 Dec 23 07:46 nsre2hlfcrj8gfyjhkmkzikvo
drwxr-xr-x  7 root root     72 Dec 23 07:46 ny86bmlhtzhmwz8hpml3k1wxb
drwxr-xr-x  7 root root     72 Dec 23 07:46 o4h7bj53r3h2okonm0xpup6x7
drwxr-xr-x  7 root root     72 Dec 23 07:46 p9cro83hxvfjjmri7j5j5s40q
drwxr-xr-x  4 root root     30 Dec 23 07:46 pzhawj4na0f1acy6h8yuovj8s
drwxr-xr-x  7 root root     72 Dec 23 07:46 qekh4rlvykyhwanna76j9dz7l
drwxr-xr-x  6 root root     55 Dec 23 07:46 rdnm9wtznkr8i9tu2ifuohb5z
drwxr-xr-x  7 root root     72 Dec 23 07:46 rik7ap00pc0y4y7zld0auuhml
drwxr-xr-x  7 root root     72 Dec 23 07:46 sg6fs94q3l581v3jjf2f5g8ef
drwxr-xr-x  7 root root     72 Dec 23 07:46 sgvf56au51ewajadu1lfmqvp1
drwxr-xr-x  4 root root     30 Dec 23 07:46 u096pjde56xs5hmiifj8zdepe
drwxr-xr-x  4 root root     30 Dec 23 07:46 urbi46hk6vrpd1rtrog76bs3k
drwxr-xr-x  7 root root     72 Dec 23 07:46 v0fto05alyqsrjouz1h10l3tz
drwxr-xr-x  4 root root     30 Dec 23 07:46 vfwd8r4g2cd5pjl913b624di1
drwxr-xr-x  7 root root     72 Dec 23 07:46 wc9nnd6rj6ct4454dnj4i172b
drwxr-xr-x  4 root root     30 Dec 23 07:46 wiu3rbigbguui4z343p1wys4j
drwxr-xr-x  4 root root     30 Dec 23 07:46 wwa2sp3urqib9haxq0bpb29z2
drwxr-xr-x  4 root root     30 Dec 23 07:46 xae990rbkbi1m9ayg90is113a
drwxr-xr-x  7 root root     72 Dec 23 07:46 xshjevp3zlhjfldkzoaws6ku3
drwxr-xr-x  4 root root     30 Dec 23 07:46 xstzpse1wjhtlrokn2ifkrtzp
drwxr-xr-x  7 root root     72 Dec 23 07:46 yb2jskw2xx8tn2ypa0l0042gh
drwxr-xr-x  7 root root     72 Dec 23 07:46 yrszkq9ov38e7qeqaqci163s4
drwxr-xr-x  7 root root     72 Dec 23 07:46 z5eqhak7oaxgzapqxsfeeifrz
drwxr-xr-x  4 root root     30 Dec 23 07:46 zdk7zih54i7ed6sgp9lmxbk89

[Container] 2024/12/23 07:46:20.809051 Phase complete: PRE_BUILD State: SUCCEEDED
[Container] 2024/12/23 07:46:20.809067 Phase context status code:  Message: 
[Container] 2024/12/23 07:46:20.843603 Entering phase BUILD
[Container] 2024/12/23 07:46:20.844758 Running command echo Build started on `date`
Build started on Mon Dec 23 07:46:20 UTC 2024

[Container] 2024/12/23 07:46:20.853233 Running command echo Building the Docker image...
Building the Docker image...

[Container] 2024/12/23 07:46:20.859749 Running command cd app

[Container] 2024/12/23 07:46:20.865966 Running command docker build -t $ECR_REPOSITORY_URI:$IMAGE_TAG .
#0 building with "default" instance using docker driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 901B done
#1 DONE 0.0s

#2 [internal] load metadata for docker.io/library/node:22.0-bookworm-slim
#2 ERROR: read /var/lib/docker/buildkit/content/blobs/sha256/50c9001eee06b3191944e72d87e3b72fb5380c5a8f70210cb0cbe28b2f708bba: is a directory
------
 > [internal] load metadata for docker.io/library/node:22.0-bookworm-slim:
------
Dockerfile:2
--------------------
   1 |     # ベースイメージとしてnode:22.0-bookworm-slimを使用
   2 | >>> FROM node:22.0-bookworm-slim
   3 |     
   4 |     # アプリケーションのソースコードを格納するディレクトリを指定
--------------------
ERROR: failed to solve: node:22.0-bookworm-slim: failed to resolve source metadata for docker.io/library/node:22.0-bookworm-slim: read /var/lib/docker/buildkit/content/blobs/sha256/50c9001eee06b3191944e72d87e3b72fb5380c5a8f70210cb0cbe28b2f708bba: is a directory

[Container] 2024/12/23 07:46:21.806761 Command did not exit successfully docker build -t $ECR_REPOSITORY_URI:$IMAGE_TAG . exit status 1
[Container] 2024/12/23 07:46:21.816361 Phase complete: BUILD State: FAILED
[Container] 2024/12/23 07:46:21.816376 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: docker build -t $ECR_REPOSITORY_URI:$IMAGE_TAG .. Reason: exit status 1
[Container] 2024/12/23 07:46:21.859079 Entering phase POST_BUILD
[Container] 2024/12/23 07:46:21.860108 Running command echo Build completed on `date`
Build completed on Mon Dec 23 07:46:21 UTC 2024

[Container] 2024/12/23 07:46:21.868043 Running command echo Pushing the Docker image...
Pushing the Docker image...

[Container] 2024/12/23 07:46:21.875161 Running command docker push $ECR_REPOSITORY_URI:$IMAGE_TAG
The push refers to repository [701286927883.dkr.ecr.ap-northeast-1.amazonaws.com/nocall-prod-sbv2]
An image does not exist locally with the tag: 701286927883.dkr.ecr.ap-northeast-1.amazonaws.com/nocall-prod-sbv2

[Container] 2024/12/23 07:46:21.895904 Command did not exit successfully docker push $ECR_REPOSITORY_URI:$IMAGE_TAG exit status 1
[Container] 2024/12/23 07:46:21.904749 Phase complete: POST_BUILD State: FAILED
[Container] 2024/12/23 07:46:21.904765 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: docker push $ECR_REPOSITORY_URI:$IMAGE_TAG. Reason: exit status 1
[Container] 2024/12/23 07:46:21.989256 Set report auto-discover timeout to 5 seconds
[Container] 2024/12/23 07:46:21.989349 Expanding base directory path:  .
[Container] 2024/12/23 07:46:21.992722 Assembling file list
[Container] 2024/12/23 07:46:21.992736 Expanding .
[Container] 2024/12/23 07:46:21.996099 Expanding file paths for base directory .
[Container] 2024/12/23 07:46:21.996113 Assembling file list
[Container] 2024/12/23 07:46:21.996117 Expanding **/*
[Container] 2024/12/23 07:46:22.000173 No matching auto-discover report paths found
[Container] 2024/12/23 07:46:22.000188 Report auto-discover file discovery took 0.010932 seconds
[Container] 2024/12/23 07:46:22.000253 Phase complete: UPLOAD_ARTIFACTS State: SUCCEEDED
[Container] 2024/12/23 07:46:22.000265 Phase context status code:  Message: 

```

ただし、npmやpython packageをキャッシュ化するのは向いているので、それらを使用したビルドの場合はs3の使用を検討する余地があります。

https://qiita.com/suzuki-navi/items/e5c58a2889262b6148db

docker imageをあらかじめecrから取得(pull)して、それをキャッシュとして使用する

しかし、自分が試した場合はキャッシュが効きませんでした。

https://ohke.hateblo.jp/entry/2020/09/26/230000

docker buildx —cache-fromを使用

今回採用したのがこの方法です。docker buildx コマンドで ECR 上にキャッシュを保存・取得するアプローチにより、ビルド時間が 11 分 → 14 秒に短縮できました。

## buildxの環境作成
docker buildx create --name mybuilder --driver docker-container --use
DOCKER_BUILDKIT=1 docker buildx build --builder mybuilder --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from=type=registry,ref=$ECR_REPOSITORY_URI:cache --cache-to=mode=max,image-manifest=true,oci-mediatypes=true,type=registry,ref=$ECR_REPOSITORY_URI:cache -t $ECR_REPOSITORY_URI:$IMAGE_TAG --push .
  • -build-arg BUILDKIT_INLINE_CACHE=1: キャッシュメタ情報を仕込むために必須
  • -cache-from: ECR からキャッシュを取得
  • -cache-to: キャッシュを格納先に ECR を指定。image-manifest=true,oci-mediatypes=trueはECRにpushする場合、必須
  • -push: ビルド完了後にイメージを push まで実行

https://jareddesign.medium.com/my-experience-getting-docker-images-to-cache-in-aws-codebuild-using-ecr-974c5d9428ec

参考

https://zenn.dev/ohsawa0515/articles/gha-aws-codebuild-run-codebuild

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project

余談

なお、こんなに頑張ったのに、GPUを用いたビルドは結局必要なかったというオチでした。なぜかNvidia+CPU指定Pytorchの場合、GPU環境でビルドしなければならないと思い込んでおり、GPUが使用できるCodeBuild環境を選びました。結局、GPUのないUbuntu環境でも問題なかったため、GitHub Actionsでのビルドに戻りましたとさ。

Discussion

ログインするとコメントできます