【AWS】 Fargate CLI + Terraform で Docker コンテナを動かす簡単なチュートリアル

AWS の ECS Fargate を使って本番環境を構築していますが、 Terraform だけだと環境構築が若干複雑になるのが悩みでした。
そこで、 Fargate CLI と Terraform を組み合わせると、非常にシンプルでいい感じになりました。

[追記]
最近は、 Fargate CLI をやめて素の AWS CLI でやっています。
完全自動構築を目指す場合、その方が確実だな思いました。詳しくは下記。

[AWS Fargate サービスを Terraform で構築、 コマンドラインからデプロイ](https://qiita.com/acro5piano/items/d1c684ec3e8c74f9c05e)
[/追記]

デモ: https://github.com/acro5piano/terraform-fargate-example

# 概要

- [Terraform](https://www.terraform.io) を使って、 AWS ECS に必要な基本的な環境を構築する
- [Fargate CLI](https://github.com/awslabs/fargatecli) で Fargate のサービスやタスク定義などを構築する
- デプロイは Fargate のおかげでダウンタイム無し

# モチベーション

AWS の ECS Fargate を使って本番環境を構築する場合、いくつかの選択肢があります。

1. 管理画面からぽちぽち
- AWS CLI
- ECS CLI
- Terraform
- Fargate CLI

それぞれ、 

1. は属人的になるのでやりたくない。 
2. は設定が大変なのと、元に戻せないため、管理画面ぽちぽちするのとあまり変わらない。
3. はセキュリティグループや ALB の設定などは結局管理画面からやらないといけない。
4. は全てコード化でき、再適用や複製などもできるが、構築スクリプトがちょっと長くなる。そしてデプロイは単体ではできない。（できるけど Terraform 完全管理だと、運用上やりづらい）
5. はセキュリティグループの設定などは結局管理画面からやらないといけない。

そこで、 4. と 5. を組み合わせて、いいとこ取りしようという話です。

完成品はこちらをご覧ください。

https://github.com/acro5piano/terraform-fargate-example

# やり方

簡単な Web App を作る前提で進めます。

### Dockerfile

ポート 3000 を Listen できれば、何でも良いです。

```docker:Dockerfile
FROM python:3.7.7-alpine3.11

CMD python -m http.server 3000
```

### Terraform

Terraform は、下記を作成する役割を持っています。

- ECS クラスター （Fargate のサービスを動かすもの）
- Security Group （ファイアーウォール） 下記2つ
    - ALB 
    - ECS （ ALB との通信のみを許可する）

`aws-credentials.ini` の置き場所やリージョンはプロジェクトによると思うので、適宜置き換えて下さい。

```ruby:main.tf
# この辺の設定は環境によります
provider "aws" {
  region                  = "ap-northeast-1"
  shared_credentials_file = "./aws-credentials.ini"
  profile                 = "default"
}

resource "aws_ecs_cluster" "webapp" {
  name = "webapp"
}

resource "aws_security_group" "webapp" {
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port       = 3000
    to_port         = 3000
    protocol        = "tcp"
    security_groups = [aws_security_group.webapp_lb.id]
  }
}

resource "aws_security_group" "webapp_lb" {
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

output "webapp_ecs_security_group" {
  value = aws_security_group.webapp.id
}

output "webapp_lb_security_group" {
  value = aws_security_group.webapp_lb.id
}
```

Fargate CLI で使う値は、 Terraform の `output` 機能で、簡単に取得できるようにしています。

### Fargate CLI

次に、 Fargate CLI でサービスを作成します。

Fargate CLI のインストールは、下記からバイナリをダウンロードしてください。

https://github.com/awslabs/fargatecli/releases

やや長いのでスクリプトにしました。

`aws-credentials.ini` の置き場所やリージョンはプロジェクトによると思うので、適宜置き換えて下さい。

```bash:create.sh
#!/bin/bash

export AWS_SHARED_CREDENTIALS_FILE=$PWD/aws-credentials.ini
export AWS_PROFILE=default
export AWS_REGION=us-east-1

# 先に ALB を作成する
fargate lb create webapp \
    --cluster webapp \
    --port HTTP:80 \
    --security-group-id `terraform output webapp_lb_security_group`

# 次に Fargate のサービスを作成する
fargate service create webapp \
    --cluster webapp \
    --lb webapp \
    --num 1 \
    --port HTTP:3000 \
    --cpu 256 \
    --memory 512 \
    --security-group-id `terraform output webapp_ecs_security_group`
```

`fargate service create` コマンドには、 `--image` で ECR を明示的に指定することもできますが、何も指定しないことで

- 現在のディレクトリにある `Dockerfile` をビルド
- ビルドした Dockerfile を ECR にプッシュ
- その ECR をイメージに指定してサービスを作成

という挙動をしてくれます。

### 確認する

サービスができたことを確認します。

```bash
$ fargate lb list --cluster webapp

NAME    TYPE            STATUS  DNS NAME                                        PORTS
webapp  Application     Active  webapp-xxxxxxxxxx.us-east-1.elb.amazonaws.com   HTTP:80
```

出力の `webapp-xxxxxxxxxx.us-east-1.elb.amazonaws.com` を開くと、サービスができていると思います。

### 作成されたもの一覧

By Terraform:

- ECR Repository
- ECS Cluser
- EC2 Security Group

By Fargate CLI:

- ECS Task Definition
- ECS Service
- ECS Task
- ECS Task Execution Role
- ALB
- ALB Target Group
- CloudWatch Log Group

### 更新する

```sh
$ fargate service deploy webapp --cluster webapp
```


### 消す

残念ながら Fargate CLI 自体には冪等性はありませんが、消すことはできます。

```sh
$ fargate service scale webapp 0 --cluster webapp
$ fargate service destroy webapp --cluster webapp
$ fargate lb destroy webapp
$ terraform destroy
```

Note: ECS Execution Role will not be deleted.

# まとめ

簡潔になりましたが、これを基にデプロイのスクリプトなども容易に作れると思います。

下記のデモのリポジトリでは、 Makefile や fargate コマンドのラッパーを使って、より簡潔にしています。良かったらご覧ください。

https://github.com/acro5piano/terraform-fargate-example