CodePipelineからFargate(ECS)にデプロイする(2/2) - 実装編

## はじめに
前回は準備編としてサンプルアプリケーションを作成し、コンテナイメージ化、
ECS Fargate構成にデプロイして動作確認を行うところまで実施しました。

今回は実際にCodePipelineを使って、ソースコードがGitHubの特定のブランチにプッシュされたことを契機に
サンプルアプリケーションとコンテナをビルドし、ECSにインプレースデプロイする仕組みを作っていきます。

この一連の動作をするためには、CodeBuildとCodePipeline、GitHubアカウントが必要です。
まずはこれらの準備から行い動作確認をしていきます。

準備編は以下をご参照ください。
[CodePipelineからFargate(ECS)にデプロイする(1/2) - 準備編](https://qiita.com/pideoh/items/c841746f9b97d36667f9)
本記事に使用した各コードは以下で公開中です。
[pideoh/fargate-inplace-deploy-sample](https://github.com/pideoh/fargate-inplace-deploy-sample)

## CodeBuildの作成
CodeBuildはビルドプロジェクトとソースに同梱した`buildspec.yml`で動作します。
以降はIDEとCodeBuildコンソールとで操作します。

### buildspec.ymlの作成
ソースの`build-template`ディレクトリに`buildspec.yml`ファイルを作成し、以下の内容を入力します。
`buildspec.yml`は[リファレンス](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)をもとに実装します。

```yml
version: 0.2
 
phases:
  install:
    runtime-versions:
      docker: 18
      java: corretto11
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - export VERSION=`date '+%Y%m%d%H%M%S'`
      - $(aws ecr get-login --no-include-email)
  build:
    commands:
      - echo Build started on `date`
      - echo Building the SpringBoot Application...
      - ./gradlew build
      - echo Building the Docker image...
      - docker build -t ${REGISTORY_URI}:latest -f build-template/Dockerfile .
      - docker tag ${REGISTORY_URI}:latest ${REGISTORY_URI}:${VERSION}
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push ${REGISTORY_URI}:latest
      - docker push ${REGISTORY_URI}:${VERSION}
      - echo Writing image definitions file...
      - printf '[{"name":"sample-api","imageUri":"%s"}]' ${REGISTORY_URI}:${VERSION} > imagedefinitions.json
artifacts:
  files: 
    - imagedefinitions.json
cache:
  paths:
    - '/root/.gradle/caches/**/*'
    - '/root/.gradle/wrapper/**/*'
    - '.gradle/**/*'
```

ここでは、この中の重要な内容のみ記載します。

#### installシーケンス
ビルドの実行環境を指定します。
今回はGradleを利用してJDK 11のSpring Bootプロジェクトをビルドし、
ビルドしたアプリケーションをDockerイメージとするため、Docker 18及びCorretto11を指定します。
なお、Corretto11はランタイムをAmazon Linux 2としている場合に利用可能です。
※詳細はリファレンスを参照

#### post_buildシーケンス
post_buildシーケンスの最後で`printf '[{"name":"sample-api","imageUri":"%s"}]' ${REGISTORY_URI}:${VERSION} > imagedefinitions.json`というコマンドを実行し、`imagedefinitions.json`というjsonを作成しています。
`"name"`のValueはECSのタスク定義のサンプルアプリケーションのコンテナ名と合わせる必要があります。
これは、CodePipelineからビルドされたコンテナをECSにデプロイする際に、コンテナイメージを橋渡しする役割をするファイルです。
規定のjsonファイルで出力し、後述のartifactsシーケンスでBuild Artifactとして後続のデプロイシーケンスに渡します。
`imagedefinitions.json`の仕様やチュートリアルについては、以下をご参照ください。
[イメージ定義ファイルのリファレンス](https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html#pipelines-create-image-definitions)
[チュートリアル: CodePipeline を使用した継続的なデプロイ](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-cd-pipeline.html)

#### artifactsシーケンス
ここではpost_buildシーケンスで作成した`imagedefinitions.json`を、
後続のステージにBuild Artifactとして渡す定義をしています。
ビルドの結果として得られたパラメータやファイルを次のシーケンスに渡した場合は、
対象のファイルをfilesにリスト形式として渡します。

### ビルドプロジェクトの作成
CodeBuildコンソールを開き、「ビルドプロジェクトを作成する」を押下し、
画面に以下の通り入力していきます。
特に、`環境 - 特権付与`は忘れずにチェックボックスをつけてください。
CodeBuildは今回の環境ではコンテナ上で動くため、コンテナ上からコンテナビルドをするためにはこのチェックボックスを忘れずにつける必要があります。
入力完了後、作成を押下します。

| 項目            |入力内容| 
|:----------------|:------|
|プロジェクトの設定 - プロジェクト名|任意のプロジェクト名を入力|
|送信元 - ソースプロバイダ|GitHub|
|送信元 - リポジトリの URL|準備編で用意したリポジトリのURL|
|送信元 - 接続のステータス|「GitHubに接続」を押下し、OAuthを使用して接続|
|送信元 - ソースバージョン|ビルドするブランチを選択|
|環境 - オペレーティングシステム|Amazon Linux 2|
|環境 - ランタイム|Standard|
|環境 - イメージ|`aws/codebuild/amazonlinux2-x86_64-standard:2.0`|
|環境 - 特権付与|チェックボックスをオンにする|
|環境 - サービスロール|新しいサービスロール|
|環境 - ロール名|任意のロール名を入力|
|環境 - タイムアウト|10分|
|環境変数|名前：REGISTORY_URI、値:準備編で作成したECRのURI|
|Buildspec - Buildspec名 - オプショナル|`build-template/buildspec.yml`|
|ログ - CloudWatch Logs - グループ名|任意のグループ名を入力|
|ログ - CloudWatch Logs - ストリーム名|任意のストリーム名を入力|

入力し終わるとこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/1271b8c6-2391-3d8a-efd3-b14897d87da6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/5ed4c531-6b0c-7aeb-6537-42e257f285d9.png)

### サービスロールの変更
ビルドプロジェクトを作成する際に一緒に作成したサービスロールはECRの権限を持たないため、
ビルドに失敗します。そのため、IAMコンソールから作成したIAMロールを選択し、以下のインラインポリシーをアタッチします。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:CompleteLayerUpload",
                "ecr:GetAuthorizationToken",
                "ecr:InitiateLayerUpload",
                "ecr:PutImage",
                "ecr:UploadLayerPart"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}
```



## CodePipelineの作成
ここからはいよいよCodePipelineを作っていきます。
CodePipelineコンソールから「っパイプラインを作成する」を押下します。

### Step 1 of 5 パイプラインの設定を選択する
「パイプライン名」と「ロール名」を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/5cce3c09-691f-c473-7106-3c2daaefc450.png)

### Step 2 of 5 ソースステージを追加する
ソースプロバイダーにGitHubを選択し、CodeBuild同様に「GitHubに接続する」を押下し接続します。
リポジトリ・ブランチもCodeDeploy同様に設定していきます。
ここで設定したリポジトリ・ブランチにコードがプッシュされると、それを起因にCodePipelineが動作します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/914697a8-a658-659b-5d77-96815ebea95c.png)

### Step 3 of 5 ビルドステージを追加する
プロバイダーにCodeBuildを選択し、先程作成したビルドプロジェクトを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/a413ea15-aa43-7b67-b6f4-5c65945982eb.png)

### Step 4 of 5 デプロイステージを追加する
デプロイプロバイダーにECSを選択し、作成したクラスタ名・サービス名を入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/d4873289-652d-4909-0705-6a7180356232.png)

### Step 5 of 5 レビュー
問題ないか確認し、パイプラインを作成します。

## 動作確認
以上で実装は完了です。
早速動作確認をしてみましょう。

まずはCodePipelineから手動でビルドとデプロイを行います。
作成したパイプラインに入り、「変更をリリースする」を押下することでパイプラインが開始します。
完了すると以下の通り、すべて成功するはずです。
デプロイタスクはALBのdrainingがデフォルト値の300秒のため、時間がかかります。
気になる方は必要に応じて小さな値に変更してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/2f27c4c9-5ac4-7bd5-4f6d-27e8c3450b70.png)

ECSを確認すると、新しいリビジョンのタスク定義が作成され、タスクにデプロイされていることが確認できたと思います！

次は、リポジトリにプッシュされたことを契機にパイプラインが動作することを確認します。
任意のファイルをリポジトリに作成し、プッシュしてみます。
そうするとGitHubからCodePipelineにWebHookが走り、パイプラインが動作しました！

## まとめ
準備編からECS環境をセットアップし、ECS Fargate環境にCodePipelineを使い、GitHubへのプッシュ契機でインプレースデプロイすることに成功しました。
WebHook対象のブランチをリリース環境用のブランチとすることで、マージされた瞬間にビルドとデプロイが走る、といったことがAWSサービスだけで実現できますね。