0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[AWS] ECSタスクとRDSを使わない時に停止させる

Last updated at Posted at 2025-02-16

概要

  • コスト削減のため、ECSやRDSを休日に停止させるコードを記述する
    • 平日に起動させる処理はまた別途作成が必要(後述の処理を流用して書き換える)

システム構成

  • Lambda
  • ECS
  • RDS(RDS Proxy)
  • EventBridge

Lambdaのコード

python
import boto3
import os
import logging
import time

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

def retry(func, retries=3, delay=5):
    for attempt in range(retries):
        try:
            return func()
        except Exception as e:
            if attempt < retries - 1:
                logger.warning(f'Retry {attempt + 1} failed: {str(e)}. Retrying...')
                time.sleep(delay)
            else:
                logger.error(f'Failed after {retries} attempts: {str(e)}')
                raise

def stop_ecs_service(ecs_client, ecs_cluster_name, ecs_service_name):
    ecs_client.update_service(
        cluster=ecs_cluster_name,
        service=ecs_service_name,
        desiredCount=0
    )

def stop_rds_cluster(rds_client, rds_cluster_identifier):
    rds_client.stop_db_cluster(
        DBClusterIdentifier=rds_cluster_identifier
    )

def wait_for_rds_cluster_stop(rds_client, rds_cluster_identifier):
    while True:
        response = rds_client.describe_db_clusters(DBClusterIdentifier=rds_cluster_identifier)
        status = response['DBClusters'][0]['Status']
        logger.info(status)
        if status == 'stopped':
            break
        time.sleep(30)

def remove_rds_proxy_targets(rdsproxy_client, rds_proxy_name, rds_instance_identifier):
    response = rdsproxy_client.describe_db_proxies(DBProxyName=rds_proxy_name)
    logger.info(f'Describe DB Proxies response: {response}')
    if 'DBProxies' in response and len(response['DBProxies']) > 0:
        db_proxy = response['DBProxies'][0]
        logger.info(db_proxy)
        target_groups = db_proxy.get('TargetGroups', [])
        if target_groups:
            for target_group in target_groups:
                rdsproxy_client.deregister_db_proxy_targets(
                    DBProxyName=rds_proxy_name,
                    TargetGroupName=target_group['TargetGroupName'],
                    DBInstanceIdentifiers=[rds_instance_identifier]
                )
            logger.info(f'Target groups removed for DB Proxy {rds_proxy_name}')
        else:
            logger.warning(f"No target groups found for DB Proxy {rds_proxy_name}")
    else:
        logger.warning(f"No DB Proxies found with name {rds_proxy_name}")

def lambda_handler(event, context):
    ecs_client = boto3.client('ecs')
    rds_client = boto3.client('rds')
    rdsproxy_client = boto3.client('rds')

    ecs_cluster_name = os.environ['ECS_CLUSTER_NAME']
    ecs_service_name = os.environ['ECS_SERVICE_NAME']
    rds_cluster_identifier = os.environ['RDS_CLUSTER_IDENTIFIER']
    rds_instance_identifier = os.environ['RDS_INSTANCE_IDENTIFIER']
    rds_proxy_name = os.environ['RDS_PROXY_NAME']

    try:
        retry(lambda: stop_ecs_service(ecs_client, ecs_cluster_name, ecs_service_name))
        logger.info('ECSサービスを停止しました')

        retry(lambda: stop_rds_cluster(rds_client, rds_cluster_identifier))
        logger.info('RDSクラスターの停止処理が開始しました')

        wait_for_rds_cluster_stop(rds_client, rds_cluster_identifier)
        logger.info('RDSクラスターが停止しました')

        retry(lambda: remove_rds_proxy_targets(rdsproxy_client, rds_proxy_name, rds_instance_identifier))
        logger.info('RDS Proxyターゲットグループの削除処理が完了しました')

        message = 'Successfully stopped ECS, RDS, and updated RDS Proxy'
        logger.info(message)
    except Exception as e:
        message = f'Failed to stop ECS, RDS, or update RDS Proxy: {str(e)}'
        logger.error(message)
        raise e

    return {
        'statusCode': 200,
        'body': message
    }

上記のコードは同期処理のため、動かしてみて処理が15分以上かかる場合は非同期処理にする

コード上でリトライ処理を実装しているが、EventBridgeのルール作成でもリトライ処理は設定可能

必要なこと

IAM

  • ロール作成
  • ポリシー作成
    • LambdaExcecution
    • CloudWatchLog
    • RDS
    • ECS
    • RDSProxy

→作成したロールをLambdaにアタッチ

セキュリティグループ

インバウンドルールまたはアウトバウンドルールを設定する

RDS及びECSのインバウンドルールに、Lambdaのアクセス許可設定は不要。停止処理は管理者操作のため。

EventBridge

  • cron式でスケジュール設定する(設定方法はAWS内のツールチップを参照)
  • 実装したLambda関数を指定する

おわりに

  • 過去に個人で実装した経験を思い出しながら執筆しました
  • このコードで問題なく動作した記憶があるので、問題ないはずです
    • 仮に動作しない場合は構成が違っている可能性があるので、コードを修正して実装してみてください

以上

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?