CodeBuildを使ってECRにpushして本番作業を減らしたい

# 背景
- 現在の本番環境はECSを使っている。
- リリース毎に手動でDockerのビルドからECRにpushを行い、タスクを作成してデプロイを行っている。
- 手動作業による人的ミス、本番作業の精神的負荷を下げたい。
- AWSのSAAの勉強中CodeBuildサービスなるものを発見。
- まずはCodeBuildを使ってDockerのイメージ生成からECRへのpushをワンコマンドで行いたい。

# コード解説
- 下記のリンクのDockerファイルから生成したDockerイメージをECRで管理します。
    - https://github.com/akapo001/terraform-ecr-ecs/blob/main/Dockerfile
- ECR
  - ここは単純にECRの名前を設定しているだけなので解説は省きます。

```terraform
resource "aws_ecr_repository" "nginx" {
  name                 = "my-nginx"
  image_tag_mutability = "MUTABLE"
  image_scanning_configuration {
    scan_on_push = true
  }
}

output "ecr_url" {
  value = aws_ecr_repository.nginx.repository_url
}
```

- IAMポリシー
  - CodeBuild用のIAMポリシーの設定
  - CodeBuild関連の権限に加えて、ECRへのpushのための権限とCloudWatchにログを流すための権限を付与
  - 実際の本番環境は資材をS3で管理しているのでそちらの権限も追加している
 
```terraform
data "aws_iam_policy_document" "assume_role_codebuild" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["codebuild.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "codebuild_execution_role" {
  name               = "MyCodeBuildRole"
  assume_role_policy = data.aws_iam_policy_document.assume_role_codebuild.json
}

resource "aws_iam_role_policy_attachment" "amazon_ec2_container_registry_full_access" {
  role       = aws_iam_role.codebuild_execution_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess"
}

resource "aws_iam_role_policy_attachment" "amazon_ec2_container_registry_power_user" {
  role       = aws_iam_role.codebuild_execution_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser"
}

resource "aws_iam_policy" "codebuild_base_policy" {
  name = "CodeBuildBasePolicy-my_nginx"
  policy = jsonencode({
    Version: "2012-10-17",
    Statement: [
      {
        Effect: "Allow",
        Resource: [
          "arn:aws:logs:ap-northeast-1:${var.aws_account_id}:log-group:/aws/codebuild/my_nginx",
          "arn:aws:logs:ap-northeast-1:${var.aws_account_id}:log-group:/aws/codebuild/my_nginx:*"
        ],
        Action: [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ]
      },
      {
        Effect: "Allow",
        Resource: [
          "arn:aws:s3:::codepipeline-ap-northeast-1-*"
        ],
        Action: [
          "s3:PutObject",
          "s3:GetObject",
          "s3:GetObjectVersion",
          "s3:GetBucketAcl",
          "s3:GetBucketLocation"
        ]
      },
      {
        Effect: "Allow",
        Action: [
          "codebuild:CreateReportGroup",
          "codebuild:CreateReport",
          "codebuild:UpdateReport",
          "codebuild:BatchPutTestCases",
          "codebuild:BatchPutCodeCoverages"
        ],
        Resource: [
          "arn:aws:codebuild:ap-northeast-1:${var.aws_account_id}:report-group/my_nginx-*"
        ]
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "codebuild_policy" {
  role       = aws_iam_role.codebuild_execution_role.name

  policy_arn = aws_iam_policy.codebuild_base_policy.arn
}
```
- CodeBuild
  - **ビルドする環境の設定**: 今回はFargateのECSを想定しているのでx86アーキテクチャを指定。
  - **ビルドするソースの指定**: 下記ではGitHubを指定しているがS3等も指定可能。
  - **ビルドのコマンドの設定**:　https://github.com/akapo001/terraform-ecr-ecs/blob/main/buildspec.yml  
※特に指定がない場合、sourceセクションで指定したファイルの場所から`buildspec.yml`を自動で見つけてくれる。(任意のファイルを指定することも可能)
    
```terraform
resource "aws_codebuild_project" "my_nginx" {
  name = "my_nginx"
  service_role = aws_iam_role.codebuild_execution_role.arn
  artifacts {
    type = "NO_ARTIFACTS"
  }
  environment {
    compute_type = "BUILD_GENERAL1_SMALL"
    image = "aws/codebuild/amazonlinux2-x86_64-standard:2.0"
    type = "LINUX_CONTAINER"
    privileged_mode = true

    environment_variable {
      name = "AWS_ACCOUNT_ID"
      value = var.aws_account_id
      type = "PLAINTEXT"
    }
    environment_variable {
      name = "AWS_DEFAULT_REGION"
      value = "ap-northeast-1"
      type = "PLAINTEXT"
    }
    environment_variable {
      name = "IMAGE_REPO_NAME"
      value = "my-nginx"
      type = "PLAINTEXT"
    }
    environment_variable {
      name = "IMAGE_TAG"
      value = "latest"
      type = "PLAINTEXT"
    }
  }
  source {
    type = "GITHUB"
    location = "https://github.com/akapo001/terraform-ecr-ecs.git"
    git_clone_depth = 1
    git_submodules_config {
      fetch_submodules = true
    }
  }
  source_version = "main"
}
```

## ビルド
- `terraform.tfvars`ファイルにシークレット情報を書き込んだ後`terraform apply`を実行する。
- 下記画面より、「ビルドを開始」を押すだけで`buildspec.yml`に書かれたコマンドを実行し、最終的にECRにイメージがpushされる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226241/c4869909-e195-7a01-4260-dfed7c8b25e7.png)

### 完了画面
- ECSのコンソールにてプッシュされていることが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226241/f6ccaf6c-5b73-df41-39c3-b97465d47d3d.png)