Amazon Elastic Container Service (ECS) を Terraform で素振り

ECS を Terraform で素振りしたメモ。Fargate 前提です。残骸はこちら

Fargate の費用

ECS は EC2 でクラスタを作るか Fargate かを選択できます。Fargate の方がマネージドなので楽ですが割高です。

ざっくりと EC2 の t3 インスタンスを比べてみます。Fargate の費用は以下のとおりですが。

AWS Fargate の料金

  • CPU → per vCPU per hour 0.0632USD
  • MEM → per GB per hour 0.0158USD

EC2 の t3 の費用と比較すると次のとおりです。

タイプ vCPU MEM USD/時間 Fargate
t3.nano 2 0.5 GiB 0.0068 0.1343
t3.micro 2 1 GiB 0.0136 0.1422
t3.small 2 2 GiB 0.0272 0.1580
t3.medium 2 4 GiB 0.0544 0.1896
t3.large 2 8 GiB 0.1088 0.2528
t3.xlarge 4 16 GiB 0.2176 0.5056
t3.2xlarge 8 32 GiB 0.4352 1.0112

Fargate がメモリに比べて CPU が高い?ため、小さめのインスタンスだと Fargate がかなり割高ですが、大きめのインスタンスだとだいたい2倍程度です。

ただし、EC2 はこれとは別に EBS の費用も必要です。Fargate のストレージはタスクあたり 10GB(コンテナあたり?)+ボリュームマウント用に 4GB と固定なものの、追加の費用はかからなさそうです。また、EC2 だと Rolling アップデートや Blue/Green デプロイしようとするとある程度の余剰(Blue/Green なら倍)リソースが必要になります(EC2 でもそのときだけスケールさせればいいのかもしれないけどデプロイがかなり辛くなりそう)。

なお、Fargate の最小構成は 0.25 vCPU / MEM 0.5 GB なので、最小でも .0237 (USD/時間) なので t3.nano と比べてだいぶ高いです。

クラスタとサービスとタスク

タスクは複数のコンテナによって構成されていて、事前に作成されたタスク定義をひな型として実行されます。ひとつのタスクの中の複数のコンテナは同じノードで実行されます。Fargate で実行するときのネットワーキングタイプの awsvpc だとタスクごとに ENI がアタッチされます。

サービスは↑のタスクが指定数が実行され続けるように維持したり、タスクのポートをELB のターゲットグループへ登録したりします。

クラスタはタスクを実行するためのコンピューティングリソースです。

ぐぐるともっとわかりやすい説明がたくさんあります。

タスクロールとタスク実行ロール

ECS でサービスやタスクを実行するとき、「タスクロール」と「タスク実行ロール」の2つのロールを設定します。

「タスク実行ロール」はタスクを実行するためのロールで(そのまんま)、 ECR からイメージを Pull したり、ログを CloudWatchLogs に記録するために使用されます。マネジメントコンソールで操作するのであれば基本的に自動で作成されるもので問題ありません。Terraform なら

「タスクロール」は実行されるコンテナに付与されるロールです。EC2 のインスタンスプロファイルみたいなものです。

新しいイメージのデプロイ

イメージを Dockerhub にプッシュしたあと、サービスの更新で「新しいデプロイの強制」を ON にして更新すると、新しいイメージがプルされてコンテナが開始され、古いコンテナが停止されます。AWS CLI でも相当のことはできます。

デフォルトだと Rolling update なので新旧のコンテナが混在します。試していないですが Blue/Green にもできます。

Terraform 単体だと・・・イメージをビルドして Dockerhub にプッシュするたびに、イメージのタグを変更し、tf ファイルのタスク定義を書き換えるしか無いですかね。あるいはタグは latest にして、デプロイには AWS CLI を使うとか。

タスクのスケジューリング

ECS のマネジメントコンソールに「タスクのスケジューリング」というのがあって、特定のタスク定義から Cron 風に定期的にタスクを実行させたりできます。ただし、これは実は CloudWatch Event のターゲットとして ECS タスクを指定しているだけなので、AWS CLI や Terraform で設定するときは CloudWatch Event の方を設定します。

プライベートサブネット

ECS サービスをプライベートサブネットに入れる場合、Docker イメージをプルしたりログを CloudWatch Logs に送信したりするために NAT ゲートウェイなり PrivateLink なりが必要です。 以下によると ECS からのイメージのプルには ECR の 2 つのエンドポイント以外に S3 も必要なようです。

ECS CLI

ECS のクラスタやタスクの作成や更新を行うための CLI ツールです。

ecs-cli up で VPC とサブネットを作成できます。が、サブネットをフロントとバックで分けたりセキュリティグループを細かく設定したりしようとすると ECS CLI だけでは完結できないので VPC などは Terraform で作っとけば良いように思います。

docker-compose.yml ファイルでタスクを定義をして、VPCやサブネットなどの ECS 固有のパラメータを ecs-params.yml で指定し、ecs-cli compose service up でサービスを開始できます。

ecs-cli compose service up \
    --cluster hello-ecs-cluster \
    --launch-type FARGATE \
    --create-log-groups \
    --target-group-arn arn:aws:elasticloadbalancing:ap-northeast-1:999999999999:targetgroup/hello-ecs-http/9999999999999999 \
    --container-name app \
    --container-port 80

普段 docker-compose を使っていれば docker-compose.yml に慣れ親しんでいるので良いのですけど・・・ELB(ALB)との紐付けは ECS CLI のコマンドラインオプションで指定するしかない?

ecs-cli configure で IAM アクセスキーなどの認証情報やデフォルトのクラスタ・起動タイプ(Fargate/EC2)・リージョンなどを設定します。ただ、認証情報は AWS CLI のために設定した認証情報もそのまま使えるので ECS CLI 用に新たに設定する必要は無いと思います。デフォルトのクラスタや起動タイプも都度コマンドラインオプションで指定しても良いように思うので、ecs-cli configure しなくても良いような気もします。

うーん・・・ Terraform で十分な気がする? VPC やサブネットの ID を ecs-params.yml にベタ書きする必要があるし。

強いて言えば ECS CLI ならタスクの強制リスタート(新しいイメージをデプロイしたあとにそのイメージで起動し直す)とか、ecs-cli compose service ps コマンドでタスクの一覧をさっと見たり、ecs-cli compose run でタスクを one-shop で実行したり、ECR から pull/push も ECS CLI からできるので、Terraform と併用すると良いかも?

さいごに

マネジメントコンソールを触っていると、チュートリアル代わりなのだと思いますが「今すぐ始める」で CloudFormation でばこーんと一通りの環境を立ち上げてお試しすることができます。

また、「今すぐ始める」を使わなくても、クラスタを作成するときに一緒に VPC も作成できたり(これも CloudFormation だったと思う)、サービスを作成するときに一緒に ELB も作成できたり(これは CloudFormation ではなかったと思う)、至れり尽くせりなのですが、逆にどこでなにが作成されているかわかりにくいので、お試しで使う以外ではこれらの便利作成機能は使わなくて良いと思います。

Terraform で作成してみましたが、Fargate なら VPC(とそれに紐付くいろいろ)の作成が一番めんどくさくて、ECS 固有のものはタスク定義が Terraform の中にさらに JSON を書く必要があって微妙なの以外は難しいところはなさそうです。ただ EC2 でクラスタを組むのと比べると制限もあります。

Fargate だと Docker Volumes が使用できません。ので EFS をマウントして永続化ボリュームにしたりできません。Fargate では永続化ストレージには RDS とか S3 とサービスを使うしか無いようです。そもそも EFS のようなファイルシステムが必要という時点でなにかおかしいという意見もあると思いますが。

Fargate だとネットワーキングは awsvpc で固定です、host や bridge は使用できません。ただ awsvpc で十分な気もします。host や bridge でなければ困るようなユースケースあるかな? 強いて言えば awsvpc だとタスクごとに ENI が作成されるのでタスクをたくさん作ると ENI の上限にかかりやすいようです。

でもたいていのユースケースで Fargate で問題ないと思うし、スケールのために EC2 もオートスケールさせるのは Fargate と比べて面倒くさすぎるし、基本的に Fargate で良いと思う。

.

.

.

と思ったけど、オートスケールせずに固定的にリソース確保するのなら EC2 でクラスタ作るのでも良いかも。その場合 Blue/Green はまあ無理だけど、素のままでも Rolling Update できて Cron の冗長化も考えなくて良くなるなら、それだけでも素の EC2 と比べれば十分メリットはあるような気がする。

Fargate だとホストに SSH できないので、なにか問題があるときの調査がめちゃくちゃ困難だし。ただし、その場合でも EC2 インスタンスの可用性や手動スケーリングの容易さのために Auto Scaling Group は使っておいて良いと思う。

.

.

.

いやまあでも Fargate の制限は MySQL on EC2 に対する RDS for MySQL の制限みたいなものだと思えば Fargate 一択という気もする