業務で扱う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にも通知されました。