【AWS】ECS構築の基礎 −CI/CD編 (CodeCommit/CodeBuild/CodePipeline)−

#はじめに
ECSとECRを触れるようになったので、今回はCodeCommit, CodeBuild, CodePipelineを使って、AWSサービスのみでCI/CDパイプラインを構築してみました。

https://qiita.com/suzuki0430/items/2745e9ca0ead8db982f9

https://qiita.com/suzuki0430/items/440daea590b22d1621c8

https://qiita.com/suzuki0430/items/6e4e7f513f982dadbf09

https://qiita.com/suzuki0430/items/7e0ad9903d26dd5f2529

GitHub ActionsによるCI/CDパイプラインの構築については以前記事を書きました。
もし興味があればこちらをご覧ください。

https://qiita.com/suzuki0430/items/de0cc70f0b9d2b1ad00b

#AWSのCI/CD関連サービス
CI(Continuous Integration)では、ソースコードをホスティングサービス(GitHub, Bitbucketなど)にプッシュしたときに、自動でテスト(ビルド)サーバ(GitHub Actions, Jenkins CI, Circle CIなど)でテストを行い、ビルドの成功可否を開発者にフィードバックします。
CIを行うことで早期にバグを発見することができるようになります。
CodeCommitはホスティングサービスの役割、CodeBuildはテストサーバの役割をもつAWSサービスです。

また、CD(Continuous Delivery)はCIを拡張した手法で、ビルドやテストだけでなく、リリースプロセス全体を自動化します。
CDでは、ビルドとテストを実行した後、テスト環境またはステージング環境にデプロイして、システムテストやUIテストを行います。
開発者は、これらのテストが完了した後に、最後のステップとして本番(運用)環境への更新を承認します。

CDを行うことでリリースサイクルを早め、市場からのフィードバックをすぐにソフトウェアへ反映させることができるようになります。
CodeDeployはデプロイを自動化する役割をもつAWSサービスで、ECSのblue/greenデプロイ(古いサーバを残したまま新しいサーバを構築し、一定期間経過後に新しいサーバが安定したのを確認してから古いサーバを消去するデプロイ手法)などに使用します。

CI/CDの流れと使用するサービスをまとめると以下の図のようになります。
CodePipelineでCI/CD全体のステップを管理することができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/f5bc4e4f-390f-5232-e0dd-6264ac66a04e.png" width="800">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/f1dde29f-9e2f-f061-6e46-d3ca99170901.png" width="800">

#CodeCommit
以下でCodeCommitの操作を学びます。

1. CodeCommitにリポジトリを作成し、ローカルにクローンを作成
2. GitHubのコードをローカルにプルして、1で作成したクローンにコードを追加
3. コードをCodeCommitにプッシュ

##実装
CodeCommitを開いて"リポジトリを作成"を選択すると以下の画面になります。
リポジトリ名と説明(任意)を記述すれば作成完了です。
![スクリーンショット 2021-07-08 21.55.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/263d644e-4f5b-5353-1705-0b12dc9d62bb.png)

以下の画面になるので、接続のステップを順番に実行していきます。
![スクリーンショット 2021-07-08 22.09.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/df177cc8-810f-fc9c-3010-5692f03646f3.png)

まず、`brew install git`でGitクライアントをインストールします。

次にGit認証情報を作成します。
ログイン中のIAMユーザ`ecs-course`にCodeCommitへのアクセス権限を付加します。
![スクリーンショット 2021-07-08 22.13.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/c37cbae0-3c91-6731-84ff-75e7b8931e3f.png)

`AWSCodeCommitPowerUser`を付加します。
![スクリーンショット 2021-07-08 22.14.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/4ddc5aaf-5b5e-7a03-0eb2-d7c6ea17c143.png)

認証情報タブからAWS CodeCommitリポジトリへのHTTPS接続の認証に使用できるユーザー名とパスワードを生成します。
![スクリーンショット 2021-07-08 22.16.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/d1e94b40-2daf-68b3-31d8-5c8a31e9838c.png)
![スクリーンショット 2021-07-08 22.17.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/a985e760-5783-9b2d-4008-2201a94f7744.png)

`git clone ~`でリポジトリ`simplehttp`のクローンをローカルに作成します。
`git remote -v`を実行すると、リモートリポジトリがCodeCommitにあることが確認できます。

```
origin	https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/simplehttp (fetch)
origin	https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/simplehttp (push)
```

`cd simplehttp`でディレクトリを移動し、`git clone https://github.com/gkoenig/go-simplehttp.git /tmp/simplehttp`でGitHubからリポジトリをクローンします。
`cp /tmp/simplehttp/Dockerfile /tmp/simplehttp/README.md /tmp/simplehttp/simpleHTTP.go .`で`simplehttp`にファイルをコピーします。

コミットとプッシュを行います。

```
$ git add .
$ git commit -m 'initial commit'
$ git push
```
コンソールから`simplehttp`を開くと、正常にプッシュされたことを確認できます。
![スクリーンショット 2021-07-08 22.47.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/53f63caf-58de-779f-9a91-0ecb045774a7.png)

#CodeBuild
以下でCodeBuildの操作を学びます。

1. IAMロールの作成
2. ビルドプロジェクトの作成

##実装
一つの手順が長いので項目をわけます。

###IAMロールの作成
CloudWatchLogsやCodeCommitへのアクセスに関するIAMポリシーを新しく作成します。
![スクリーンショット 2021-07-09 21.13.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/0ab5c3f4-d3b4-9595-61f0-5cb3c3974913.png)

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CloudWatchLogsPolicy",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "CodeCommitPolicy",
      "Effect": "Allow",
      "Action": [
        "codecommit:GitPull"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "S3GetObjectPolicy",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "S3PutObjectPolicy",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "ECRPullPolicy",
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "ecr:PutImage",
        "ecr:InitiateLayerUpload",
        "ecr:UploadLayerPart",
        "ecr:CompleteLayerUpload"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "ECRAuthPolicy",
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "S3BucketIdentity",
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketAcl",
        "s3:GetBucketLocation"
      ],
      "Resource": 
        "*"
    }
  ]
}
```

ポリシー名は`CodeBuildServiceRolePolicy`とします。
![スクリーンショット 2021-07-09 21.15.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/94ec9f20-f4f9-9dd9-891a-df88a0246bc8.png)

次にIAMロールを作成します。
`CodeBuild`というユースケースを選択します。
![スクリーンショット 2021-07-09 21.17.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/b7d5bd94-944f-d252-b762-02183eb4e5bf.png)

作成したポリシーをアタッチします。
![スクリーンショット 2021-07-09 21.18.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/93443de4-491d-110c-a8ec-87a9b8cb27c3.png)

ロール名は`CodeBuildServiceRole`とします。
![スクリーンショット 2021-07-09 21.28.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/44816aa1-e1ea-372f-e3cf-d2ba283fa833.png)

ポリシーをもう一つアタッチします。
![スクリーンショット 2021-07-09 21.31.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/be775c56-3820-5513-2665-9d3234c43184.png)

`AmazonElasticContainerRegistryPublicPowerUser`を選択します。
![スクリーンショット 2021-07-09 21.32.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/5a9c0739-113f-0f26-cb3e-0f29721cc99d.png)

###ビルドプロジェクトの作成
CodeBuildの画面からビルドプロジェクトを新しく作成します。
![スクリーンショット 2021-07-09 21.59.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/f551b448-3faa-9b90-9c78-aa2808400273.png)
![スクリーンショット 2021-07-09 21.59.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/6b77070b-0a80-ede5-29c9-78b415d0ff0e.png)
![スクリーンショット 2021-07-09 22.00.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/9a532523-c757-7637-11d3-e0e2ae50ccf8.png)
![スクリーンショット 2021-07-09 22.01.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/28652f8f-b776-85c9-1615-bc9d65655be6.png)
![スクリーンショット 2021-07-09 22.01.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/96b6427c-aa41-896d-5bc7-6cb80037901a.png)

Buildspecの設定の部分で`buildspecファイルを使用する`を選択する場合は、ソースコードのルートディレクトリに`buildspec.yml`が必要となります。
`buildspec.yml`には以下のように、buildコマンドを記述します。
`<<account-id>>`にはアカウントIDを記述します。

```yml:buildspec.yml
version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - echo $AWS_DEFAULT_REGION
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
      - REPOSITORY_URI=<<account-id>>.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-simplehttp
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
      commands:
        - echo Build completed on `date`
        - docker push $REPOSITORY_URI:latest
        - docker push $REPOSITORY_URI:$IMAGE_TAG
```

ビルドプロジェクトを作成できたら、ビルドを開始します。
すべて成功すればOKです。
![スクリーンショット 2021-07-10 8.31.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/eeacd253-7964-6c70-3979-0eb6e87bd2e8.png)

ECRのリポジトリ画面からもイメージがプッシュされたことを確認できます。
![スクリーンショット 2021-07-10 8.48.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/41957f18-7303-533d-09c4-8d18e1d0fbde.png)

#CodePipeline
CodePipelineを使って、CI/CDパイプラインを構築します。

1. アーティファクト作成のために`buildspec.yml`を更新
2. パイプラインの構築
  1. CodeCommitリポジトリとの連携
  - CodeBuildのビルドプロジェクトとの連携
  - デプロイプロバイダとしてECSを設定

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/d25b5f6f-d8d2-9f2f-49ed-e7481806104a.png" width="800">

##実装
ビルドやテストのプロセスをアーティファクト`imagedefinitions.json`として出力するために、`buildspec.yml`に以下の記述を加えてプッシュします。

```yml
        - printf '[{"name":"simplehttp-container","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
  artifacts:
      files: imagedefinitions.json
```

CodePipelineから新規のパイプラインを作成します。
![スクリーンショット 2021-07-10 9.20.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/444ba39c-b8d3-aa5c-0082-22f7b0e4bfc7.png)

ソースコードのリポジトリ(CodeCommit)との連携を設定します。
![スクリーンショット 2021-07-10 9.22.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/75f3834c-747b-0428-696b-11bd3c595a5d.png)

ビルドプロジェクトとの連携を設定します。
![スクリーンショット 2021-07-10 9.24.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/e21b5ce4-cc59-c4fb-4159-10ace091d743.png)

デプロイプロバイダに`Amazon ECS`を選択し、クラスターやサービスは前の記事で作成したものを選択します。
デプロイプロバイダを`Amazon ECS`にすることで、リポジトリのコードを変更したとき、パイプラインが新しいDockerイメージを作成してコンテナレジストリにプッシュし、更新されたイメージをECSにデプロイします。
![スクリーンショット 2021-07-10 9.38.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/f27dac71-3ea1-0706-8be8-02eb2d8f1467.png)

パイプラインを作成すると実行を開始し、以下の画面になれば成功です。
![スクリーンショット 2021-07-10 10.01.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/ffa8cafe-f825-626f-0efb-a8bbe8066795.png)

#参考資料

https://www.udemy.com/course/aws-ecs-fargate/

https://cloudbees.techmatrix.jp/devops/cd/

https://qiita.com/NaokiIshimura/items/8e43135864f2518dcabc

https://blog.spacemarket.com/code/ci-cd-codepipeline/