Performance InsightsのカウンターメトリクスをCloudWatchに連携してみた

2019.07.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、崔です。

RDSのPerformance Insightsは、データベースインスタンスを監視しパフォーマンスを分析する機能です。
Amazon RDS Performance Insightsの使用

Performance Insightsは、システムパフォーマンスの監視に役立つように様々なOSとDBのカウンターメトリクスを収集します。
また、各RDSデータベースエンジンには、独自のカウンターメトリクスのセットがあります。
パフォーマンスインサイトのカウンター:Aurora
パフォーマンスインサイトのカウンター:RDS

Performance Insightsのこれらのメトリクスはマネジメントコンソール上のダッシュボードで確認可能ですが、提供されているAPIを使えばそのデータを取得できます。
今回は、Performance Insightsが提供しているAPIである GetResourceMetrics と、AWS Lambdaを用いてCloudWatchに連携してみました。

Lambda関数の作成

まずは、Lambda関数を作成していきます。
[関数の作成]をクリックします。

次に、[一から作成]を選択します。

[関数名]を入力し、[Runtime]はPython3.7を選択します。
次に、アクセス権限の[実行ロールの選択または作成]のプルダウンを開き、[実行ロール]でAWSポリシーテンプレートから新しいロールを作成を選択、[ロール名]を入力します。 [関数の作成]をクリックします。([ポリシーテンプレート]は空白のままでOK)

[基本設定]でタイムアウトを1分に設定し、保存します。

次に、[トリガーを追加]をクリックします。

[トリガーの設定]でCloudWatch Eventsを選択します。
[ルール]で「新規ルールの作成」を選択します。
[ルール名]を入力し、[ルールタイプ]で「スケジュール式」を選択します。
[スケジュール式]にrate(5 minutes)(5分おき)を入力します。
[トリガーの有効化]にチェックが入っていることを確認し、「追加」をクリックします。

次に「テスト」をクリックします。「新しいテストイベントの作成」を選択し、[イベントテンプレート]で「Amazon CloudWatch」を選択し、イベント名を入力します。

「テスト」をクリックし、実行結果が「成功」であることを確認します。

Lambda関数のIAMロールの更新

IAMサービスのコンソール上で、メニューから「ポリシー」を選択し、[ポリシーの作成]をクリックします。
[サービスの選択]からPerformance Insightsで検索し、クリックします。

[手動のアクション]にて、[すべてのPerformance Insights アクション]のチェックボックスを選択します。

[リソース]で[すべてのリソース]を選択し、「ポリシーの確認」をクリックします。

[名前]に「RDSPerformanceInsightsFullAccess」と入力し、「ポリシーの作成」をクリックします。

次に、IAMサービスのコンソール上で、上記で作成したロール「PerformanceInsightsToCloudWatch」を選択します。
[ポリシーをアタッチします]をクリックします。

先程作成したポリシー名で検索し、チェックボックスをチェックしアタッチします。

同様に、AmazonRDSReadOnlyAccessポリシーとCloudWatchFullAccessポリシーを追加します。

Lambda関数を更新

最後に、Lambda関数のコードを修正します。

import time
import boto3
import logging

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

pi_client = boto3.client('pi')
rds_client = boto3.client('rds')
cw_client = boto3.client('cloudwatch')

engine_metrics = {
    'aurora': ['db.SQL.Innodb_rows_read.avg', ],
    'aurora-postgresql': ['db.Transactions.xact_commit.avg'],
    'postgres': ['db.Transactions.xact_commit.avg']
}

def lambda_handler(event, context):
    pi_instances = get_pi_instances()

    for instance in pi_instances:
        pi_response = get_resource_metrics(instance)
        if pi_response:
            send_cloudwatch_data(pi_response)

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

def get_pi_instances():
    rds_client = boto3.client('rds')
    response = rds_client.describe_db_instances()

    return filter(
        lambda _: _.get('PerformanceInsightsEnabled', False),   
        response['DBInstances']
    )

def get_resource_metrics(instance):
    pi_client = boto3.client('pi')

    metric_queries = []
    if engine_metrics.get(instance['Engine'], False):
        for metric in engine_metrics[instance['Engine']]:
            metric_queries.append({'Metric': metric})

    if not metric_queries:
        return

    return pi_client.get_resource_metrics(
        ServiceType='RDS',
        Identifier=instance['DbiResourceId'],
        StartTime=time.time() - 300,
        EndTime=time.time(),
        PeriodInSeconds=60,
        MetricQueries=metric_queries
    )

def send_cloudwatch_data(pi_response):
    metric_data = []

    for metric_response in pi_response['MetricList']:
        cur_key = metric_response['Key']['Metric']

        for datapoint in metric_response['DataPoints']:
            logger.debug('adding datapoint={}'.format(datapoint))

            # We don't always have values from an instance
            value = datapoint.get('Value', None)

            if value:
                metric_data.append({
                    'MetricName': cur_key,
                    'Dimensions': [
                        {
                            'Name':'DbiResourceId',    
                            'Value':pi_response['Identifier']
                        } 
                    ],
                    'Timestamp': datapoint['Timestamp'],
                    'Value': datapoint['Value']
                })

    if metric_data:
        cw_client.put_metric_data(
            Namespace='PerformanceInsights',
            MetricData= metric_data
        )

コードブロック先頭のengine_metricsディクショナリで、データベースエンジン名とそれに対応するメトリクスのリストをマッピングしています。
上記の場合、Aurora MySQLに対応するInnodb_rows_read、Aurora PostgreSQL・RDS for PostgreSQLに対するxact_commitのAvgを取得しています。
このマップを変更し、取得したいカウンターメトリクスを指定することが可能です。

CloudWatchでデータを表示する

CloudWatchを開くと、PerformanceInsightsという新しいカスタム名前空間が表示されます。

こちらを開いていくと、追加したメトリクスが表示されていることが確認できます。

まとめ

PerformanceInsightsが提供しているAPIであるGetResourceMetricsを使えば、OSやデータベースの様々なカウンターメトリクスをCloudWatchに連携することができます。 これにより、データベースのメトリクスも、監視やアラームが実施しやすくなるのではないでしょうか。

参考

Importing Amazon RDS Performance Insights counter metrics to Amazon CloudWatch
パフォーマンスインサイトAPI