自分が開発するアプリがAWS ECSに乗っかってるなら、できれば知っていて欲しいこと
想定読者
- 自分が開発しているシステムの稼働環境がAWS ECSであることは知っている
- ECSを触る機会がなく、ECSの全体像をイメージできない
ECS(Elastic Container Service)
ECS(Elastic Container Service)は、アプリケーションをコンテナ(例えばDocker)として実行するためのマネージドサービスです。
ECSを使えば、インフラの管理を最小限に抑えつつ、コンテナアプリケーションのデプロイやスケーリングを効率よく行うことが可能です。
もし自分が開発するアプリがECSにデプロイされているなら、以下の各要素の役割や基本的な流れを押さえておくと、トラブル時や開発効率の向上に役立つかもしれません。
ECSの構成要素
稼働するコンテナやタスク定義、リポジトリが1つのみの場合、構成要素は以下のようになります。
この図を確認しながら、各要素の説明を読んでみてください。

タスク定義
「タスク定義」は、ECSで実行したいコンテナ群とその設定内容を記述したJSON(またはYAML)ファイルです。
アプリがどんなDockerイメージから起動するのか、どんなコマンドで動き出すのか、CPUやメモリの割り当て、環境変数、ポートの公開設定などをここで指定します。
タスク定義の変更(例:新バージョンのイメージ使用や環境変数の追加)は、新しいリビジョンとして記録されます。
ECR(Elastic Container Registry)
AWSでは「ECR」というコンテナレジストリサービスが用意されており、多くのケースでここにアプリのイメージをpush(格納)し、ECSのタスク定義からこのイメージをpull(取得)してコンテナ実行を行います。
イメージをpushする際にイメージタグを設定しており、そのイメージタグをタスク定義に設定することでどのイメージを取得するかを指定することができます。
クラスター
ECSの「クラスター」とは、タスクやサービス(=コンテナ群)を実行するための論理的なグループです。
クラスターにはEC2インスタンス(EC2起動タイプの場合)やAWS Fargate(サーバーレスな実行環境)を定義し、そこにタスクをデプロイします。
タスク
タスク定義の内容をもとに作成するコンテナ群です。
スタンドアロンタスク
スタンドアロンタスクは、一時的に1回だけ起動し、用事が済んだら終了させたいバッチ処理やメンテナンス用の実行によく使われます。
繰り返し実行の必要がない場合や、サービス管理の外で単発実行したいケースに便利です。
このような用途でも使えるという意図でタスクと分けて説明していますが、実体はタスクになります。
サービス
ECSでは「サービス」としてタスクを立ち上げることで、常に決まった数のコンテナを保って実行し続けることができます。
例えばWeb APIであれば、「常に2つのタスクを起動し続ける」といった構成が可能です。
また、サービスに紐づけたALB(Application Load Balancer)を通して、複数タスクへの負荷分散やヘルスチェックも行えます。
スケールイン/アウトも柔軟に設定可能です。
デプロイ手法
ローリングアップデート
ECSの標準的なデプロイ手法でコンテナを1台ずつ切り替えます。
例えばサービスに2つのタスクが稼働している場合があったとします。
新タスクが立ち上がり、ヘルスチェックが通ると、新タスクが稼働し、旧タスクは停止します。
この流れをタスクの数だけ順次行います。
ブルーグリーンデプロイ
稼働中コンテナと新稼働コンテナが一時的に共存し、接続先を切り替えて新旧コンテナを入れ替えるデプロイ手法です。
ECSでブルーグリーンデプロイを行う場合は、CodeDeployを使用する必要があります。
トラフィック切り替え、旧コンテナ停止を経てデプロイ完了とし、デプロイに問題があれば稼働中コンテナにロールバックすることが可能です。
ブルーグリーンデプロイの流れ
サービスに1タスク(この例ではNext.jsとAPIが起動するコンテナ群)がある場合を想定し、ブルーグリーンデプロイをしたとします。
TargetGroupとはALB(ロードバランサー)の受け口のようなものです。
デプロイ前の状態はこのようなイメージです。

稼働中のTargetGroupとは別のTargetGroupに紐づく新タスクが立ち上がります。
このときポート番号は8080などのテスト用ポートで新タスクに接続することを想定しています。

新タスクにトラフィック切り替えをすると、新タスクが稼働中のタスクとして切り替わります。

旧タスクを停止して、デプロイは終了です。

ユースケース
よくあるユースケースからECSに関連する要素のどこを操作するべきなのかをまとめてみました。
コンテナを再起動したい
サービスには再起動という概念がないため、サービスを更新することになります。
サービスを更新するということは新タスクが立ち上がり、旧タスクが停止するため、デプロイをしていることと同義になります。
バッチ処理をしたい
スタンドアロンタスクを使います。
必要に応じてタスク定義のコマンドを上書きします。
containerOverridesオプションを使えば、コマンドの上書きができます。
コンテナイメージを更新したい
タスク定義にあるコンテナのイメージタグを更新します。
imageという項目がありますが、ECRを使用している場合は[12桁の数字].dkr.ecr.ap-northeast-1.amazonaws.com/[コンテナ名]:[イメージタグ]という値を設定することになると思いますので、[イメージタグ]を変更して、タスク定義を作成します。
サービスで稼働中のタスクを更新する場合は作成したタスク定義を指定して、サービスを更新します。
環境変数を更新したい
タスク定義にある環境変数の設定値を更新します。
手動で設定値を入れても問題ないですが、AWS Secrets ManagerやAWS Systems Manager Parameter StoreのARNを指定すれば、その値を読み込むことも可能です。
サービスで稼働中のタスクを更新する場合は作成したタスク定義を指定して、サービスを更新します。
コンテナ台数を調整したい
サービスの設定に必要なタスクと自動スケーリングに関する設定があるのでその値を調整します。
調整後はサービスを更新します。
CPU、メモリを調整したい
タスク定義にcpu、memoryの項目があるのでそれを調整します。
タスク単位でcpu、memoryを共有しているので、複数コンテナを動かす場合は注意が必要です。
タスクが使用できるAWSのサービスを調整したい
タスク定義にtaskRoleArnという項目があります、そこに指定されているIAMロールの権限を調整してください。
IAMロールの権限調整についてはここでは割愛します。
稼働しているタスクのコンテナに接続したい
ECS Execを使用してください。
ECS Execをサービスで稼働しているタスクに行う場合は、サービスの設定変更とサービス更新が必要です。
AWSコンソール上では変更できないのでAWS CLIを使う必要があります。
update-serviceコマンドの--enable-execute-commandオプションを使って、サービスの設定を更新し、サービスを更新し直すとECS Execができるようになります。
スタンドアロンタスクはタスク起動のrun-taskコマンドに--enable-execute-commandオプションを設定すれば、ECS Execができるようになります。
まとめ
自分のアプリがECS上で稼働している場合、「タスク定義」「ECR」「クラスター」「タスク」「スタンドアロンタスク」「サービス」そして「デプロイ手法」といった各コンポーネントの役割やつながりを一度は押さえておくと、トラブル対応やバージョンアップ時も安心です。
AWSの公式ドキュメントや、実際のECSコンソールを触りながら、それぞれの設定や挙動を確認してみるのもおすすめです。
Discussion