DockerfileからWebアプリケーションを手軽にAWSへデプロイする方法3選

先日LightsailがDockerイメージからのデプロイをサポートしました。
https://aws.amazon.com/jp/blogs/news/lightsail-containers-an-easy-way-to-run-your-containers-in-the-cloud/

AWS上でのコンテナサービスはECS/EKSをはじめ様々ありますが、今回のLightsailコンテナは、シンプルなアプリケーションを簡単に提供することに特化したサービスと言うことが出来そうです。
また、ECSのデプロイツールであるCopilotもGAとなりました。
こちらもとても簡単にECS環境が構築できるということで注目です。

そこで、現在AWSでDockerイメージから手軽にWebアプリケーションをデプロイする方法をいくつか比較してそれぞれの使いどころなどを考えてみたいと思います。

今回は手軽にデプロイが可能な方法として以下の3つについて比較します。

- [Lightsail](https://aws.amazon.com/jp/lightsail/)
- [Beanstalk (Dockerプラットフォーム)](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/docker.html)
- [Copilot (ECS)](https://aws.amazon.com/jp/blogs/news/introducing-aws-copilot/)

## Lightsail

Lightsailのコンソールからコンテナサービスを起動することで、All-in-OneのWebアプリケーションが構築できます。

### デプロイ手順

参考: [プライベートなコンテナイメージを Amazon Lightsail コンテナサービスで使う | Developers.IO](https://dev.classmethod.jp/articles/how-to-use-private-images-with-amazon-lightsail-container-service/)

詳細な手順は上記参考記事を見て頂くとして、必要な作業ステップは

1. 事前準備
    1. AWS CLI v2のインストール
    2. lightsailプラグインのインストール
2. DockerイメージをLightsailにpush
    3. ローカルで`docker build`を実行
    4. AWS CLIで`aws lightsail push-container-image ...`を実行
5. デプロイ
    6. push済みのイメージからデプロイするイメージを選択
    7. インスタンスサイズやPort、環境変数などを設定

となります。

コンテナレジストリがアプリケーションにセットで付いていてPushとデプロイがそれぞれ行えるようなイメージですね。
新しいイメージをデプロイした結果ヘルスがNGになった場合は自動で以前の正常なコンテナにロールバックもしてくれるようです。

### AutoScaling

インスタンス数の変更やスケールアップはボタン1つで簡単に実行可能ですが、
今のところ、AutoScalingには対応していないようです。

### メトリクス

CloudWatchアラームにも今のところ対応していないようですが、
簡単なCPUとメモリの平均のみは確認出来ます。
![metrics.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204890/7d6f542f-6677-491e-bd5b-da8fdd9c76b8.png)

### ログ

CloudWatch logsにログを流すことは出来ないようです。
Lightsailコンソールからのみログを確認することが出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204890/27b9e2f0-76ff-5d21-4ddc-3aa74b8df6db.png)


### VPC接続

今のところVPC接続が出来る様子はありません。
あくまでコンテナで簡潔するケース、publicにアクセス出来るサービスとの連携のみで足りるケースでの利用を想定した方がよさそうです。
private subnetにいるRDSと連携するような用途は今のところ難しいのかも。

### まとめ

- ローカルでbuildしたイメージをPushしてデプロイするだけ。というのはとてもシンプル
- ロードバランサーも含まれていてある程度の可用性もあるのもよい
- 監視やAutoScalingがちゃんと欲しい場合、RDSにつなぎたい場合など、本格的なサービス提供に使うのは難しそう

## Elastic Beanstalk (Docker platform)

BeanstalkのプラットフォームでDockerを選択することで、プラットフォームとしてDockerEngineを動かしてその上でコンテナを起動して動かすことが出来ます。

参考: [Docker プラットフォームの使用](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/docker.html#single-container-docker.deploy-local)

### デプロイ手順

デプロイまでのステップはおおよそ以下の流れになります。

1. 事前準備
    1. Dockerプラットフォームを選択したBeanstalkの環境を作成する
2. Beanstalkの構成定義を作成
    3. Dockerrun.aws.jsonを作成  
    4. Dockerfile,Dockerrun.aws.json,Dockerのbuildに必要なファイルを含むArtifact一式を構成
5. デプロイ
    6. `eb deploy`などのコマンドでデプロイ

#### Dockerrun.aws.jsonサンプル

```
{
  "AWSEBDockerrunVersion": 1,
  "Ports": [
    {
      "ContainerPort": 8000
    }
  ]
}
```

#### deploy.zipサンプル

```
deploy.zip
 |- Dockerfile
 |- Dockerrun.aws.json
 |- index.html
```

DockerfileとDockerrun.aws.jsonを含むzipファイルを作成し、それをデプロイすることで、各インスタンスで`docker build` & `docker run`が実行されコンテナを起動することが出来ます。

ECR等のコンテナレジストリからPullすることも出来ますが、今回想定している簡単なDockerイメージのデプロイという用途としてはこれで十分なケースもあるかと思います。

環境が構築出来れば、後は一式のzipファイルをデプロイするだけですのでその後の展開は容易です。

### 運用

Beanstalkの場合はEC2インスタンス上でDockerホストが起動してコンテナが立ち上がる構成になるため、標準的な運用インフラはほぼほぼそのまま使えることになります。
EC2単独での実行、ALBとAutoScalingグループを含んだ冗長構成、CloudWatchとの連携やVPC、SecurityGroupの設定など個別のカスタマイズが可能です。
ただし逆に言うとカスタマイズ箇所が多いということでもあり、手軽さという点では一歩劣ります。
EC2インスタンスで動く関係上、Fargateのように細かなCPU/メモリスペックを指定してサービスを構築することは出来ません。

### まとめ

- Beanstalkの仕組みに乗る形でコンテナサービスを構築できる
    - Beanstalkの運用に慣れている人には向いている
- 必要な機能は一通りカスタマイズして使える
- EC2インスタンス上で動くためECS Fargateほどの柔軟性はない

## Copilot (ECS)

CopilotはECSを構築するためのCLIツールで、Copilotが提供するフレームワークに沿った形でサービスを定義して構築することで簡単にECSクラスタやその他必要なリソースを一括作成してくれます。

参考: [AWS Copilot のご紹介 | Amazon Web Services ブログ](https://aws.amazon.com/jp/blogs/news/introducing-aws-copilot/)

最近ついにv1.0.0がリリース:tada:されました。

### 手順

参考: [ECSのオペレーションを劇的に簡略化するAWS Copilotが発表されました！ | Developers.IO](https://dev.classmethod.jp/articles/aws-copilot/)

詳細な手順は例によって上記記事が大変詳しいです。いつもありがとうございます。
必要なステップは下記の通りです。

1. 事前準備
    2. copilotのインストール  
3. 環境の構築
    4. Applicationの作成
    5. Environmentの作成
    6. Serviceの作成
    7. Manifestの定義
8. デプロイ

#### Applicationの作成

Getting Startedとしては記事の通り`copilot init`で対話的にセットアップするのが早くて便利ですが、細かくカスタマイズしたい場合は`copilot app init`で始めることで空のアプリケーションを作ることも出来ます。

`copilot app init`コマンドではApplicationの登録と、Copilotから諸々操作するのに必要なRoleだけが作られます。
この時Applicationが登録されるAWSアカウントはaws configureでdefaultに紐付けられたアカウントか、環境変数`AWS_PROFILE`で指定されたアカウントのようです。
`--profile`パラメータは指定出来ません。

#### Environmentの作成

`copilot env init`コマンドでdev/stg/prod/testといった環境を作成出来ます。
ここでは`--profile`パラメータも指定出来るため、prod環境だけ別のAWSアカウントへデプロイするといったことも出来そうです。
また、ここでVPCやサブネットを指定することで既存のVPCにタスク定義を配備することも出来ます。

```
copilot env init --name prod --profile prod-web \
  --import-vpc-id vpc-xxxxxxxx \
  --import-public-subnets subnet-aaaaaa,subnet-bbbbbbbbb \
  --import-private-subnets subnet-ccccccccc,subnet-ddddddddd
```

#### Serviceの作成

`copilot svc init`コマンドでサービスを定義出来ます。
ここでDockerfileを指定しておきます。

```
copilot svc init --name api \
  --svc-type "Load Balanced Web Service" \
  --dockerfile ./Dockerfile
```

このコマンドで、指定したDockerfileをbuildしてpushするためのECRリポジトリも合わせて作成されます。

#### Manifestの定義

サービス作成でプロジェクト内に生成された`${service-name}/manifest.yml`を編集して必要なインスタンスサイズ等を定義します。

##### manifest.ymlサンプル

```
http:
  path: '/'
  healthcheck: '/health'
cpu: 256
memory: 512
count:
  range: 2-3
  cpu_percentage: 70
```

range指定でAutoScalingを設定することも出来ます。

#### デプロイ

`copilot deploy`コマンドで、指定した環境にデプロイが開始されます。
ローカルでdocker buildしたイメージをECRにプッシュし、ECSクラスターを作成し、タスク定義を作成し、サービスを開始するというところまで一気に実行されます。

Lightsailと比較するとさすがに設定項目は多いですが、ECSクラスターを手で構築する手間を考えるとCLIコマンド3つ4つとmanifestファイルの定義のみでWebサービスが立ち上がるのは相当に楽です。
Beanstalkの各種設定と比較してもかなり楽に思えます。

注意点としてはまだGAしたばかりということで細かいところではうまく動かないところもありそうですが今回みたいようなシンプルなケースでは概ね問題なさそうでした。

### まとめ

- ECSを少し抽象化したフレームワークのような感触でWebサービスを立ち上げることが出来る
- ECSの構築に必要なロールの扱いなどを意識しなくてよい

## 料金比較

ここではそれぞれの方法について同程度スペックでの料金比較するため

vCPU : 1
メモリ : 2GB
リージョン : ap-northeast-1
インスタンス数 : 2

の条件で1ヶ月動かした場合の料金を試算してみました。
データ転送量その他の料金は除いた概算です。(2020/12現在)

### Lightsail

プランMedium x2 : \$80

### Beanstalk

EC2 (t2.small) x2 : \$49
ALB: \$17
合計: \$66

### ECS

Fargate: \$35
ALB: \$17
合計: \$52

同条件で比較するとLightsailが意外と安くないなという感じになりますね。

あくまで同条件での見積もりになるので、
小規模な場合はどうかというケースで例えばt4g.nano(2vCPU,0.5GB)あたりにするとBeanstalkは\$30ほどになりますが、Lightsailではnanoプラン(0.25vCPU,0.5GB)では\$14まで下げることができます。

ALBがコミというところにLightsailの料金的な強みがあるので、小規模の方が相対的にLightsailに分があり、規模が大きくなるほどECS Fargateに移行する方がメリットが出てきそうです。

## 結論

小規模で簡単なアプリケーションを単独でホストしたいならLightsailはとても強いサービスと言えそうです。
一方で出来ないことも多く、本格的なサービス運用にはさすがに向かなそうですので、その場合はCopilotを活用してECSでサービス提供するというのは手軽さを考慮してもとても良さそうに思えます。

Beanstalkによるアプリケーション構築は、Beanstalkでの構築に慣れている人にとっては十分簡単ですが、他の2つと比較すると相対的に重たい手順になってきました。
まとめ始めた時は手軽さ⇔高機能の関係が`Lightsail < Beanstalk < Copilot`みたいな順になるかなと想像していましたが、思ったよりBeanstalkのメリットが小さくなっていましたね。

少し前までは「とはいえECSは初期セットアップもそれなりに大変ですし学習コストもあるので、簡単なアプリケーションをホストしたいだけであればBeanstalkも便利ですよ」などと推していたりもしたのですが、いよいよBeanstalkのDockerプラットフォームは役割を終えたと言える日が近そうです。

Copilotは先日1.0.0がリリースされたばかりということでこれからもっとECSが使いやすくなっていくと思われるので今後も期待していきたいです。