CodePipelineからFargate(ECS)にデプロイする(1/2) - 準備編

## はじめに
Fargateを使用したECSのサービスデプロイはインプレースデプロイ一択となりますが、
CodePipelineを使ってFargateにインプレースデプロイしている記事がなかったので書いてみることにしました。
具体的にはソースコードがGitHubの特定のブランチにプッシュされたことを契機に
サンプルアプリケーションとコンテナをビルドし、ECSにインプレースデプロイする仕組みを作ります。

長くなるので2部構成とし、準備編ではFargateでECSを構成し、サンプルのWebアプリケーションをデプロイします。
実装編ではCodePipelineを利用してFargate ECS環境にインプレースデプロイを行います。

作業は以下の流れで進めていきます。

- サンプルアプリケーションの準備
- ALBの作成
- Firehoseの作成
- ECSの作成

なお、ログは最近Fargateに対応したFireLensも利用してFirehose→S3と送ってみます。

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

## サンプルアプリケーションの準備
誰でも作れるをモットーに、[Spring Initilizer](https://start.spring.io/)を使って
サンプルアプリケーションのガワをサクッと作ります。
Spring Initializerを開き、以下の通り入力します。

| 項目                              |入力内容         | 
|:----------------------------------|:---------------|
| Project                           |Gradle Project  |
| Language                          |Java            |
| Spring Boot                       |2.2.2           | 
| Project Metadata - Group          |任意            | 
| Project Metadata - Artifact       |任意            | 
| Project Metadata - Options - Java |11              | 
| Dependencies                      |Spring Web      | 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/da264a2b-0d0d-3f95-8c5f-b05e05053074.png)

入力したらGenerateをクリックするとzipファイルでガワをダウンロードできます。
ここからはCLoud9で作業していきます。
Cloud9の環境構築は[こちら](https://qiita.com/pideoh/items/f9a4883cd46eb2b07f07)にまとめてありますので、合わせて御覧ください。

Cloud9にDLしたzipファイルを移動し、すると以下のような構成になります。

```bash
.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── HELP.md
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── pideoh
    │   │           └── sample
    │   │               └── SampleApplication.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── java
            └── com
                └── pideoh
                    └── sample
                        └── SampleApplicationTests.java
```

`src/main/java/com/pideoh/sample`以下に`SampleController.java`を作成し、以下のコードを追加します。

```java
package com.pideoh.sample;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController("/")
public class SampleController {

    @GetMapping
    public String sample() {
        return "Sample\n";
    }

}
```

これで、`$ ./gradlew bootJar`を実行して、エラーが起きなければOKです。
出来上がったソースは自分のGitHubリポジトリにプッシュしておきましょう。


## ALB作成
ECSで起動したサンプルアプリケーションを確認するために、ALBを作成しておきます。
ALBは特段インターネット公開する必要がないため、Internal ALBとします。
以降はEC2コンソール前提で進みます。

### Target Group作成
EC2コンソールから、ロードバランシング→ターゲットグループと進み、「ターゲットグループの作成」を押下します。
設定値は名前とVPCは適宜選択し、その他のパラメータは以下とし、入力後「作成」を押下します。（デフォルトから変えるもののみ羅列）

| 項目            |入力内容| 
|:----------------|:------|
| ターゲットの種類  |IP     |
| ポート           |8080   |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/ebb5e818-f395-bffd-c0ea-2915efe118e3.png)

### ALBの作成
同じく、ロードバランシング→ロードバランサーと進み、「ロードバランサーの作成」を押下します。
Application Load Banalncerの「作成」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/4396ffa4-f68a-d96b-bfe3-e5192e9b6799.png)

### 1. ロードバランサーの設定
スキームを「内部」にして「名前」、「VPC」、「AZ」、「サブネット」を入力し、次に進みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/0cf2b77a-7792-39f8-5442-b55abdd5a527.png)

### 2. セキュリティ設定の構成
警告が出ますが、次に進みます。

### 3. セキュリティグループの設定
セキュリティグループを指定します。
ここでは、VPCのCIDRからのインバウンドを許可しているセキュリティグループを事前に作成し、指定します。

### 4. ルーティングの設定
先程作成したターゲットグループを設定し、次へ進みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/45fb3408-6ee6-b62c-fb50-4cbb4058d071.png)

### 5. ターゲットの登録、確認
次に進み、確認画面で内容に問題なければ「作成」を押下し完了します。

## Firehoseの作成
Kinesisコンソールから「Data Firehose」を選択し、「Create delivery stream」を選択します。

### Step 1: Name and source
「Delivery stream name」を入力し、次へ進みます。

### Step 2: Process records
データ変換等はしないため、次へ進みます。

### Step 3: Choose a destination
Destinationの選択をします。
ここではS3に転送するため、以下の通り入力し、次へ進みます。

| 項目            |入力内容| 
|:----------------|:------|
|Destination|Amazon S3|
|S3 bucket|出力先のバケット|アク
|S3 prefix|log/sample-app/|
|S3 error prefix|error/firehose/sample-app/|

![FireShot Capture 006 - Amazon Kinesis Firehose - ap-northeast-1.console.aws.amazon.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/c1ddac72-70be-201b-fbf6-8704cbdb60b7.png)

### Step 4: Configure settings
Permissionsの「IAM Role」にて、「Create new or choose」を選択します。
「IAMロール」にて、「新しいIAMロール」を選択して「許可」を押下します。
許可を押すと作成されたIAMロール名が入力されるため、次へ進みます。

### Step 5: Review
内容が問題ないか確認して作成を押せば完了です。

## ECSの作成
いよいよ、ECSを作成してきます。
ここからはECSコンソールで操作していきます。

### ECRの作成
「Repositories」を選択し、「リポジトリを作成」を押下します。
リポジトリ名を入力し、プッシュ時にスキャンを有効にした上で「リポジトリを作成」を押下し、リポジトリを作成します。

#### ECRへコンテナのプッシュ
Cloud9に戻り、ソースのルートディレクトリに「build-template」ディレクトリを作成する。
作成したディレクトリにDockerfileを作成し、以下を記述する。

```docker
FROM amazoncorretto:11.0.5

## Make application directory
RUN set -x && mkdir -p /opt/app

## 
COPY ./build/libs/sample-0.0.1-SNAPSHOT.jar /opt/app

## Environment Variables
STOPSIGNAL 15
EXPOSE 8080
CMD ["java","-jar","/opt/apps/sample-0.0.1-SNAPSHOT.jar"]
```

ここまでで以下のようになっている、はず。

```bash
.
├── build.gradle
├── build-template
│   └── Dockerfile
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── HELP.md
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── pideoh
    │   │           └── sample
    │   │               ├── SampleApplication.java
    │   │               └── SampleController.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── java
            └── com
                └── pideoh
                    └── sample
                        └── SampleApplicationTests.java
```

ソースのディレクトリにてソースのビルドとコンテナビルド、先程作成したコンテナリポジトリにコンテナをプッシュします。

- アプリケーションのビルド

```bash
$ ./gradlew build

> Task :test
2019-12-13 01:28:11.174  INFO 15592 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

BUILD SUCCESSFUL in 7s
5 actionable tasks: 5 executed
```

- コンテナのビルドとプッシュ

```bash
$ docker build -f build-template/Dockerfile -t 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/pideoh/sample-api:latest .                                                                                                                                        
Sending build context to Docker daemon  18.03MB
Step 1/6 : FROM amazoncorretto:11.0.5
 ---> baf7d8995918
Step 2/6 : RUN set -x && mkdir -p /opt/app
 ---> Using cache
 ---> 95eec55cf32a
Step 3/6 : COPY ./build/libs/sample-0.0.1-SNAPSHOT.jar /opt/app
 ---> Using cache
 ---> 9d8367f599ca
Step 4/6 : STOPSIGNAL 15
 ---> Using cache
 ---> 2f794c817a3a
Step 5/6 : EXPOSE 8080
 ---> Using cache
 ---> ed1bfa2854a3
Step 6/6 : CMD ["java","-jar","/opt/apps/sample-0.0.1-SNAPSHOT.jar"]
 ---> Using cache
 ---> 45673049a1a8
Successfully built 45673049a1a8
Successfully tagged 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/pideoh/sample-api:latest
$ docker tag 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/pideoh/sample-api:latest 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/pideoh/sample-api:20191213
$ $(aws ecr get-login --no-include-email)
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /home/ec2-user/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
$ docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/pideoh/sample-api:latest
The push refers to repository [123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/pideoh/sample-api]
067a7f1bc871: Pushed 
a8d07ad8783f: Pushed 
8e2692204e8e: Pushed 
fdf6c4a26006: Pushed 
latest: digest: sha256:34e5ee5ceca59d4b3d760bf28b4e62e4586a937253ac2b7225d90d7010d415e4 size: 1161
$ docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/pideoh/sample-api:20191213The push refers to repository [123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/pideoh/sample-api]
067a7f1bc871: Layer already exists 
a8d07ad8783f: Layer already exists 
8e2692204e8e: Layer already exists 
fdf6c4a26006: Layer already exists 
20191213: digest: sha256:34e5ee5ceca59d4b3d760bf28b4e62e4586a937253ac2b7225d90d7010d415e4 size: 1161
```

うまくいけばこのように登録されるはずです。
ここに表示されている「イメージのURI」は後で利用するためメモしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/afb4bc6b-5318-903d-a557-959b754639cc.png)


### タスク定義の作成
タスク定義から「タスク定義の作成」を押下します。

#### ステップ 1: 起動タイプの互換性の選択
「Fargate」を選択し、次へ進みます。

#### ステップ 2: タスクとコンテナの定義の設定
以下の項目を入力していきます。

| 項目            |入力内容| 
|:----------------|:------|
|タスク定義名|任意の名前を入力します|
|タスクロール|コンテナに付与するIAMロールを選択します。今回はFireLensからFirehoseにログを流すため、Firehoseにアクセスできるポリシーを付与したIAMロールが必要です。|
|タスク実行ロール|すでに存在する場合はecsTaskExecutionRoleを選択し、存在しない場合は「新しいロールを作成」を選択します|
|タスクメモリ (GB)|0.5GB|
|タスク CPU (vCPU)|0.25vCPU|
|Enable FireLens integration|チェックボックスを選択し、Applyを押下します|
|コンテナの定義 - コンテナの追加 - コンテナ名|sample-api|
|コンテナの定義 - コンテナの追加 - イメージ |先程メモした「イメージのURI」を入力します|
|コンテナの定義 - コンテナの追加 - ポートマッピング|8080 tcp|
|コンテナの定義 - コンテナの追加 - ヘルスチェック- コマンド|CMD-SHELL, curl -f http://localhost:8080/ &#124;&#124; exit 1|
|コンテナの定義 - コンテナの追加 - 環境 - コマンド|java,-jar,/opt/app/sample-0.0.1-SNAPSHOT.jar|
|コンテナの定義 - コンテナの追加 - ログ設定|Auto-configure CloudWatch Logsのチェックボックスを外します|
|コンテナの定義 - コンテナの追加 - ログ設定 - ログドライバー|awsfirelens|
|コンテナの定義 - コンテナの追加 - ログ設定 - ログオプション|Name:firehose|
|コンテナの定義 - コンテナの追加 - ログ設定 - ログオプション|region:ap-northeast-1|
|コンテナの定義 - コンテナの追加 - ログ設定 - ログオプション|delivery_stream:作成したFirehoseのリソース名|
|コンテナの定義 - log_route - 環境 - コマンド|Auto-configure CloudWatch Logsのチェックボックスを入れます|

上記をすべて入力すると、以下のようになるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40643/29271703-c00d-04fd-3b40-6e7a308db0f7.png)


### ECSクラスターの作成
ECS→クラスター画面から「クラスターの作成」を押下します。

#### ステップ 1: クラスターテンプレートの選択
「ネットワーキングのみ」を選択し次に進みます。

#### ステップ 2: クラスターの設定
「クラスター名」を入力して作成します。

### サービスの作成
ECS→クラスター画面から先程作成したクラスタを選択し、クラスタの画面に入ります。
「サービス」タブで「作成」を押下します。

#### ステップ 1: サービスの設定
以下の項目を入力していきます。

| 項目            |入力内容| 
|:----------------|:------|
|起動タイプ|Fargate|
|タスク定義 - ファミリー、リビジョン|先程作成したタスク定義を選択します|
|サービス名|サービス名を入力します|
|タスクの数|1|

#### ステップ 2: ネットワーク構成
以下の項目を入力していきます。

| 項目            |入力内容| 
|:----------------|:------|
|クラスター VPC|コンテナを構築するVPCを選択します|
|サブネット|コンテナを構築するサブネットを選択します|
|セキュリティグループ|ALB作成に使ったセキュリテイグループを選択します|
|パブリック IP の自動割り当て|DISABLED|
|ロードバランサーの種類|Application Load Balancer|
|ロードバランサー名|作成したALBを選択します|
|ロードバランス用のコンテナ|`sample-api:8080`が選択されていることを確認し、ロードバランサーに追加を選択します|
|プロダクションリスナーポート|80:HTTP|
|ターゲットグループ名|作成したターゲットグループを選択します|

#### ステップ 3: Auto Scaling (オプション)
特に設定しないため、次に進みます。

#### ステップ 4: 確認
問題なければ「サービスの作成」を押下します。

## 動作確認
お疲れ様でした。これで基本的に設定は終わっているはずです。
ELBのエンドポイントにアクセスして、「Sample」と帰ってくれば、無事動作しています。
また、S3上にアクセスログが出力されていれば、ログも正常に出力されています。
次回はいよいよCodePipelineを使ってGitHubにソースコードがPushされるとAPビルド→コンテナビルド→コンテナ起動する仕組みを作ります。