RDSのコスト最適化術!予測可能なワークロードに自動IOPSとスループットスケーリングで賢く対応

RDSのコスト最適化術!予測可能なワークロードに自動IOPSとスループットスケーリングで賢く対応

RDSのストレージ費用を最適化したいと思ったことはありませんか?

特に、Eコマースサイトのセール期間や、SaaSアプリの月次予算更新後など、アクセス集中が予測できる場合、高性能なストレージを常時稼働させておくのはコスト的に無駄が多いですよね。

そこで今回は、予測可能なワークロードに対して、Amazon RDSのコストを最適化する方法をご紹介したいと思います。具体的には、Amazon RDSのIOPSとスループットを自動スケーリングして、月次や季節ごとのピークに合わせてスケールアップ・ダウンする方法を解説します。

ぜひこの記事を参考にして、無駄なコストを抑えつつ、必要な時に十分なパフォーマンスを発揮できるRDS環境を構築してください!

RDSストレージ費用最適化のソリューション概要

今回ご紹介するソリューションでは、AWS Lambda関数を使用して、RDSインスタンスのタグで定義されたスケジュールに従って、ストレージ設定をスケールアップ・ダウンします。

ワークフローは以下のステップと主要コンポーネントで構成されます。

  1. Amazon EventBridge Schedulerが、スケジュールに基づいてタスクを実行します。このソリューションでは、スケジューラーはLambda関数を1日に1回呼び出します。
  2. Pythonで記述されたLambda関数が、アカウント内のRDSリソースをスキャンし、インスタンスに関連付けられた特定のタグを探します。
  3. 関数は、特定のタグを見つけると、現在のIOPS設定の状態が期待される状態と一致するかどうかを確認します。
  4. 関数は、必要に応じて、Amazon RDSのBoto3呼び出しを使用して、IOPS設定を変更します。
  5. ログは、Amazon CloudWatchログに送信されます。

以下の図は、ソリューションのアーキテクチャを示しています。

今回の例ではAmazon RDS for MariaDBインスタンスを使用していますが、RDS for PostgreSQL、RDS for MySQL、RDS for SQL Server、RDS for Oracle、RDS for Db2にも、このガイダンスを適用できます。ただし、Amazon Auroraは全く異なるストレージアーキテクチャを使用しているため、今回のソリューションの対象外となりますのでご注意ください。

Amazon RDSストレージ操作の制限事項

Amazon RDSストレージ操作には、以下のような制限事項があります。

制限事項補足
IOPSとスループットの変更は6時間ごと、
またはストレージの最適化が完了するまでの
間隔のいずれか長い方で1回だけ許可
期間とパフォーマンスのパラメータは賢く選択する必要があります。
ディスクパフォーマンスがデータベースにとって重要な場合は、
Amazon EBS最適化インスタンスの使用を検討
gp3ストレージで提供される以上のパフォーマンスと
耐久性が必要な場合は、io2 Block Expressの使用を検討してください。
io2 Block Expressは、最大256,000 IOPSを実現できます。
gp3ボリュームの場合、
プロビジョニングされたIOPSとスループットの変更
Oracleの場合は200 GB以上、Db2、MariaDB、MySQL、PostgreSQLの場合は400 GB以上の
ボリュームに対してのみ許可され、SQL Serverの場合はストレージ割り当て制限は設定されていません。

制限値未満の場合は、3,000 IOPS/125MiB/sの固定ベースラインが適用され、設定を変更することはできません。
ベースラインは価格に含まれており、容量を追加でプロビジョニングした場合にのみ追加料金が発生します。
上記の制限値を超えるgp3ストレージでは、
プロビジョニングされたIOPSを12,000~64,000、
スループットを500~4,000 MiB/sの範囲で設定可能。
インスタンスクラスが設定されたIOPSとスループットの量をサポートしていることを確認する必要があります。
そうでない場合、インスタンスクラスがボトルネックになります。
io2 Block Expressの場合、
スループットはプロビジョニングされたIOPSごとに最大0.256 MiB/s、
最大4,000 MiB/sまで比例的にスケールアップ

これらの制限事項の詳細については、Amazon RDS DBインスタンスのストレージの操作を参照してください。

前提条件

  • IOPS(gp3、io1、またはio2 Block Express)を設定できるボリュームタイプを使用するRDSデータベース
  • Amazon EventBridge、Lambda関数、およびAWSリソースのナビゲート方法に関する基本的な知識

Lambda関数を作成する

以下の手順に従って、Lambda関数を作成します。簡略化のため、以下の例ではIOPSスケーリングを自動化する方法について説明していますが、スループットをスケーリングするようにコードを適応させることもできます。

【1】AWS Identity and Access Management(IAM)Lambda実行ロールを作成し、Lambda関数にRDSインスタンスのストレージパラメータを変更する権限を付与します。Amazon CloudWatchにメッセージをログに記録するために必要な権限を含む、以下の権限が必要です。

rds:DescribeDBInstances
rds:ListTagsForResource
rds:ModifyDBInstance
logs:CreateLogGroup
logs:CreateLogStream
logs:PutLogEvents

以下は、必要な権限を提供するIAMポリシーの例です。(例にあるアカウントIDは、ご自身のアカウントIDに変更する必要があります)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor",
            "Effect": "Allow",
            "Action": [
                "rds:DescribeDBInstances",
                "rds:ListTagsForResource",
                "rds:ModifyDBInstance"
            ],
            "Resource": "arn:aws:rds:*:{your_account_id}:db:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:{your_account_id}:*"
        }
    ]
}

【2】PythonでLambda関数を作成

前に作成したIAMロールをアタッチし、Lambda関数では以下のことを行います。

  • 各インスタンスについて、以下のタグが存在するかどうかを確認
  • 5-10と設定した場合、毎月の5日から10日の間に自動化が実行されると、高い設定が設定される(期間外は低い設定が適用される)
  • タグが存在する場合は、IOPSHighDayIntervalタグを解析、高いIOPS期間の開始日と終了日を取得

また、現在の日付を取得し、その日が需要が高い期間に該当するかどうかを確認し、需要が低い場合はmodify_db_instance()を呼び出して、IOPSをIOPSLowSettingの値に設定します。

タグ意味
IOPSHighSettingインスタンスの需要が高い場合(期間内)に設定されるIOPSの数
IOPSLowSettingインスタンスの需要が低い場合(期間外)に設定されるIOPSの数
IOPSHighDayIntervalデータベースの需要が高い日数の間隔

IOPSをスケーリングするためのソースコードは以下のとおりです。

import boto3
import os
import logging
logger = logging.getLogger()
logger.setLevel("INFO")
from datetime import datetime
rds = boto3.client('rds')

def lambda_handler(event, context):
    logger.info('## ENVIRONMENT VARIABLES')
    logger.info(os.environ['AWS_LAMBDA_LOG_GROUP_NAME'])
    logger.info(os.environ['AWS_LAMBDA_LOG_STREAM_NAME'])
    logger.info('## EVENT')
    logger.info(event)
    # Get current day of month
    today = datetime.today().day
    # Get list of RDS instances
    response = rds.describe_db_instances()
    for db in response['DBInstances']:
        curr_Iops = int(db['Iops'])
        arn = db['DBInstanceArn']  
        tags = rds.list_tags_for_resource(ResourceName=arn)
        # Check if tags exist
        tag_dict = {tag['Key']: tag['Value'] for tag in tags['TagList']}
        logger.info('Processing database {0}'.format(arn))
        if 'IOPSHighSetting' in tag_dict and 'IOPSLowSetting' in tag_dict and 'IOPSHighDayInterval' in tag_dict:
            logger.info('IOPS tags found')
            high_iops = int(tag_dict['IOPSHighSetting']) 
            low_iops = int(tag_dict['IOPSLowSetting'])
            interval_str = tag_dict['IOPSHighDayInterval']
            # Parse interval
            interval_days = [int(x) for x in interval_str.split("-")]
            interval_start = interval_days[0]
            interval_end = interval_days[1]
            # Check if current day is within interval
            if interval_start <= today <= interval_end:
                iops = high_iops
            else:
                iops = low_iops
            if curr_Iops == iops:
                logger.info('No change needed, databases IOPS already set to {0}'.format(str(iops)))
            else:
                logger.info('Changing databases IOPS from {0} to {1}'.format(curr_Iops,str(iops)))
                # Modify IOPS        
                try:
                    rds.modify_db_instance(
                        DBInstanceIdentifier=db['DBInstanceIdentifier'],
                        AllocatedStorage=db['AllocatedStorage'],
                        Iops=iops,
                        ApplyImmediately=True
                    )
                except Exception as error:
                    logger.error('Could not apply change to database {0}: {1}'.format(db['DBInstanceIdentifier'], error))
        else:
            logger.info('No IOPS tags found')

【3】Lambda関数を毎日午前0時に呼び出すには、スケジュール式cron(0 0 * * ? *)を使用して、EventBridge Schedulerルールを作成します。

これにより、Lambda関数が毎日UTC 00:00にトリガーされますが、より適切な変更時間を選択できます。運用中にダウンタイムは発生しません。

Lambda関数に、必要なアクションを完了するための十分なタイムアウトとメモリが割り当てられていることを確認してください。デフォルトの3秒と128 MBは、RDSインスタンスが1つのテスト環境では機能しますが、複数のインスタンスでは機能しない場合があります。

Lambda関数オートメーションをテストする

【1】3つのタグ(IOPSHighSetting、IOPSLowSetting、IOPSHighDayInterval)を設定して、RDSインスタンスを作成

前に説明したように、インスタンスとストレージタイプのIOPS設定の境界内に設定が収まっていることを確認するには、Amazon RDS DBインスタンスストレージを参照してください。

【2】Lambdaコンソールで、テスト環境を設定

提供されているコードの場合、テストイベントに変数を指定する必要はありません。

【3】Lambda関数をテスト

Lambda関数が期待どおりに機能している場合は、ログに次のようなメッセージが表示されます。

上記の例では、RDSインスタンスはすでに本日の予想されるIOPS設定で設定されていました。

【4】本日を高い需要間隔に含めるように、タグIOPSHighDayIntervalを変更してみましょう。

【5】Lambdaテストイベントを再度実行すると、インスタンスのIOPS設定が変更されていることがわかります。

インスタンスの状態は「変更中」に変わります。変更が完了するまでしばらく時間がかかる場合があります。「変更中」の後は、状態は「ストレージの最適化」に変わります。インスタンスがこれらのいずれかの状態にある間は、少なくとも6時間はストレージに新しい変更を加えることができず、間隔を再度変更してLambda関数を実行すると、次のエラーが発生します。ModifyDBInstance操作の呼び出し中にエラーが発生しました(InvalidParameterCombination):以前のストレージ変更が最適化されているため、現在このDBインスタンスのストレージを変更できません。

ストレージの最適化の詳細については、Amazon RDS DBインスタンスのストレージの操作を参照してください。

Lambda関数を毎日実行するようにスケジュールする

スケジューラータグに従ってストレージ設定を調整するには、Lambda関数を毎日実行する必要があります。これは、EventBridgeでスケジューラーイベントを作成することで実現できます。以下の手順を実行します。

【1】EventBridgeコンソールのナビゲーションペインの[スケジューラー]で、[スケジュール]を選択

【2】[スケジュールの作成]を選択します。

【3】ルール名、オプションの説明、イベントバスなどのルール詳細を入力

【4】[ルールタイプ]で、[スケジュール]を選択します。

【5】[スケジュールパターン]で、[スケジュール式]を選択します。

【6】[発生]で、[繰り返しスケジュール]を選択

【7】[スケジュールタイプ]で、[Cronベースのスケジュール]を選択

【8】[Cron式]に、cronパラメータを入力

この記事では、アクションを毎日午前0時に実行するようにスケジュールします。

【9】15分の柔軟な時間枠を選択し、[次へ]を選択

【10】[ターゲットの選択]で、[AWS Lambdaの呼び出し]を選択

【11】関数名で、前に作成したLambda関数を選択

【12】[設定]で、[すべてのリクエストで同じ設定を使用する]を選択

【13】[スケジュールの完了後のアクション]で、[なし]を選択

【14】[権限]セクションで、すでに作成済みの特定のロールがない限り、[スケジュールの新しいロールを作成する]を選択し、ロール名を入力

【15】[次へ]を選択

【16】すべてが想定どおりであることを確認し、[スケジュールの作成]を選択します。

Lambda関数はスケジュールどおりに実行されます。実行を監視するには、Amazon EventBridgeの監視を参照してください。

クリーンアップ

このガイドに従ってテスト環境を作成した場合は、継続的なコストが発生しないように、以下のリソースを削除する必要があります。

  • RDSインスタンス
  • EventBridgeスケジュール
  • Lambda関数

まとめ

この記事では、RDSインスタンスタグで設定されたスケジューラーに基づいて、ストレージパフォーマンスパラメータを自動的に調整する方法について概説しました。このソリューションは、EventBridgeスケジューラーとPythonで記述されたLambda関数に基づいており、コストを削減し、データベースのパフォーマンスを向上させることができます。このソリューションは、要件に応じて、IOPSパラメータだけでなく、スループットもスケーリングできます。

この記事が、皆さんのRDS環境の最適化に役立てば幸いです。

AWSモダナイズ開発データ分析基盤構築とデータ活用支援、基幹業務システムのUI.UX刷新はお気軽にお問い合わせください。