codebuild構築とdeployフローの整備

June 27, 2023

はじめに

BitBucketPipelines で image の push を行っていましたが、cpu がマルチプラットフォームに対応しておらず、x86_64 のみしか対応していないため、イメージの push を CodeBuild で行うことにしました。 arm64 で ECS タスクを実行するとコストが 20%落ち、使い方によってはパフォーマンスが向上するらしい。

  • 今まで
    • [BitBucketPipelines]  →  [awsECR]
  • 変更後
    • [BitBucketPipelines]  →  [awsCodeBuild]  →  [awsECR]

結果

コストが 20%ぐらい落ちた。パフォーマンスはまだサービスインしてないため分からない

やること

  • CodeBuild の設定
    • 構成
      • /image/以下の Dockerfile を build して ECR に push する
      • /image/以下に buildspec.yml を配置
  • Bitbucket から CodeBuild を実行するための script 用意
  • bitbuket-pipelines.yml の修正

CodeBuild の設定

data "aws_region" "this" {}

resource "aws_codebuild_project" "ecr" {
  badge_enabled          = false
  build_timeout          = 20 // 20
  concurrent_build_limit = 1  // 並列実行数
  encryption_key         = "arn:aws:kms:${data.aws_region.this.name}:${data.aws_caller_identity.self.account_id}:alias/aws/s3"
  name                   = "ecr-build-${terraform.workspace}"
  project_visibility     = "PRIVATE"
  queued_timeout         = 480
  service_role           = aws_iam_role.codebuild_role.arn
  tags = {
    Environment  = "${terraform.workspace}"
    Name = "ecr-build-${terraform.workspace}"
  }

  environment {
    compute_type                = "BUILD_GENERAL1_SMALL" // 1vCPU, 3.75GB
    image                       = "aws/codebuild/amazonlinux2-aarch64-standard:3.0"
    type                        = "ARM_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"
    privileged_mode             = true // dockerコマンドを叩く場合は必須
  }

  artifacts {
    type = "NO_ARTIFACTS"
  }

  source {
    location            = "bucketname/buildspec/"
    buildspec           = "codebuild/${terraform.workspace}/buildspec.yml"
    report_build_status = false
    type                = "S3"
  }
}

#------------------------------------------------------------#
# code build role
#------------------------------------------------------------#
data "aws_iam_policy_document" "codebuild_role" {
  statement {
    sid     = "1"
    effect  = "Allow"
    actions = ["sts:AssumeRole"]

    principals {
      type = "Service"

      identifiers = [
        "codebuild.amazonaws.com"
      ]
    }
  }
}
resource "aws_iam_role" "codebuild_role" {
  name               = "codebuild-ecr-build-${terraform.workspace}"
  assume_role_policy = data.aws_iam_policy_document.codebuild_role.json
}

#------------------------------------------------------------#
# code build atached policy s3 access
#------------------------------------------------------------#
resource "aws_iam_policy" "codebuild_s3_access_policy" {
  name        = "codebuild-s3-access-policy-${terraform.workspace}"
  description = "codebuild_s3_access_policy"
  policy      = <<EOT
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::bucketname/buildspec/",
                "arn:aws:s3:::bucketname/buildspec/*"
            ]
        },
        {
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::bucketname"
            ],
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketAcl",
                "s3:GetBucketLocation"
            ]
        }
    ]
}
EOT
}

resource "aws_iam_role_policy_attachment" "codebuild_s3_access_policy" {
  role       = aws_iam_role.codebuild_role.name
  policy_arn = aws_iam_policy.codebuild_s3_access_policy.arn
}

#------------------------------------------------------------#
# code build atached ecr push policy
#------------------------------------------------------------#
resource "aws_iam_policy" "codebuild_ecr_push_policy" {
  name        = "codebuild-ecr-push-policy-${terraform.workspace}"
  description = "codebuild_ecr_push_policy"
  policy      = <<EOT
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ecr",
            "Effect": "Allow",
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:CompleteLayerUpload",
                "ecr:GetAuthorizationToken",
                "ecr:InitiateLayerUpload",
                "ecr:PutImage",
                "ecr:UploadLayerPart"
            ],
            "Resource": "*"
        }
    ]
}
EOT
}

resource "aws_iam_role_policy_attachment" "codebuild_ecr_push_policy" {
  role       = aws_iam_role.codebuild_role.name
  policy_arn = aws_iam_policy.codebuild_ecr_push_policy.arn
}

buildspec yal の配置

BitBucket から codebuild を実行する用のスクリプト

#!/bin/bash
env=$1

# codebuildの実行結果を取得する
build_output=$(aws codebuild start-build --project-name nnslink-ecr-build-${env})
build_id=$(echo "$build_output" | jq -r '.build.id')

count=0
while : ; do
    # codebuildの状態を取得する
    build_status=$(aws codebuild batch-get-builds --ids $build_id --query 'builds[0].[buildStatus]' --output text)
    if [ "$build_status" = "SUCCEEDED" ]; then
        echo "done!"
        break
    elif [ "$build_status" = "FAILED" ]; then
        exit 1
    fi
    # 10分以上かかったら失敗扱いで終了
    count=$((count+1))
    if [ $count -gt 20 ]; then
        echo "[timeout] check aws codebuild console for more detail."
        exit 1
    fi
    echo "prosessing..."
    sleep 30
done

exit 0

BitbucketPipeline の設定

definitions:
    steps:
        - step: &ECR-push
              name: ECR push
              image: atlassian/pipelines-awscli:latest
              trigger: manual
              oidc: true
              script:
                  - export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
                  - echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
                  - aws s3 sync ${BITBUCKET_CLONE_DIR}/image/ s3://bucketname/buildspec/ --delete
                  - /bin/bash ./scripts/codebuild.sh ${ENV}
pipelines:
    custom:
        image-update:
            - step:
                  <<: *ECR-push
                  deployment: Staging
            - step:
                  <<: *ECR-push
                  deployment: Production
Nifty tech tag lists from Wouter Beeftink