【Docker × ECS入門】docker compose upでECSデプロイ

DockerのECS Pluginがリリースされ、Docker for MacのStable版にも対応したみたいなので、試してみました。

公式ドキュメントや解説サイトもありますが、所々つまづいた点もありましたので、
（いつの間にかdocker ecs composeコマンドがdocker composeに統合されてたり...）
ECS初心者の自分用メモとしてまとめてみました。

# 検証環境
- macOS Catalina 10.15.7
- [Docker for Mac](https://hub.docker.com/editions/community/docker-ce-desktop-mac/) (Docker version 19.03.13)
- [AWS CLI](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-mac.html) (aws-cli/2.0.59)

AWS CLIはバージョン２系が必須です。
Docker、AWS CLIともに最新版にアップデートしておくことをおすすめします。

参考リンク：
[AWS CLI の設定](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-quickstart.html)
[macOS での AWS CLI バージョン 2 のインストール、更新、アンインストール](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-mac.html)

# Dockerイメージの準備

今回は[Docker公式サンプル](https://github.com/docker/ecs-plugin/tree/master/example)を使用します。
まずはローカルでの動作を確認します。
ほぼ[公式Doc](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/getting-started-cli.html)に書いてあるのですが、自分用メモとしてDocker公式サンプルでの手順を解説していきます。

- Docker公式サンプルをpull

```zsh
$ git clone https://github.com/docker/ecs-plugin.git
$ cd ecs-plugin/example/
```

- exampleという名前でタグを付け、appディレクトリをビルド

```zsh
$ docker build app -t example
```

- タグを指定して、イメージが作成されていることを確認

```zsh
$ docker images --filter reference=example
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
example             latest              33e46b7030e0        4 minutes ago       52.3MB
```

- ローカル動作確認

```zsh
$ docker run -t -i -p 5000:5000 example
Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
```

http://0.0.0.0:5000/ にアクセスしてみてください。
redisが起動していないため、
redis.exceptions.ConnectionError
が発生するはずです。
CTRL+C で終了します。

# Amazon ECR でのセットアップ

Amazon ECRは、DockerHubみたいなものです。
作成したDockerイメージをECRにpushし、docker-compose使用時にpullします。

※ ECRへプッシュの段階ではdefaultコンテクストを使用します。
（特段使用していなければ、defaultになってるはず）

```zsh:
$ docker context use default
$ docker context ls
```

### IAMユーザー準備

こちらも[公式Doc](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/get-set-up-for-amazon-ecr.html)に沿ってIAMユーザーを作成します。
手順通り、「Administrator」という名前で作成します。
作成したIAMユーザーでサインインすると、
ナビゲーションバーに「Administrator@ aws_account_id」が表示されます。
ex. Administrator@1234-5678-9012

認証情報は後ほど使用するため、メモしておいてください。
aws_account_id
aws_access_key_id
aws_secret_access_key

### コンソールからデフォルトレジストリに対して認証する

筆者の場合は東京リージョン（ap-northeast-1）を使用します。
[aws_account_id]にナビゲーションバーで確認したaws_account_idをハイフンなしで入力。
ex. 1234-5678-9012 => 123456789012


```zsh
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [aws_account_id].dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded
```

### レポジトリを作成

これで Amazon ECR にプッシュするイメージが用意できたので、それを保持するレポジトリを作成します。
example:latest というレポジトリを作成し、先ほど作成したexampleイメージをプッシュします。

- レポジトリを作成

```zsh
$ aws ecr create-repository \
		--repository-name example \
		--image-scanning-configuration scanOnPush=true \
		--region ap-northeast-1 
```

- リポジトリにプッシュするイメージにタグ付け

```zsh
$ docker tag example:latest [aws_account_id].dkr.ecr.ap-northeast-1.amazonaws.com/example:latest
```

- イメージをプッシュ

```zsh
$ docker push [aws_account_id].dkr.ecr.ap-northeast-1.amazonaws.com/example:latest
```

- 確認

```zsh
$ aws ecr list-images --repository-name example
```

# docker-compose ローカル動作確認

ECRにプッシュしたイメージをプルして、ローカルで動作確認を行います。
ERCにイメージがプッシュできているか確認しましょう。
Amazon ECR コンソール (https://console.aws.amazon.com/ecr/) を開きます。
exampleリポジトリが作成されているはずです。

![スクリーンショット 2020-10-30 13.01.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465986/8251a6c9-db2b-a19c-64f6-8bbdf61c3506.png)

URLをコピーします。
docker-compose.ymlのimageに貼り付け、docker-compose up します。
x-aws-pull_credentialsは今回は不要なため、コメントアウトします。

```example/docker-compose.yml
version: "3.8"
services:
  frontend:
    build: app
    # x-aws-pull_credentials: <<<your arn for your secret you can get with docker ecs secret list>>>
    image: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/example:latest
    ports:
      - "5000:5000"
    depends_on:
      - backend
  backend:
    image: redis:alpine
```

```zsh
$ docker-compose up
```

ECRにプッシュしたイメージをプルできるため、ローカルでアプリの動作を確認できます。

![スクリーンショット 2020-10-30 13.09.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465986/0a51899d-093f-d1a7-1881-ac4a31e08ef8.png)

redisも動作確認できました。

```zsh
$ docker-compose down
```

# ECSへデプロイ

### AWS Context作成

[docker docs(翻訳)](https://matsuand.github.io/docs.docker.jp.onthefly/engine/context/ecs-integration/)通り、myecscontextという名前で作成します。
先ほどIAMで作成した認証情報を使います。
aws_access_key_id
aws_secret_access_key

```
$ docker context create ecs myecscontext
? Select AWS Profile new profile
? profile name myecscontext
? Region ap-northeast-1
? Enter credentials Yes
? AWS Access Key ID aws_access_key_id
? Enter AWS Secret Access Key aws_secret_access_key
Successfully created ecs context "myecscontext"
```

contextを確認

```
$ docker context ls
NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT               KUBERNETES ENDPOINT   ORCHESTRATOR
default *           moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
myecscontext        ecs                 ap-northeast-1                 
```

myecscontextを使用

```
$ docker context use myecscontext
```

※セットアップでエラーが出る場合

```
this tool requires the "new ARN resource ID format"
```

long ARN formatを有効にする必要があるみたいです。
おまじないだと思って実行します。

```
$ aws ecs put-account-setting-default --name awsvpcTrunking --value enabled
$ aws ecs put-account-setting-default --name containerInsights --value enabled
$ aws ecs put-account-setting-default --name containerInstanceLongArnFormat --value enabled
$ aws ecs put-account-setting-default --name serviceLongArnFormat --value enabled
$ aws ecs put-account-setting-default --name taskLongArnFormat --value enabled
```

### ECSへデプロイ

やっとECSですが、一瞬で終わります。
docker compose upすると自動でリソースを作成し、デプロイしてくれます。

※ハイフン注意
○ docker compose
× docker-compose

```zsh
$ docker compose up
WARN[0000] services.build: unsupported attribute        
[+] Running 17/17
 ⠿ example                        CREATE_COMPLETE                                                                                                          204.0s
 ⠿ ExampleLoadBalancer            CREATE_COMPLETE                                                                                                          122.0s
 ⠿ FrontendTCP5000TargetGroup     CREATE_COMPLETE                                                                                                            1.0s
 ⠿ CloudMap                       CREATE_COMPLETE                                                                                                           51.0s
 ⠿ FrontendTaskExecutionRole      CREATE_COMPLETE                                                                                                           22.0s
 ⠿ LogGroup                       CREATE_COMPLETE                                                                                                            3.0s
 ⠿ ExampleDefaultNetwork          CREATE_COMPLETE                                                                                                            8.0s
 ⠿ Cluster                        CREATE_COMPLETE                                                                                                            6.0s
 ⠿ BackendTaskExecutionRole       CREATE_COMPLETE                                                                                                           21.0s
 ⠿ ExampleDefaultNetworkIngress   CREATE_COMPLETE                                                                                                            1.0s
 ⠿ FrontendTaskDefinition         CREATE_COMPLETE                                                                                                            3.0s
 ⠿ BackendTaskDefinition          CREATE_COMPLETE                                                                                                            4.0s
 ⠿ FrontendServiceDiscoveryEntry  CREATE_COMPLETE                                                                                                            3.0s
 ⠿ BackendServiceDiscoveryEntry   CREATE_COMPLETE                                                                                                            1.9s
 ⠿ BackendService                 CREATE_COMPLETE                                                                                                           68.0s
 ⠿ FrontendTCP5000Listener        CREATE_COMPLETE                                                                                                            1.0s
 ⠿ FrontendService                CREATE_COMPLETE                                                                                                           68.0s
```

### デプロイしたアプリを確認

起動しているコンテナの状態を確認。

```zsh
$ docker compose ps
ID                                     NAME                REPLICAS            PORTS
example-BackendService-xSDWkSABSvEt    backend             1/1                 
example-FrontendService-0pIz1giwUZg4   frontend            1/1                 ExampleLoadBalancer-xxxxxxxxxx.elb.ap-northeast-1.amazonaws.com:5000->5000/tcp
```

frontendのPORTSにアクセスしてみてください。
サンプルアプリがデプロイできているかと思います。
http://ExampleLoadBalancer-xxxxxxxxxx.elb.ap-northeast-1.amazonaws.com:5000/
ロードバランサーのリスナーポートをTCP:5000からTCP:80に変更するとポート指定なしでアクセスできるかと思います。（反映に少し時間がかかります）

### デプロイしたリソースの削除

docker compose downすると、自動でリソースを削除してくれます。

```zsh
$ docker compose down
[+] Running 17/17
 ⠿ example                        DELETE_COMPLETE                                                                                                                515.0s
 ⠿ ExampleLoadBalancer            DELETE_COMPLETE                                                                                                                389.0s
 ⠿ FrontendTCP5000TargetGroup     DELETE_COMPLETE                                                                                                                389.0s
 ⠿ CloudMap                       DELETE_COMPLETE                                                                                                                514.0s
 ⠿ FrontendTaskExecutionRole      DELETE_COMPLETE                                                                                                                393.0s
 ⠿ LogGroup                       DELETE_COMPLETE                                                                                                                471.0s
 ⠿ ExampleDefaultNetwork          DELETE_COMPLETE                                                                                                                467.0s
 ⠿ Cluster                        DELETE_COMPLETE                                                                                                                467.0s
 ⠿ BackendTaskExecutionRole       DELETE_COMPLETE                                                                                                                472.0s
 ⠿ ExampleDefaultNetworkIngress   DELETE_COMPLETE                                                                                                                  3.0s
 ⠿ FrontendTaskDefinition         DELETE_COMPLETE                                                                                                                390.0s
 ⠿ BackendTaskDefinition          DELETE_COMPLETE                                                                                                                467.0s
 ⠿ FrontendServiceDiscoveryEntry  DELETE_COMPLETE                                                                                                                389.0s
 ⠿ BackendServiceDiscoveryEntry   DELETE_COMPLETE                                                                                                                467.0s
 ⠿ BackendService                 DELETE_COMPLETE                                                                                                                464.0s
 ⠿ FrontendTCP5000Listener        DELETE_COMPLETE                                                                                                                388.0s
 ⠿ FrontendService                DELETE_COMPLETE                                                                                                                386.0s
```

# 参考文献
[Docker公式サンプル](https://github.com/docker/ecs-plugin/tree/master/example)
[ECS での Docker コンテナーのデプロイ](https://matsuand.github.io/docs.docker.jp.onthefly/engine/context/ecs-integration/)
[AWS CLI を使用した Amazon ECR の開始方法](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/getting-started-cli.html)
[Fargate を使用した Amazon ECS の開始方法](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/getting-started-fargate.html)
[DockerとAWSのコラボによりdocker ecsコマンドが爆誕したので使ってみた](https://dev.classmethod.jp/articles/docker-ecs/)
[Docker ComposeのAmazon ECSデプロイを試してみた](https://tech.smartcamp.co.jp/entry/docker-compose-with-ecs)