Amazon Event Bridge(CloudWatchEvent)とLambdaでRDS自動起動を行う

技術

業務で扱うRDSステージング環境のコストを少しでも下げるため、RDSの起動停止を自動化した時の対処したメモ

目的

  • RDSのコストを少しでも下げる。
  • 月曜日から金曜日の業務時間(9時〜18時)のみRDSのクラスタを起動させ、それ以外の時間は停止させる。

手順

ロール、ポリシーの作成

  • LambdaでRDSを操作するため、権限を作成する。
  • Lamdaでの実行経過をログに出力したかったので、CloudWatchの操作権限も付与

RDSにタグを付ける

  • Lambdaから操作するRDSクラスタを特定するため、対象となるステージングのRDSにタグを付与。
  • タグ名:AUTO_STARTSTOP 値:true

Lambda関数の作成

  • RDSクラスタの停止実行は、以下のようなコードを記載しました。
  • AutoStopRDSClusterという名前で作成。クラスタ状態がわかるように、停止時にslackにも通知
  • 関数作成時には、前述で作成したロールを付与してください。
#!/usr/bin/env python

import boto3
import time
import os
import json
import logging
from botocore.client import ClientError
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
 
client = boto3.client('rds', os.environ['AWS_REGION'])
 
ACCOUNTID = os.environ['ACCOUNTID']
TAGKEY = os.environ['TAGKEY']
TAGVALUE = os.environ['TAGVALUE']
SLACK_WEBHOOK_URL = os.environ['SLACK_WEBHOOK_URL']
 
def lambda_handler(event, context):
    stop_db_cluster()
 
def stop_db_cluster():
    stop_dbs = []
 
    all_dbs = client.describe_db_clusters()
 
    for db in all_dbs['DBClusters']:
        response = client.list_tags_for_resource(
            ResourceName="arn:aws:rds:" + os.environ['AWS_REGION'] + ":" + ACCOUNTID + ":cluster:" + db['DBClusterIdentifier']
        )
 
        for tag in response['TagList']:
            if tag['Key'] == TAGKEY and tag['Value'] == TAGVALUE:
                stop_dbs.append(db['DBClusterIdentifier'])
 
    if stop_dbs:
        targetlist = list(stop_dbs)
        for dbname in targetlist:
            print('Stop rds db cluster: %s' % (dbname))
            response = _stop_db_cluster(dbname)
            response = _slack(dbname)
 
    else:
        print('Target rds db cluster is not exist.')
 
    return
 
def _stop_db_cluster(db_cluster_identifier):
    for i in range(1, 3):
        try:
            return client.stop_db_cluster(
                DBClusterIdentifier=db_cluster_identifier
            )
        except ClientError as e:
            print(str(e))
        time.sleep(1)
    raise Exception('Cannot stop rds db cluster: , ' + db_cluster_identifier)
 
 
def _slack(db_cluster_identifier):
    color = "good" 
    icon = ":bulb:" 
    SlackText="Aurora Cluster Stop" 
    SlackTextAttachments = "Cluster Name: " + db_cluster_identifier
    slack_message = {
        'username': "AWS Lambda",
        'text': SlackText,
        'icon_emoji': icon,
        'attachments': [
            {
                "color": color,
                "text": SlackTextAttachments
            }
        ]
    }
    req = Request(SLACK_WEBHOOK_URL, json.dumps(slack_message).encode('utf-8'))
    try:
        response = urlopen(req)
        response.read()
        print('Message posted to %s' % (SLACK_WEBHOOK_URL))
    except HTTPError as e:
        print('Request failed: %d %s' % (e.code, e.reason))
    except URLError as e:
        print('Server connection failed: %s' % (e.reason)) 


# EOF
  • コード内で参照する環境変数を設定
  • あとは、同じようにクラスタ起動用の関数を作成すればLambdaに関してはOKです。

EventBridgeの設定

  • Lambda関数をスケジュール実行するため設定します。
  • [Amazon EventBridge] → [ルール]より作成します。
  • パターンの定義はcron式で設定
  • クラスタ停止時間は、残業を考慮し終業時間の3時間後(21時に設定)
  • 設定時刻はUTCなのでマイナス9時間の12時で設定
  • ターゲットに前述で作成したLambda関数を指定する
  • あとは、同じように起動用の関数のイベントルールを作成すればOKです。

起動確認

  • RDSクラスタの起動、停止が行われ無事slackにも通知されました。

タイトルとURLをコピーしました