docker compose cliでecsにデプロイした話

TL; DR

  • Phoenixでサーバーを書いた
  • docker compose の ecs integrationでデプロイした
  • AWSのいろんなサービスがいい感じに起動して、便利

docker compose ecs integrationとは

2020年11月に発表されて、待望の機能が来たと結構な反響があったように思います

仕事でPhoenixのアプリを作る機会があり、そこでこのスタックを採用したのでハマったところを紹介していきます

概要、必要なこと、アーキテクチャなどはほぼ以下のドキュメントに書いてあるのでそちらをご覧ください

docs.docker.com

ふわっとまとめると、docker-compose.ymlに基づいてAWSのリソースを良い感じに用意してくれるコマンドです

ハマったところ

ECSって何、ECRって何

本当に無知でdockerをデプロイできるサービスぐらいにしかECSを理解していなかった

そのため、ECRって何という状態だった

  • ECSはElastic Container Service
  • ECRはElastic Container Registry

ECRはDocker HubみたいにDocker imageをhostingできるサービス

ECSはclusterを作成して、taskを定義して、serviceを実行するものっぽい

clusterはEC2なんかのコンピューティングリソース

taskはその名の通り仕事、実体的には単数 or 複数のdockerのcontainer定義

serviceはtaskをclusterで何個走らせるかを決める

これらを統合して、docker compose cliを使うとデプロイはこんな感じになるはず

  1. サーバーを書く
  2. buildしてDocker imageに固める
  3. ECRなどのDocker image hosting serviceにpushする
  4. docker compose up

port:80が使えない

Unix系のOSにて、デフォルトでは1024以下のportはroot権限がないと使用できないらしい

これを回避する方法は以下

  1. nginxでリバースプロキシを立てて、アプリサーバーを直接ALBに紐付けない
  2. rootユーザーでアプリサーバーを起動する
  3. port mappingする

2はセキュリティ的に推奨される感じではなさそうです

3はecs integrationではサポートされていません

このあたりはいかに詳しいです

別のcontainerで立つ以上portのabstractionは利がない & compose specがroutingに関することに関与しないのでECS integrationでもそれを踏襲する方針のようです

この事実が分かるまで、いつものlocal開発向けのdocker-compose.ymlのように80:4000をやろうとしてdocker compose upでエラーを踏んでissueを漁りドキュメントを読むというのを繰り返しました

ということで、素直に1でいきます

nginx.confを書いて、アプリサーバーと同じくbuildしてECRにpushして解決

その際に、後述のサービス検出の話でハマりました

サービス検出

英語だとService Discovery

恥ずかしながらService Discoveryが何を指しているかさっぱり分かっていなかったが少し理解した

nginxでリバースプロキシを立てたが、どうやってnginxに来た通信をpPhoenixのアプリサーバーに向けるのかという話

つまり、ECSでcontainer間通信を実現する方法が分からなくてハマりました

実はドキュメントに書いてあります

Deploying Docker containers on ECS | Docker Documentation

Services are registered automatically by the Docker Compose CLI on AWS Cloud Map during application deployment. They are declared as fully qualified domain names of the form: <service>.<compose_project_name>.local.

app.hoge-project.localのようなドメインでサービスのcontainer間で通信ができるようになります

当初やりたいことがService Discoveryであることが分かっていなかったため、いろんなクエリでググった末に以下のページにたどり着いてようやく理解できました

Amazon ECS サービスでタスク間の通信を許可する

ということでnginx.confにphoenixのアプリサーバーのドメイン名を書いて完了

Migration

これはハマったというよりもハックで乗り切った感じの話

基本的にサーバーを書いていると発生するMigrationですが、それをECSのサービスとして定義するとdocker compose cliの仕様上、ちょっと困ったことになります

docker compose convertをすると分かりますが、service.deploy.replicasに値を設定しないとECSのサービス定義のdesiredCountは1にデフォルトで設定されます

そのため、DB migrationのサービスが起動 -> 正常終了 -> desiredCountが1なので、再度起動 -> 正常終了のループを延々と繰り返します

最初はdocker compose converで生成されたCloudFormation templateをカスタマイズしようと思いましたが、上手いこといかなかったので方針を切り替えました

結果、docker compose upを走らせた後に、migrationの終了を確認してからマネジメントコンソールでNumber of Taskを0にするように手動で回避しています

この辺、上手い方法があったら教えていただけると 🙏

Command support

ecs contextでsupportされているコマンドは筆者が試した限りでは以下だけでした。

  • docker compose up
  • docker compose down
  • docker compose logs
  • docker compose convert

runが使えないので上述のDB migrationで困ったりしてました

runはsupportしてほしいという要望が既に上がっています

Add docker run support to ECS · Issue #829 · docker/compose-cli · GitHub

ちなみにdocker compose downをするとupで作成されたすべてのAWSリソース(既存のリソースは除く)が破棄されるのでお気をつけください

既存のAWSリソースの使用

ここに説明があります

Deploying Docker containers on ECS | Docker Documentation

VPCだけでいけるかと思いましたが、ドメインのヒモ付のため、LBも既存のものを使うようにしました

最後に

docker compose ecs integration、まだ荒削りな部分もありますが、production運用も十分に視野に入ってくると思います

もちろん、凝ったことをやろうとすると足りない部分も多いのでそのあたりはissueに起票したりでfeedbackしていくと良さそうです

これで、好きな言語やフレームワークでサーバーをcontainerにしてデプロイするのが簡単になりました

どんどん使っていきたいところ

おしまい