AWS ECS（Fargate）を本番運用する時に、気にしないといけないあれこれについてのまとめ

Fargateを本番運用するにあたって、気にしなければいけない下記の点についてまとめました。

- デプロイ
- ログ
- 環境変数
- バッチ処理（Cron）
- 監視

CIは `CircleCI` を使いました。

## Fargateとは
AWSが提供している、サーバの管理が不要なコンテナタイプです。
https://aws.amazon.com/jp/fargate/

そもそものFargateの概念とかはこちらを参考にしてみてください
[AmazonECS / Fargate 本番運用のための構築とデプロイ方法まとめ](https://qiita.com/HiromuMasuda0228/items/2170032142ae1c33adcc)

### 注意点
Fargateはsshでサーバにログインすることができません。
本番サーバでバグがおきたからといって、サーバに直接入ってファイルを修正、なんていうことできないです（やりませんけど... :rolling_eyes: ）
そのためサーバ上で何かしらのコマンドを実行する場合は `ecs-cli compose run` コマンドを実行します。

これを実行すると任意のコマンドを実行するための一時的なコンテナを立てて、その中でコマンドを実行させることができます。

## デプロイ
AWS ECSをコマンドラインから操作できる、`ecs-cli` を使ってデプロイします。
AWSの公式です。

作成するコンテナの定義に、docker-compose.ymlをそのまま使うこともできるので、コンテナ定義の設定が一元管理できて嬉しい :smiley: 


コマンドの例

```
ecs-cli compose -f docker-composer.yml -p <プロジェクト名> service up
```

これでECSのサービスに紐づいたコンテナを更新することができます

オプションについて詳しい説明はこちら
[ecs-cliのドキュメント](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/cmd-ecs-cli-compose.html
)

### ecs-params.yml

`ecs-cli compose` コマンドを実行するには、ecs-params.ymlを作成しておく必要があります。
デフォルトでカレントディレクトリの ecs-params.yml を読み込みます。

設定内容はこちらを参考にしてください
[Amazon ECS パラメータの使用](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/cmd-ecs-cli-compose-ecsparams.html
)

### DBマイグレーション
DBのマイグレーション処理は、デプロイ（ecs-cli compose）とは別にコマンドを実行します。
注意点で書いた通りFargate上でコマンドを実行するには、**コマンド実行用のコンテナを立てて実行** することになります。

コマンド例
※php artisan migrate はLaravelのmigrationコマンドです

```
ecs-cli compose -f docker-compose.yml run <コンテナ名> "php artisan migrate"
```

これでコンテナ名に指定したコンテナに対して、コマンドを実行できます。

## ログ

ログ収集はCloudWatchに流すのがとりあえず一番楽でした。


まず最初に各コンテナで出力するログを、標準出力にするように変更します。

**Nginxの場合**

```
error_log  /dev/stderr;   # /var/log/nginx/error.log;
access_log /dev/stdout;   # /var/log/nginx/access.log;
```


CloudWatchにログを流すには、デプロイ時に使用する docker-composer.yml に設定を書きます。

```docker-compose.yml

Services:
  <コンテナ名>:
    image: <ECRに登録してあるイメージURL>
    logging:
      driver: awslogs
      options:
        awslogs-region: ap-northeast-1
        awslogs-group: <ロググループ名>
        awslogs-stream-prefix: <ログにつけるプレフィックス>
```
 
こうするとCloudWatchにログが流れます。
**ロググループはあらかじめ作成しておかないと、エラーになってしまったかもしれません。**

## 環境変数

環境変数管理には Amazon System Manger（以下SSM）を使うのが良さそうです。
ecs-params.ymlの `secrets` 属性を使うことでSSMに設定をした値をコンテナで使う環境変数として読み込ませることができます。

```ecs-params.yml
task_definition:
  中略
  services:
    <コンテナ名>:
      essential: true
      secrets:
        - value_from: APP_ENV
          name: APP_ENV
        - value_from: APP_DEBUG
          name: APP_DEBUG
        - value_from: APP_KEY
          name: APP_KEY
```

`value_from` がSSMで設定をしている名前です
`name` がコンテナで実際に使う環境変数名になります。

SSMを使うことでKMS（AWS Key Management Service）を使って値を暗号化して保存することもできるので、秘匿情報の管理が楽になります。

SSMの使い方に関しては、こちらを参考にしてみてください
[【祝！】FargateでもECSにごっつ簡単に環境変数に機密情報を渡せるようになりました！](https://dev.classmethod.jp/cloud/aws/fargarte-secrets/)


## バッチ処理（Cron）

Cronを使うようなバッチ処理は、ECSのコンソール画面から設定をすることができます。
<img width="575" alt="スクリーンショット 2019-06-16 15.43.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6808/b402be3a-4182-54c5-8522-7acbf23d3efe.png">

注意点としてはCronの時刻設定がUTCであることと、コマンドの上書きの設定がスペース区切りではなくカンマ区切りになるところです。

<img width="594" alt="スクリーンショット 2019-06-16 15.45.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6808/6348bf95-311a-795d-9f04-81e0c6afaefd.png">

<img width="615" alt="スクリーンショット 2019-06-16 15.46.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6808/8828005f-1f8e-7438-8a9b-8fd0dc571d16.png">

## 監視

|サービス|理由|
|---|---|
|Mackerel|デプロイする度にコンテナが生成される関係上<br>ホストがどんどん追加されてしまい<br>大変なことになるので辞めた|
|datadog|デプロイ時にコンテナを生成するタイミングで<br>過去のメトリクス情報が見れなくなってしまい断念。<br>表示されているCPU使用率とかもなんかちゃんと<br>とれているのか怪しかった（設定の問題かも）|

上記の2つしか試してませんが、どちらもちょっといまいちだったので、素直にCloudWatchで監視をするようにしています。

## 本番で運用してみた感想と課題

特にトラブルもなくいい感じです。
今の所Fargateならではの恩恵は受けていないのですが、そのうちFargateでよかったと思う場面があることでしょう。

### 課題: デプロイが遅い

※解決しました。
これが問題だったようです。
https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-target-groups.html#deregistration-delay

ターゲットグループの登録解除の遅延がデフォルト300秒になっているので、これを短くしておけば、drainingのスピードが早くなります。
<img width="1034" alt="ターゲットグループ___EC2_Management_Console.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6808/a49e0d71-22de-6268-b7c1-5bcd83a8cdcf.png">


~~正確にはALBのdraining処理がめちゃくちゃ遅いです。
新しくコンテナを作ってALBに紐づけるところまでは、普通なのですが紐づけたあと古いコンテナを切り離す、drainingが時間がかかってしまっています。時間にして8〜9分ほど... :sob: 
ここの解決策は模索中です。
ecs-cli composeのタイムアウトがデフォルト5分なので、今はオプションでtimeout時間を伸ばしてどうにかしているところです。:thermometer_face:~~

```
ecs-cli compose -f docker-composer.yml -p <プロジェクト名> service up --timeout 10
```