ECS上のFargateタスクを夜間は自動的に落とすようにして節約してみた


概要

ECS上のfargateタスクをスケジュールで起動したり落としたりしたい。
例えば開発環境のfargateタスクは夜間落としておいて、朝起動しておくと
タスクの実行時間が減るので料金を節約することができる。

大まかな流れ

  • Lambdaを作り、ECS Serviceを更新し、必要なタスク量を増減させる
  • Cloud Watch Eventにて、cronの時間になったらLambdaを発火する

実装

Lambda

ソースコード

今回はpythonで実装。

  • eventで受けとったデータの中から、操作する対象のECSのクラスタ名、サービス名、更新するタスク数を取得
  • ECSに対して更新操作を発行

サンプルソース

import json
import boto3
import logging
from botocore.exceptions import ClientError

logger = logging.getLogger()
logger.setLevel(logging.INFO)


def lambda_handler(event, context):

    logger.info("Event: " + str(event))

    targets = event["targets"]
    desiredCount = event["desiredCount"]

    for target in targets:
        edit_ecs(target, desiredCount)


def edit_ecs(target, desiredCount):
    try:
        client = boto3.client('ecs')

        # update ecs cluster
        service_update_result = client.update_service(
            **target,
            desiredCount = desiredCount
        )

        print(service_update_result)
    except ClientError as e:
        print("exceptin: %s" % e)

IAMロールの加工

Lambda実行時のIAMロールに、ECSへの更新操作権を付与する

Lambdaに付与したIAMロールを開き、以下のようにポリシーを編集

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecs:DescribeServices",
                "ecs:UpdateService"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}

テスト実行

Lambdaでテストイベントを作成。
ECSのクラスター名が dev-ecs-cluster
タスクの増減をしたいサービスが dev-service1 dev-service2 の2種類があると仮定すると以下のようなJSONになる

{
  "targets": [
    {
      "cluster": "dev-ecs-cluster",
      "service": "dev-service1"
    },
    {
      "cluster": "dev-ecs-cluster",
      "service": "dev-service2"
    }
  ],
  "desiredCount": 1
}

上記でLambdaをテスト実行すると ECSの必要タスク数が1に更新され、
タスクが2つ以上上がっていた場合、自動的に1つに変更されるはず。

Lambdaを指定時間で発火させる

指定時間でLambdaを起動させたいので、 Cloud Watch でルール設定をする。

Cloud Watch -> イベント -> ルールの作成

イベントソースをスケジュールに設定し、cron式を入力する。
たとえば毎日AM08:00に実行するなら 0 23 * * ? * となる(時間はGMTなので注意)

イベントターゲットに上記で作成したLambdaを指定し
入力の設定で「定数(JSONテキスト)」を選択し、そこにLambdaのテストで利用したようなJSONを記載する
(入力フィールドがテキストエリアでないのが辛い・・・)
こうするとLambda実行時のeventとして指定したJSONがLambdaに渡る。

desiredCountを 0 にすると該当サービスのタスクをゼロにできる。
なので同様のルールを2つ作り、08:00にはdesiredCountを 1 に。20:00にはdesiredCountを 0 のように設定しておけば夜間にタスクを落としておくことができる