スケジュールされたアクションで Amazon ECS サービスを夜間停止させてみた

記事タイトルとURLをコピーする

こんにちは、すえひろです。 検証環境等で ECS サービスを使っている時、「夜間のみ停止したいなぁ…」と思ったことはありませんか? その場合、おそらく手動で Desired tasks を 0 にしたり、EventBridge と Lambda を作成してスケジュールしたりすることが多いと思います。

本ブログでは ECS の「スケジュールされたアクション」という仕組みを使い、自動的に夜間はタスク数を 0 にすることで利用料を抑える方法を紹介します。

前提条件、注意点

  • VPC や ECS クラスター、サービスは作成済みとします
  • 本検証内容を実行すると ECS サービスの Disired tasks の数を上書きするため、タスクが停止する可能性がある点ご注意ください
  • 本内容を活用することでスケーリングに応用することができますが、今回は起動停止のみに着目して検証しています
  • サービスの CPU やメモリ状態によってスケーリングさせる「スケーリングポリシー」については考慮しません、以下弊社ブログを参考ください

blog.serverworks.co.jp

マネジメントコンソールから設定

公式ドキュメントでの設定方法は以下で紹介されているので合わせてご参考ください。

まずは対象の ECS クラスターからサービスを選択して「サービスの自動スケーリング」タブを見つけます。

サービスの自動スケーリング

上記前提条件に記載したスケーリングポリシーを設定していない場合は何も設定されていないかと思います。

タスク数を設定

タスク数を設定を押下すると、準備としてスケーリング時に使用するスケーリングポリシーの最小タスク数と最大タスク数を設定する必要があると表示されます。

タスク数を設定

「スケーリングポリシーの」と記載がある通り今回のスケジュールされたアクションではあまり関係がない(この後本部分が上書きされる)ため、適当な値でよいです。しかし入力した値が Desired tasks に 即時に反映される条件があります。 後述の設定時にマネジメントコンソールに以下のように説明が出てきます

現在のキャパシティが、スケジュールされた時刻に最小キャパシティを下回っている場合、キャパシティは最小キャパシティ制限までスケールアウトされます。現在のキャパシティが最大キャパシティを上回っている場合は、最大キャパシティまでスケールインされます。

ここが大事な要素なので図を使って説明します。

現在のキャパシティが最小キャパシティを下回っている場合

現在 < 最小

この時、最小キャパシティ制限までスケールアウトされるということなので、Desired tasks が最小数に設定した値となります。

現在のキャパシティが最大キャパシティを上回っている場合

現在 > 最大

この時は最大キャパシティまでスケールインさるので Desired tasks が最大数に設定した値となります。

2つのパターン以外

最小 ≦ 現在 ≦ 最大

2つのパターン以外、つまり最小数 ≦ 現在の Desired tasks ≦ 最大数の場合は特に変化は起こりません。

これを起動停止の観点で考えると、最大数(0) < 現在の Desired tasks 数(1) と設定した場合、設定した瞬間に Desired tasks が 0 になり、タスクが全て停止してしまいます。逆に現在の Desired tasks 数(0) < 最小値(1) に設定すると Desired tasks が 1 になり、タスクが新たに起動してしまいます。

結論、 「最小数 ≦ 現在の Desired tasks ≦ 最大数」にしておくと現環境に何も影響が起こらないため、この数値にすることを推奨します。

タスク数の設定が完了すると、新たにスケーリングポリシー、スケジュールされたアクション、レコメンデーションのタブが出現します。

タスク数設定後

スケジュールされたアクションを設定

それではスケジュールされたアクションを設定していきます。

停止アクション

アクション名とタイムゾーン、開始時刻の入力をします。開始時刻にはアクションを作成する現在時刻より後の時間をいれる必要がありました。自動入力される値は作成ボタンを押した瞬間の10分後なので、10分以上のトイレ休憩を挟んでいると過去の時になってしまうので注意しましょう。

スケジュールされたアクションの詳細を設定

過去の時間を入力してみる

cron 式で繰り返しを設定します。添付図では30 19 ? * * *で毎日19:30 に停止させるようにします。 AWS で使うことができる cron 式はドキュメントを参考ください。

docs.aws.amazon.com

キャパシティの調整にタスク数を入力します。上記のタスク数を設定で説明した内容と同じ考え方です。(引用部分はここです) タスクの夜間停止アクションとして作成するため、最大数を0にすればよいわけです。最小数 ≦ 最大数である必要があるので両方に0を入力します。

現在のキャパシティが最大キャパシティを上回っている場合は、最大キャパシティまでスケールインされます。

キャパシティの調整

起動アクション

停止アクションと同様に朝自動でタスクが起動するように設定します。 例として、毎朝9:30の cron 式を入力します。

アクションの詳細

キャパシティは最小数に起動したいタスク数を入力します。本スケジュールの実行タイミングではタスク数が0になっているはず(昨晩停止している)なので、最大数は何の値でも問題ないですが、最小数に合わせておくのが無難だと思います。

現在のキャパシティが、スケジュールされた時刻に最小キャパシティを下回っている場合、キャパシティは最小キャパシティ制限までスケールアウトされます。

キャパシティの調整

設定完了です。コンソールからは以下のように見えていると思います。

スケジュールされたアクション一覧

動作確認

スケーリングアクティビティ

動作ログはスケーリングアクティビティから確認できます。

スケーリングアクティビティ

停止アクションについて一部抜粋します。時系列的には古いほうが下になっています。 stop-action がトリガーされ、タスク数が最大数が0になるよう実行されたことが確認できました。

リソース ID スケーラブルなディメンション ステータス ステータスメッセージ 説明 原因
service/amp-amg-trial/nginx-prometheus ecs:service:DesiredCount 成功 Successfully set desired count to 0. Change successfully fulfilled by ecs. Setting desired count to 0. maximum capacity was set to 0
service/amp-amg-trial/nginx-prometheus ecs:service:DesiredCount 成功 Successfully set min capacity to 0 and max capacity to 0 Setting min capacity to 0 and max capacity to 0 scheduled action name stop-action was triggered

起動アクションについても一部抜粋します。こちらでは最小数が2になるよう実行されていました。

リソース ID スケーラブルなディメンション ステータス ステータスメッセージ 説明 原因
service/amp-amg-trial/nginx-prometheus ecs:service:DesiredCount 成功 Successfully set desired count to 2. Change successfully fulfilled by ecs. Setting desired count to 2. minimum capacity was set to 2
service/amp-amg-trial/nginx-prometheus ecs:service:DesiredCount 成功 Successfully set min capacity to 2 and max capacity to 2 Setting min capacity to 2 and max capacity to 2 scheduled action name test-action was triggered

実行タスク数

実行タスク数の設定の際に

今回のスケジュールされたアクションではあまり関係がない(この後本部分が上書きされる)ため、

と記載しました。これは、アクションが実行されたタイミング(今回で言う cron 式のタイミング)で最小数と最大数がアクションに設定された値になるため、初期値として設定する時に数値をあまり気にしなくてよいといった内容になります。

実行前

実行後

おまけ

Terraforma コード紹介

検証中にポチポチ開始時刻の設定が面倒だったため Terraform コードを作成したのでおまけとして参考ください。

registry.terraform.io

Terraform ではタイムゾーンのパラメータが Option なので、忘れず設定しないとUTC で動作してしまいます。 コンソールでは開始時刻の設定が必須でアクションを作成するタイミングより後の時間をいれる必要がありましたが、Terraform ではここが必須ではなく、作成されたタイミングからアクションは有効になるようです。

また、max_capacity, min_capacityignore_changes として設定しています。実行タスク数の最大値、最小値はスケジュールされたアクションによって上書きされるため、起動や停止の実行後は、次回の terraform apply 時に差分が発生してしまいます。これを防ぐために ignore_changes をいれておく方が安全でしょう。

resource "aws_appautoscaling_target" "ecs" {
  max_capacity       = 4 # ここはなんでもよい(現在の desired task 数より多いことが望ましい)
  min_capacity       = 1 # ここはなんでもよい(現在の desired task 数より少ないことが望ましい)
  resource_id        = "service/amp-amg-trial/nginx-prometheus" # service/<cluster-name>/<service-name>
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs" # ecs でよい

  lifecycle { ignore_changes = [max_capacity, min_capacity]}
}

resource "aws_appautoscaling_scheduled_action" "ecs-start" {
  name               = "terraform-action" # アクション名
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  schedule           = "cron(30 10 ? * * *)" # 毎日10:30分
  timezone           = "Asia/Tokyo" # 重要

  scalable_target_action {
    min_capacity = 1 # 起動したいタスク数
    max_capacity = 1 # スケーリングポリシーを作成しないなら最小数と同数で良い
  }
}

resource "aws_appautoscaling_scheduled_action" "ecs-stop" {
  name               = "terraform-stop-action"
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  schedule           = "cron(30 18 ? * * *)"
  timezone           = "Asia/Tokyo"

  scalable_target_action {
    min_capacity = 0 # 最大数と合わせる
    max_capacity = 0 # タスク停止の指定
  }
}

遊んでみる

同じ cron 式で違うキャパシティの調整のアクションを設定してどうなるか試してみました。通常の運用でこのようなことはないですが、好奇心でやってみました。つまりはこういうことです。

違うアクションを同時刻に

アクション自体はエラーなく作成できています。

アクティビティ確認

同時実行のアクティビティ1

同時実行のアクティビティ2

現在のタスク数はいくつだと思いますか?









正解は1でした。

1

何度か試しましたが、アクションの実行順序にアクションの作成した順やタスク数に降順になる、等の法則はなくランダムに並行実行されているようでした。当然ですが、最終のタスク数としては最後にトリガーされたアクションの数値になるようです。

良い子は真似しないようにしてください。

まとめ

スケジュールされたアクションで Amazon ECS サービスを夜間停止させる方法を紹介しました。Lambda や EventBridge など他リソースの管理が必要なくコストを節約できるため、検証環境などで動作させるのには適していると思います。

一方、cron 式で繰り返しを指定するため、祝日などの対応は難しい点は注意が必要です。弊社が提供している Cloud Automator では ECS タスクの実行と停止が可能で、こちらでは祝日の対応もできます。ECS サービスではないため、今回検証したものと勝手は違いますが、お使いの環境に合わせてお試しください。

blog.serverworks.co.jp

末廣 満希(執筆記事の一覧)

2022年新卒入社です。ここに何かかっこいい一言を書くことができるエンジニアになれるように頑張ります。