🐙

Amazon ECS の便利な設定 in 2024

2024/08/15に公開

Amazon ECS は、AWS でコンテナをホストしサービスを展開することができる方法の 1 つです。
AWS では他に EKS や App Runner、 Lambda が選択肢としてありますが、ECS はマネージドでありながら、細かくカスタマイズできる点が良いと思います。
特に ECS のコンピュートリソースに Fargate の他に EC2 を選ぶことができ、実行環境をカスタマイズするためやコストの観点から EC2 を利用できるのが嬉しいです。
また EKS だとクラスターのコントロールプレーンに料金が発生しますが、ECS 自体の利用には料金が発生しないため、気軽に試すことができるのも特徴です。

この記事では、既に ECS を使っている方で何かより工夫できそうな設定を知りたい!という方に向けて、いくつか便利な設定を紹介します。
また、まだ ECS を触ったことがないけどこの機会に触ってみたい!という方向けに、OpenTofu を使った最小構成をご用意しています。ぜひご利用ください。

https://github.com/defaultcf/get-started-ecs-in-2024

なお、ECS のコンピュートリソースには EC2 を利用するものとします。

セットアップ

AWS CLI や OpenTofu などのツールのインストールや管理に aqua を使用します。あらかじめ入れておいてください。
https://aquaproj.github.io/

aqua i
tofu init
tofu apply

これで VPC の作成から ECS のサービス作成まで行われます。
構成図

ECS 内の用語などの解説はここでは省略します。適宜公式ドキュメントなどをご参照ください。
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/Welcome.html

Capacity Provider で managed draining を有効化する

EC2 AutoScaling を更新した時などに、一時的にタスク数が要求した数より下回ることがあります。
その時に EC2 インスタンスの終了を少し待たせて、その間に新しいインスタンスを立ち上げコンテナを要求数まで立ち上げてから、古いインスタンスを終了する、ということができます。それが managed draining です。
古いインスタンスからタスクを drain(排出)し、そのインスタンスには新たなタスクはデプロイされなくなります。また drain が始まると ALB から切り離され、エンドユーザーがそのタスクにアクセスすることはなくなるため、エラーに遭遇することもなくなります。

実はこの機能は2024年1月に登場したもので、それまで drain の実現のためには各自 Lambda 関数を書くなどして実装する必要がありました。
https://aws.amazon.com/jp/blogs/compute/how-to-automate-container-instance-draining-in-amazon-ecs/

それが今や Capacity Provider を登録する際に、managed draining を有効化するだけです。
サンプルコードではここで設定しています。
https://github.com/defaultcf/get-started-ecs-in-2024/blob/79e7832900c78fae13b7d7014ea30e4667a04539/ecs.tofu#L20

(本機能は、生産性向上チームが出している Productivity Weekly でもご紹介しました!)
https://zenn.dev/cybozu_ept/articles/productivity-weekly-20240124#amazon-ecs-enables-easier-ec2-capacity-management%2C-with-managed-instance-draining-|-containers

スポットインスタンスの利活用

EC2 スポットインスタンスを利用することで、インスタンス 1 台につき 7 割ほどコストを削減できます。
そのトレードオフに、スポットインスタンスは AWS 側のキャパシティ低下時に突然終了します。厳密には、インスタンスの終了 2 分前にイベントが送信され、その後終了します。

スポットインスタンスの終了によるサービスの可用性低下を避けるため、AutoScaling や ECS のキャパシティプロバイダーを調整する必要があります。
それには次のようなものが考えられます。

  • 先述したキャパシティプロバイダーの managed draining を利用することで、インスタンス終了前にタスクを drain し、別のインスタンスや新しいインスタンスでタスクを立ち上げ、可用性を維持します
  • AutoScaling の「可用性の設定」にて、最小正常率を100にすることで、インスタンス数を常に要求数かそれ以上にします

これにより、コストを抑えつつ、可用性を保った ECS の利用が可能になると考えます。

起動テンプレート更新時に AutoScaling 中のインスタンスを更新する

次のように設定することで、起動テンプレートを更新した際に AutoScaling 中のインスタンスも自動で更新できます。

resource "aws_autoscaling_group" "runners" {
  ...
  instance_maintenance_policy {
    min_healthy_percentage = 100
    max_healthy_percentage = 200
  }
  instance_refresh {
    strategy = "Rolling"
    triggers = ["tag"]
  }
  ...
}

私はこの設定を知るまでは、都度「インスタンスの更新」を手動で走らせていました...
なお、instance_maintenance_policy も設定し、最小正常率を100以上にしておくことで、起動テンプレート更新と同時にインスタンスを更新する際も、可用性を保ったまま更新ができます。

x86_64 と ARM64 のインスタンスを混在させる

AutoScaling 中のインスタンスではコストパフォーマンスを重視し t4g をメインに利用したいが、スポットインスタンスのキャパシティ低下時の可用性を確保するために t3 も使用したいとします。
この時 CPU のアーキテクチャを見ると、t4g は ARM64 ですが、t3 は x86_64 であるため、それぞれ異なる起動テンプレートを利用する必要があります。

AutoScaling はそれを実現できます。ただし、AWS マネージコンソールでは設定できず、CLI や API を使用する必要があります。
https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-mixed-instances-groups-launch-template-overrides.html

OpenTofu では次のように記述することで、インスタンスタイプごとに異なる起動テンプレートを設定できます。

https://github.com/defaultcf/get-started-ecs-in-2024/blob/79e7832900c78fae13b7d7014ea30e4667a04539/ec2.tofu#L126-L146

weighted_capacity により、arm64 と x86_64 の比率を 3:2 に設定し、arm64 の比重を多めにしています。

最後に

ECS 上の工夫、サードパーティーを利用した工夫をご紹介しました。
明日の CYBOZU SUMMER BLOG FES '24 生産性向上 セイサンシャインビーチ Stage は、@ajfAfg さんの記事をお届けいたします🏖

サイボウズ 生産性向上チーム 💪

Discussion