AWSコンテナ設計・構築[本格]入門を Terraform / CodeSeries で構築した

# Zennに書いてます！
詳細はZennを見てください！

https://zenn.dev/wakaka23/articles/aa7a35cb12829a


## 概要
以前に「[Terraform : ECS on Fargate + CI/CD (GitHub Actions)](https://zenn.dev/wakaka23/articles/d6867737436c8e)」という記事において、ECSコンテナ環境をTerraformで構築し、GitHub Actionsを用いてCI/CDを実装しました。本記事では同様のCI/CD処理をAWS CodeSeries(CodePipeline, CodeBuild, CodeDeploy)を使用して実現します。

## 構成図
![](https://storage.googleapis.com/zenn-user-upload/80b8d4f59ff7-20250314.png)

## 初期構成 実装
Terraformコードは[GitHub](https://github.com/wakaka23/AWS_Container_App_CodeSeries_HCL)に記載しています。本記事では、**CodeSeriesを用いたCI/CDに焦点を当てて解説します**ので、CI/CD以外のAWS初期構成に関しては以下の記事をご確認ください。

https://zenn.dev/wakaka23/articles/d6867737436c8e

## CI/CD 実装
AWSのCode Seriesを使用してCI/CDを実装していきます。今回の構成におけるCI/CD処理の全体像、各サービス間の大まかな流れは以下の図に示したようになっています。次項からそれぞれの処理の詳細を確認していきます。
![](https://storage.googleapis.com/zenn-user-upload/e0fc7e2f0857-20250319.png)

```:ディレクトリ構成（CI/CD部分 抜粋）
├─ environments
│  ├─ prd
│  ├─ stg
│  └─ dev
│     ├─ main.tf
│     ├─ local.tf
│     ├─ variable.tf
│     └─ (terraform.tfvars)
│
├─ modules(各Moduleにmain.tf,variable.tf,output.tfが存在)
│  └─ ecs_backend(Backend ECS関連、CI/CD関連のリソース）
│
├─ backend_app（CI/CD対象となるバックエンドアプリ）
├─ buildspec.yml（CodeBuildによるビルド処理内容を記載）
├─ taskdef.json（ECSタスク定義）
└─ appspec.yml（CodeDeployによるデプロイ処理内容を記載）
```

### 0. GitHubとAWSサービスを統合
![](https://storage.googleapis.com/zenn-user-upload/99fa75dd1e39-20250319.png)

CodePipelineのSourceステージとしてGitHubを利用する大前提として、**CodeConnectionsを使用して両者を統合させる**必要があります。今回の構成では、Connectionは手動で作成し、以下のようにTerraform上にてData Sourceで参照するような形を取っています。Terraform管理下に置かないことで、`terraform destroy`の際などに再度作成する必要がないようにしています。

```hcl:modules/ecs_backend/main.tf（CodeConnectionsリソース 抜粋）
# Define CodeConnection for GitHub Repository
data "aws_codestarconnections_connection" "github" {
  arn = var.code_pipeline.code_connection_arn
}
```

そして、CodePipelineのSourceステージにおいて、GitHubと統合したConnection, 対象リポジトリを指定することで、GitHubリポジトリをCodePipelineのソースプロバイダとして認識することができるようになります。

```hcl:modules/ecs_backend/main.tf（CodePipelineリソース 抜粋）
# Define CodePipeline
resource "aws_codepipeline" "backend" {
  name           = "${var.common.env}-backend-codepipeline"
  role_arn       = aws_iam_role.codepipeline.arn
  pipeline_type  = "V2"
  execution_mode = "QUEUED"
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
  stage {
    name = "Source"

    action {
      name             = "Source"
      category         = "Source"
      owner            = "AWS"
      provider         = "CodeStarSourceConnection"
      version          = "1"
      output_artifacts = ["SourceArtifact"]

      configuration = {
        ConnectionArn    = data.aws_codestarconnections_connection.github.arn
        FullRepositoryId = "${var.code_pipeline.github_repository_owner}/${var.code_pipeline.github_repository_name}"
        BranchName       = var.code_pipeline.github_repository_branch_name
      }
    }
  }
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
}
```

### 1. 特定ブランチへPush
### 2. Pushを契機にCodePipeline起動
![](https://storage.googleapis.com/zenn-user-upload/432f8df1e6ba-20250319.png)

CodePipelineではGitHub Actionsと同様に、CI/CD発動の契機を定義することができます（[参考](https://dev.classmethod.jp/articles/codepipelinev2-file-path-trigger/)）。今回の構成では「mainブランチに対して、backend_app/handler/配下のgoファイルをPush」したことを契機にCodePipelineを起動するように設計しています。

```hcl:modules/ecs_backend/main.tf（CodePipelineリソース 抜粋）
# Define CodePipeline
resource "aws_codepipeline" "backend" {
  name           = "${var.common.env}-backend-codepipeline"
  role_arn       = aws_iam_role.codepipeline.arn
  pipeline_type  = "V2"
  execution_mode = "QUEUED"
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
  trigger {
    provider_type = "CodeStarSourceConnection"
    git_configuration {
      source_action_name = "Source"
      push {
        branches {
          includes = [var.code_pipeline.github_repository_branch_name]
        }
        file_paths {
          includes = ["backend_app/handler/*.go"]
        }
      }
    }
  }
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
}
```

以下の表の「GitHub (via GitHub App)」に記載のように、内部的には**CodeConnectionsがGitHubリポジトリに対するPushを検知してCodePipelineを起動するイベント駆動検知**となっているようです（[参考](https://docs.aws.amazon.com/codepipeline/latest/userguide/update-change-detection.html#update-change-detection-overview)）。CodeCommitでイベント駆動検知を実現するためにはEventBridgeを用いる必要があるため、余計なリソースが増えるだけでなく、クロスアカウント構成を組むのが少々面倒でした。この点、CodeConnectionsはRAMによるクロスアカウント共有に対応しているため、比較的容易にアカウント間のCI/CDを実装することができます（[参考](https://dev.classmethod.jp/articles/aws-codeconnections-sharing-between-accounts-ram/)）。これらの観点はCodeConnectionsを使用する明確な利点となりそうですね。

![](https://storage.googleapis.com/zenn-user-upload/4870b554284c-20250316.png)

> For a CodeStarSourceConnection source action, you do not have to set up a webhook or default to polling. The connections action manages your source change detection for you. ([参考](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodestarConnectionSource.html#action-reference-CodestarConnectionSource-auth))

### 3. SourceArtifactにGitHubのコードを複製
![](https://storage.googleapis.com/zenn-user-upload/537873a2aa2c-20250319.png)

リポジトリへのPushを契機にCodePipelineが起動すると、初めにアーティファクト用S3バケットにGitHubのコードを複製します。`output_artifacts`で定義したSourceArtifactというディレクトリにこれらのコードは格納されます。実際には、アーティファクト名がSourceArtiのように切り詰められてしまいますが、これはCodepipelineのポリシーサイズにおける制約であり、パイプラインは正常に動作するとのことです（[参考](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/troubleshooting.html#troubleshooting-truncated-artifacts)）。

```hcl:modules/ecs_backend/main.tf（CodePipelineリソース 抜粋）
# Define CodePipeline
resource "aws_codepipeline" "backend" {
  name           = "${var.common.env}-backend-codepipeline"
  role_arn       = aws_iam_role.codepipeline.arn
  pipeline_type  = "V2"
  execution_mode = "QUEUED"

  artifact_store {
    location = aws_s3_bucket.codepipeline.bucket
    type     = "S3"
  }
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
  stage {
    name = "Source"

    action {
      name             = "Source"
      category         = "Source"
      owner            = "AWS"
      provider         = "CodeStarSourceConnection"
      version          = "1"
      output_artifacts = ["SourceArtifact"]
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
}
```

### 4. CodePipelineよりCodeBuildが呼び出されビルド実行環境起動
### 5. ビルド実行環境にコードを複製
![](https://storage.googleapis.com/zenn-user-upload/4015b6325544-20250319.png)

Buildステージに遷移すると、CodePipelineによりCodeBuild Projectが呼び出されます。CodeBuild Projectは`image`で定義されたイメージから作成されたコンテナ上に構築されるビルド実行環境です。CodeBuild Projectが起動すると、最初にアーティファクト用S3バケットからコンテナ上にSourceArtifactを複製します。ここで注意すべき点として、**CodeBuild Projectの`source`で定義された値よりも、CodePipeline Buildステージの`input_artifacts`で定義された値が優先される**ことが挙げられます。つまり、CodeBuild Projectの`source`でGitHubリポジトリを指定したとしても、Buildステージの`input_artifacts`でS3バケットを指定していた場合には、S3バケットに格納されているコードが複製されます。

> The artifact configured in your CodeBuild project becomes the input artifact used by the CodeBuild action in your pipeline.（[参考](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodeBuild.html#action-reference-CodeBuild-input)）

```hcl:modules/ecs_backend/main.tf（CodeBuildリソース 抜粋）
# Define CodeBuild project
resource "aws_codebuild_project" "backend" {
  name         = "${var.common.env}-backend-codebuild"
  service_role = aws_iam_role.codebuild.arn

  source {
    type     = "GITHUB"
    location = "https://github.com/${var.code_pipeline.github_repository_owner}/${var.code_pipeline.github_repository_name}"
  }

  artifacts {
    type     = "S3"
    location = aws_s3_bucket.codepipeline.bucket
  }

  environment {
    compute_type = "BUILD_GENERAL1_SMALL"
    image        = "aws/codebuild/amazonlinux-x86_64-standard:5.0"
    type         = "LINUX_CONTAINER"
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
  }
}
```

```hcl:modules/ecs_backend/main.tf（CodePipelineリソース 抜粋）
# Define CodePipeline
resource "aws_codepipeline" "backend" {
  name           = "${var.common.env}-backend-codepipeline"
  role_arn       = aws_iam_role.codepipeline.arn
  pipeline_type  = "V2"
  execution_mode = "QUEUED"
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
  stage {
    name = "Build"

    action {
      name             = "Build"
      category         = "Build"
      owner            = "AWS"
      provider         = "CodeBuild"
      version          = "1"
      input_artifacts  = ["SourceArtifact"]
      output_artifacts = ["BuildArtifact"]

      configuration = {
        ProjectName = aws_codebuild_project.backend.name
      }
    }
  }
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
}
```

### 6. イメージのビルド→ECRにPush
![](https://storage.googleapis.com/zenn-user-upload/8786965f224c-20250319.png)

CodeBuild Projectはbuildspec.ymlに従って以下のような処理を実行します。

**pre_build**
- **ECRにログイン**
ECRはDockerの標準的なAPIを実装しているため、DockerクライアントがECRプライベートレジストリにアクセスするためには、他のDockerプライベートレジストリと同様に認証が必要です。`aws ecr get-login-password`により取得した一時的な認証情報でログインします。
- **Docker Hubにログイン**
Docker HubからのコンテナイメージのPull回数に設定されているレート制限に抵触しないためにユーザ認証をする必要があります。これによって「200回/6時間」というレート制限を占有することができるようになります（[参考](https://dev.classmethod.jp/articles/codebuild-has-to-use-dockerhub-login-to-avoid-ip-gacha/)）。
- **コミットハッシュを取得**
今回の構成では、ビルドするコンテナイメージに付与するタグとしてコミットハッシュを採用しています。CodeBuildのビルド実行環境では様々な環境変数が事前に定義されており、`CODEBUILD_RESOLVED_SOURCE_VERSION`によりコミットハッシュを取得することが可能です。最終的には、パイプ処理により得た短縮形ハッシュをタグに使用しています（[参考](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-env-ref-env-vars.html)）。

**build**
- **コンテナイメージのビルド**
アーティファクト用S3バケットから取得したコードに格納されたDockerfileに従い、コンテナイメージをビルドします。注意点ですが、ECRにイメージをPushするためにはイメージ名を`${REPOSITORY_URI}:${IMAGE_TAG}`という形式にする必要があります（[参考](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/docker-push-ecr-image.html)）。

**post_build**
- **イメージをECRにPush**
  `${REPOSITORY_URI}:${IMAGE_TAG}`という形式にしたイメージをECRにPushします。

```yml:buildspec.yml（ビルド部分 抜粋）
phases:
  pre_build:
    commands:
      - echo "Login to Amazon ECR"
      - aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com
      - echo "Login to Docker Hub"
      - docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD}
      - echo "Get commit hash"
      - IMAGE_TAG=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | cut -c 1-7)
  build:
    commands:
      - echo "Build Docker image"
      - docker build -f ./backend_app/Dockerfile -t ${REPOSITORY_URI}:${IMAGE_TAG} ./backend_app
  post_build:
    commands:
      - echo "Push Docker image to Amazon ECR"
      - docker push "${REPOSITORY_URI}:${IMAGE_TAG}"
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
```

### 7. イメージURLを格納 + taskdef更新
![](https://storage.googleapis.com/zenn-user-upload/c71e74b176d9-20250319.png)

CodeBuildではコンテナイメージをビルド・Pushするだけでなく、以下のようにしてCodeDeployで使用するビルドアーティファクトを準備します。
- **イメージURLを格納**
まずはビルド・PushしたコンテナイメージのURLをimageDetail.jsonに格納します。詳細はDeployステージの方で説明しますが、CodeDeployがimageDetail.jsonに格納されたイメージURLを自動でtaskdef.jsonに書き込んで、タスク定義を完成させてくれます。
- **taskdef.jsonを更新**
以下に示すように、taskdef.jsonには様々な変数を定義しています。`aws_codebuild_project`の`environment_variable`において各変数に対応する環境変数をビルド実行環境に作成し、sedコマンドで置換することで、teskdef.json内の変数に値を格納しています。
- **アーティファクト格納**
ビルドアーティファクトとして、imageDetail.json, appspec.yml, taskdef.jsonをアーティファクト用S3バケットのBuildArtifactに格納します。

```yml:buildspec.yml（ビルド後作業部分 抜粋）
phases:
  pre_build:
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
  build:
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
  post_build:
    commands:
      - echo "Push Docker image to Amazon ECR"
      - docker push "${REPOSITORY_URI}:${IMAGE_TAG}"
      - printf '{"Version":"1.0","ImageURI":"%s"}' ${REPOSITORY_URI}:${IMAGE_TAG} > imageDetail.json
      - echo "Create new revision of the task definition"
      - sed -ie "s#<TASK_FAMILY>#${TASK_FAMILY}#" taskdef.json
      - sed -ie "s#<TASK_EXECUTION_ROLE_ARN>#${TASK_EXECUTION_ROLE_ARN}#" taskdef.json
      - sed -ie "s#<SECRETS_FOR_DB_ARN>#${SECRETS_FOR_DB_ARN}#" taskdef.json
      - sed -ie "s#<REGION>#${REGION}#" taskdef.json
      - sed -ie "s#<LOG_GROUP_NAME>#${LOG_GROUP_NAME}#" taskdef.json
      - echo "$(cat taskdef.json)"
artifacts:
  files:
    - imageDetail.json
    - taskdef.json
    - appspec.yml
```

```json:taskdef.json
{
  "family": "<TASK_FAMILY>",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "networkMode": "awsvpc",
  "executionRoleArn": "<TASK_EXECUTION_ROLE_ARN>",
  "containerDefinitions": [
    {
      "name": "app",
      "image": "<IMAGE1_NAME>",
      "cpu": 256,
      "memory": 512,
      "essential": true,
      "secrets": [
        {
          "name": "DB_HOST",
          "valueFrom": "<SECRETS_FOR_DB_ARN>:host::"
        },
        {
          "name": "DB_NAME",
          "valueFrom": "<SECRETS_FOR_DB_ARN>:dbname::"
        },
        {
          "name": "DB_USERNAME",
          "valueFrom": "<SECRETS_FOR_DB_ARN>:username::"
        },
        {
          "name": "DB_PASSWORD",
          "valueFrom": "<SECRETS_FOR_DB_ARN>:password::"
        }
      ],
      "portMappings": [{"containerPort": 80}],
      "readonlyRootFilesystem": true,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-region": "<REGION>",
          "awslogs-group": "<LOG_GROUP_NAME>",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}
```

```hcl:modules/ecs_backend/main.tf（CodeBuildリソース 抜粋）
# Define CodeBuild project
resource "aws_codebuild_project" "backend" {
  name         = "${var.common.env}-backend-codebuild"
  service_role = aws_iam_role.codebuild.arn
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
  environment {
    compute_type = "BUILD_GENERAL1_SMALL"
    image        = "aws/codebuild/amazonlinux-x86_64-standard:5.0"
    type         = "LINUX_CONTAINER"
    environment_variable {
      name  = "REGION"
      value = var.common.region
    }
    environment_variable {
      name  = "AWS_ACCOUNT_ID"
      value = var.common.account_id
    }
    environment_variable {
      name  = "DOCKER_USERNAME"
      value = var.code_pipeline.docker_username
    }
    environment_variable {
      name  = "DOCKER_PASSWORD"
      value = var.code_pipeline.docker_password
    }
    environment_variable {
      name  = "REPOSITORY_URI"
      value = var.ecr_repository.backend_repository_uri
    }
    environment_variable {
      name  = "TASK_FAMILY"
      value = "${var.common.env}-backend-def"
    }
    environment_variable {
      name  = "TASK_EXECUTION_ROLE_ARN"
      value = aws_iam_role.task_execution_role.arn
    }
    environment_variable {
      name  = "SECRETS_FOR_DB_ARN"
      value = var.secrets_manager.secret_for_db_arn
    }
    environment_variable {
      name  = "LOG_GROUP_NAME"
      value = aws_cloudwatch_log_group.backend.name
    }
  }
}
```

### 8. CodePipelineよりCodeDeployが呼び出される
### 9. BuildArtifactよりファイル取得
### 10. appspecを更新→タスク更新
![](https://storage.googleapis.com/zenn-user-upload/1b2443bb5179-20250319.png)

Deployステージに遷移すると、CodePipelineによりCodeDeployが呼び出されます。CodeDeployでは以下のパラメータにより指定した格納場所から各ファイルを取得します（[参考](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECSbluegreen.html#action-reference-ECSbluegreen-config)）。今回の構成では、格納場所としてBuildArtifactを指定し、ビルドアーティファクトを取り込んでいます。
- `TaskDefinitionTemplateArtifact`：taskdef.jsonの格納場所
- `AppSpecTemplateArtifact`：appspec.jsonの格納場所
- `Image1ArtifactName`：taskdef.jsonの<IMAGE1_NAME>に格納するイメージURLを記載しているimageDetail.jsonの格納場所

```hcl:modules/ecs_backend/main.tf（CodePipelineリソース 抜粋）
# Define CodePipeline
resource "aws_codepipeline" "backend" {
  name           = "${var.common.env}-backend-codepipeline"
  role_arn       = aws_iam_role.codepipeline.arn
  pipeline_type  = "V2"
  execution_mode = "QUEUED"
~~~~~~~~~~~~~~~~~~~~~~~ 省略 ~~~~~~~~~~~~~~~~~~~~~~~
  stage {
    name = "Deploy"

    action {
      name            = "Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "CodeDeployToECS"
      version         = "1"
      input_artifacts = ["BuildArtifact"]

      configuration = {
        ApplicationName                = aws_codedeploy_app.backend.name
        DeploymentGroupName            = aws_codedeploy_deployment_group.backend.deployment_group_name
        TaskDefinitionTemplateArtifact = "BuildArtifact"
        AppSpecTemplateArtifact        = "BuildArtifact"
        Image1ArtifactName             = "BuildArtifact"
        Image1ContainerName            = "IMAGE1_NAME"
      }
    }
  }
}
```

取得したビルドアーティファクトを用いてCodeDeployがappspec.ymlを更新します。
まず、CodeDeployは`Image1ArtifactName`として定義したアーティファクト内のimageDetail.jsonに記載されたイメージURLをtaskdef.json内の<IMAGE1_NAME>に代入します。この段階で、taskdef.jsonに定義されていた全ての変数は実際の値で置換されている状態となります。
> the CodeDeployToECS action replaces the placeholder <IMAGE1_NAME> into actual image URI retrieved from imageDetail.json in the artifact which you specify as Image1ArtifactName.（[参考](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECSbluegreen.html#action-reference-ECSbluegreen-input)）

そして、CodeDeployがappspec.yml内の<TASK_DEFINITION>を更新後のタスク定義で置換することで、最終的にタスク更新に利用されるappspec.ymlの作成が完了し、デプロイが開始されます。
> TaskDefinition: <TASK_DEFINITION>
This property will be updated by the CodeDeployToECS action after the new task definition is created.For the value of the TaskDefinition field, the placeholder text must be <TASK_DEFINITION>. The CodeDeployToECS action replaces this placeholder with the actual ARN of the dynamically generated task definition.（[参考](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECSbluegreen.html#action-reference-ECSbluegreen-input)）

![](https://storage.googleapis.com/zenn-user-upload/680d6a5639c3-20250318.png)

## 動作確認

### 初期構築
冒頭でも述べたように、本記事ではCodeSeriesを用いたCI/CDに焦点を当てて解説しています。初期構築時の設定値は基本的に以下の記事と同等なので、こちらをご確認いただければと思います。
https://zenn.dev/wakaka23/articles/d6867737436c8e

### CI/CDによるB/Gデプロイメント実行
初期構築時点でフロントエンドアプリケーションのトップページにアクセスすると、バックエンドアプリケーションからAPIレスポンスとして取得した「Hello world」が出力されます。
![](https://storage.googleapis.com/zenn-user-upload/2944d210ae29-20250320.png)

```go:backend_app/handler/helloworld_handler.go
package handlers

import (
	"net/http"

	"github.com/labstack/echo/v4"
	"github.com/uma-arai/sbcntr-backend/domain/model"
)

// HelloWorldHandler ...
type HelloWorldHandler struct {
}

// NewHelloWorldHandler ...
func NewHelloWorldHandler() *HelloWorldHandler {
	return &HelloWorldHandler{}
}

// SayHelloWorld ...
func (handler *HelloWorldHandler) SayHelloWorld() echo.HandlerFunc {
	body := &model.Hello{
		Data: "Hello world",
	}
	return func(c echo.Context) error {
		return c.JSON(http.StatusOK, body)
	}
}
```

上記のhelloworld_handler.goにおいて、`Hello world`という部分を`Hello world! CI/CD succeeded!`に変更し、Commit/Pushします。CI/CD処理が実行され、トップページにおいても「Hello world! CI/CD succeeded!」という文字列が出力されることを確認します。

**【S3にソースアーティファクト格納】**
helloworld_handler.goを編集/Commit/Pushをすると、CI/CD処理が実行されます。アーティファクト用S3バケットにはSourceArtifactというディレクトリが作成され、ソースアーティファクトが格納されていることを確認できます。
![](https://storage.googleapis.com/zenn-user-upload/32020c517fb1-20250320.png)

**【ECSタスク定義更新】**
CodeBuildがソースアーティファクト内のDockerfileによりコンテナイメージをビルドします。ECRにはコミットハッシュをタグとして持つ新たなBackend用コンテナイメージ（cntr-codeseries-backend:a246f2f）が作成され、このコンテナイメージを使用した新たなECSタスク定義も作成されます。
![](https://storage.googleapis.com/zenn-user-upload/e520744a6b85-20250320.png)
![](https://storage.googleapis.com/zenn-user-upload/09deee0ef497-20250320.png)

**【S3にビルドアーティファクト格納】**
CodeBuildはECRにコンテナイメージをPushした後に、一部ファイルを更新し、ビルドアーティファクトを作成します。アーティファクト用S3バケットにはBuildArtifactというディレクトリが作成され、ビルドアーティファクトが格納されていることを確認できます。
![](https://storage.googleapis.com/zenn-user-upload/490d9d88d81c-20250320.png)

**【CodeDeployによるB/Gデプロイ】**
以前の記事でも解説しているので詳細は割愛しますが、CodeDeployはビルドアーティファクトをもとに、「テストトラフィック設定→本稼働トラフィック再ルーティング→元のタスクセット終了」という過程を経て、B/Gデプロイメントを完了します。CodePipelineにおいても、すべてのステージのアクションが成功していることを確認することができます。
![](https://storage.googleapis.com/zenn-user-upload/2bf81cee9dee-20250320.png)
![](https://storage.googleapis.com/zenn-user-upload/c71434590534-20250320.png)

**【アプリケーションに接続】**
再びフロントエンドアプリケーションのトップページにアクセスすると、「Hello world! CI/CD succeeded!」という文字列が出力されることを確認できます。以上で、AWS CodeSeriesを使用したCI/CD実行の動作確認は完了です！
![](https://storage.googleapis.com/zenn-user-upload/37104327eaf4-20250320.png)

## 最後に
前回のGitHub Actionsに引き続きAWS CodeSeriesも実践してみたことで、CI/CDに対して結構自信がついてきた気がしてます！これからの案件で上手く活用できるといいですねぇ...

## 参考

**AWS CodeConnections**
https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodestarConnectionSource.html#action-reference-CodestarConnectionSource-config
https://blog.serverworks.co.jp/create-codeconnections

**AWS CodeCommitにおけるクロスアカウント構成**
https://zenn.dev/ncdc/articles/cd84517e10e3a5