Amazon Web Services ブログ

Amazon RDS Performance Insights のカウンターメトリクスを Amazon CloudWatch にインポートする

Amazon RDS Performance Insights では、Amazon RDS データベースインスタンスをモニターでき、データベースのパフォーマンスの分析やトラブルシューティングが可能です。Performance Insights のデータは AWS Management Console で表示させることができます。また別の手段として、Performance Insights の公開された API を使い、ご自分が必要なデータを照会することもできます。この API を使って、データ取得や、既存のモニタリングダッシュボードへの Performance Insights データの追加、あるいは、ご自身のモニタリングツールの構築などが行えます。今回のブログでは、Cloudwatch へデータを送信するために、この API を使用します。

概要

Performance Insights では、システムパフォーマンスのモニタリングに使うため、オペレーティングシステムやデータベースカウンターについての、多様なメトリクスを収集します。各 Amazon RDS データベースエンジンには、Aurora メトリクスなど、一連のカウンターメトリクスが個別に備わっています。データベースパフォーマンスのトラブルシュートは、データベースの動作時刻を指定することで開始できますが、カウンターメトリクスも、データモニタリングのために、二次的ソースとして便利に利用できます。またこれらを、既存のシステムダッシュボードに統合すると便利です。

この記事は、データベースカウンターメトリクスに関心をお持ちの方すべてに向けたものです。まず、2 つある Performance Insights API の 1 つである GetResourceMetrics を取り上げます。これにより、Performance Insights からデータを取り出す AWS Lambda 関数を作成する方法と、別のモニタリングシステムにそれを統合する方法をご紹介します。この記事では、Boto3、Python3、AWS CLI、Performance Insights API を使用していきます。作業開始の前に、Performance Insights が有効化された Amazon RDS インスタンスを作成するか、既存のインスタンスにおいて Performance Insights を有効化してください。

AWS では、可能な限り頻繁に API を公開しサービス機能を追加することで、お客様がご自身のソリューションを便利に構築できるよう努めてまいります。もちろん、Performance Insights も例外ではありません。すでにご提供しているコンソールを使い、サービスを操作していただくことはできますが、API を使うと、データ自体にアクセスすることができます。

Performance Insights ダッシュボード (次のスクリーンショット) には、3 つのパートがあります。

  • カウンターメトリクスのグラフでは、特定のパフォーマンスについてカウンターメトリクスを表示します。
  • 平均アクティブセッション (Average active sessions) のグラフでは、Max CPU で与えられたデータベースインスタンス容量とデーターベースの負荷量を比較表示します。
  • 上位ロードアイテム (Top load items) の一覧では、データベースの負荷となっている上位アイテムを表示します。

Performance Insights API には、2 つのアクションがあります。

  • GetResourceMetrics では、メトリクスの時系列データを取得します。このメソッドでカウンターメトリクスと平均アクティブセッションのグラフを作成します。
  • DescribeDimensionKeys で一定期間における上位メトリクスのキーを取得します。このメソッドで上位負荷アイテムの一覧を作成します。

GetResourceMetrics

この記事では、カウンターメトリクスの取得に GetResourceMetrics を使用する方法に焦点を当てます。この API メソッドの使用方法の詳細については、AWS CLI から help コマンドを使いご確認ください。

% aws pi get-resource-metrics help
...
get-resource-metrics
--service-type <value>
--identifier <value>
--metric-queries <value>
--start-time <value>
--end-time <value>
[--period-in-seconds <value>]
  • start-time: 時系列上でクエリ範囲の開始を指定する、その範囲の先頭時刻を示す値です。Performance Insights では、デフォルトで保存期間が 7 日間に設定されていますが、2 年間に延長できます。
  • end-time: 時系列上で、クエリ範囲のパラメータの終わりを指定する、その範囲終了直後の時刻値です。
  • period-in-seconds: 戻り値となるデータポイントの分解能を示します。現在この値は、1、 60、 300、 3600、86400 のいずれかにする必要があります。
  • service-type: クエリを送るリソースを特定するために使用します。現在この値には、RDS だけが指定できます。
  • identifier: リソースを特定します。Amazon RDS コンソールでインスタンスの詳細を表示すると、この値は ResourceID として表示されています。Amazon RDS の DescribeDbInstances API を呼び出すと、この値には DbiResourceId が返されます。
  • metric-queries パラメータ: 結果を取得するクエリを 1 つ以上指定します。各クエリには、必須パラメータとして Metric が、任意パラメータとして GroupBy および Filter が含まれます。

この記事では、最も最近 5 分間のデータについての、シンプルなメトリクスクエリを作成していきます。

% aws pi get-resource-metrics
--service-type RDS \
--identifier <instance id> \
--start-time <cur time – 5 minutes> \
--end-time <cur time> \
--period-in-second 60 \
--metric-queries '[ {"Metric": "metric1" },{"Metric": "metric2"}]'

以下に具体的なコードの例を示します。リソース ID の識別子は変更する必要がありますが、コード全体はそのまま使用できます。

aws pi get-resource-metrics \
--service-type RDS \
--identifier <db-> \
--start-time `expr \`date +%s\` - 300 ` \
--end-time `expr \`date +%s\`` \
--period-in-second 60 \
--metric-queries \
'[{"Metric": "db.Transactions.xact_commit.avg" },
{"Metric": "os.general.numVCPUs.avg"}]' 

ステップ 1: 基本となる Lambda 関数を作成する

AWS Lambda コンソールで、[ Create Function] 、[Author from Scratch] の順にクリックします。

Name: PerformanceInsightsCounterMetrics を選択します。

Runtime: Python 3.7 を選択します。

Roles: 1 つあるいは複数のテンプレートから新しいロールを作成します。

Role name: PerformanceInsightsCounterMetricsRole を選択します。

 

Policy templates は空白のままにします。

最後に、タイムアウト問題を回避するためにBasic SettingsTimeout を 1 分間に設定します。

関数のスケジューリング

Lambda 関数作成した後、設定を確認します。Lambda 関数は、全種類のイベントでトリガーすることができます。ここでは、5 分毎に Lambda 関数 を実行させます。

CloudWatch Events では、この機能のための、スケジューリング パターンが設定できます。

Lambda コンソールの [Designer] で CloudWatch Events をトリガーに選択し、Create a New Rule として作成します。

Rule Name: PICounterMetrics5M を選択します。

Rule Type: Schedule Expression を選択します。

Schedule Expression: rate (5 minutes) を選択します。

関数をテストする

[Test] をクリックして新規のテストイベントを作成します。

Event Template: Amazon CloudWatch を選択します。

Name: ScheduledTest を選択します。

 

その他のデフォルト値を受け入れ保存します。

これら設定を保存すると、[Test] をクリックして「Hello from Lambda.」と記述されたログを確認できるようになります。

ステップ 2: Performance Insights API を呼び出すように Lambda 関数を修正する

ここまでで作成した Lambda 関数は何の処理も行いません。このステップでは、Performance Insights API を呼び出し、カウンターメトリクスをプリントするように修正します。

データベースのリソース ID を見つける

Amazon RDS コンソールでインスタンスを選択し、[Configuration] タブに表示されるデータベースリソース ID を確認します。この名称は「db-」で開始されているはずです。この例では、ID は db-YTDU5J5V66X7CXSCVDFD2V3SZM となっています。

クエリの対象となるメトリクスを見つける

次のスクリーンショットに示すように、Performance Insights Counter Metrics のページで設定ウィジェットを選択することで、表示させるメトリクスを設定することができます。手始めに、比較的に安定していると思われる、os.general.numVCPUs という OS メトリクスのクエリを送ってみます。クエリを送る際は、メトリクス名に統計値を組み合わせます。この例では平均値 (average) のクエリを送っているので、os.general.numVCPUs.avg となります。

Lambda 関数の IAM role を修正する

IAM で、先に作成しておいた PerformanceInsightsCounterMetricsRole を見つけます。次のアクセス権限ポリシーを含む PerformanceInsightsFullAccess ポリシーを作成および追加することで、Performance Insights API 呼び出しの権限を追加します。

{
"Version": "2012-10-17",
"Statement": [
{
"Action": ["es:*"],
"Effect": "Allow",
"Resource": "arn:aws:pi:*:*:metrics/rds/*"
}
]
}

次のコードで Lambda 関数を修正します。

import time
import boto3
import logging


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

# TODO – Change this to your instance ID from Step 2.1
instance_id = 'db-YTDU5J5V66X7CXSCVDFD2V3SZM'


def lambda_handler(event, context):
    get_resource_metrics(instance_id)

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


def get_resource_metrics(resource_id):
    pi_client = boto3.client('pi')
    response = pi_client.get_resource_metrics(
        ServiceType='RDS',
        Identifier=resource_id,
        StartTime=time.time() - 300,
        EndTime=time.time(),
        PeriodInSeconds=60,
        MetricQueries=[{'Metric': 'os.general.numVCPUs.avg'}]
    )

    logger.debug("response={}", response)
    return response

Lambda 関数を再度実行すると成功のレスポンスが返され、表示されるログには、次のスクリーンショットにあるようなレスポンス内容が含まれているはずです。

レスポンスには、次のようなフィールドが含まれています。

"AlignedStartTime": 1.55296386E9,
"AlignedEndTime":1.55296416E9,
"Identifier":”db-YTDU5J5V66X7CXSCVDFD2V3SZM”
"MetricList":[
{
    "DataPoints":[
       {"Timestamp":1.55296392E9,"Value":2.0},
       {"Timestamp":1.55296398E9,"Value":2.0},
       {"Timestamp":1.55296404E9,"Value":2.0},
       {"Timestamp":1.5529641E9,"Value":2.0},
       {"Timestamp":1.55296416E9,"Value":2.0}
    ],
    "Key":{"Metric":"os.general.numVCPUs.avg"}
}
]
  • AlignedStartTime/AlignedEndTime: 分解能に合わせ変換された、クエリの StartTime および EndTime です。Performance Insights では、指定された期間のデータベース活動を大まかに事前計算します (2 年の期間がオプションになっているのはこのためです) 。しかし、任意の時間スパンではクエリができません。
  • Identifier: クエリのパラメータとして渡される DbiResourceId です。
  • MetricList: 渡された全 MetricQuery エントリ (この例では 1 つだけ) の中の 1 つです。各レスポンスに、KeyDataPoints のリストが含まれます。

ステップ 3: RDS API と統合し複数のカウンターメトリクスを送信させるため、Lambda 関数を修正する

このステップでは、Lambda 関数を修正して、複数のカウンターメトリクスと、Performance Insights が有効化された全 Amazon RDS インスタンスからのデータを送信するようにします。

RDS API を呼び出すための権限を追加するため、Lambda 関数を修正する

IAM console で、PerformanceInsightsCounterMetricsRole を選択してから、AmazonRDSReadOnlyAccess ポリシーをアタッチします。

次のコードで Lambda 関数を修正します。

import time
import boto3
import logging


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

# Configure different metrics for different engines 
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:
        get_resource_metrics(instance)

    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

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

このコードブロックの先頭で定義する engine_metrics ディクショナリで、メトリクスのリストへ関係するエンジン名をマッピングしています。このマップを変更すると、取得するカウンターメトリクスを正確に指定できます。

ステップ 4: Performance Insights データを CloudWatch へ送るように Lambda 関数を修正する

最後のステップでは、Performance Insights データを CloudWatch へ送るように Lambda 関数を修正します。

IAM コンソールで、PerformanceInsightsCounterMetricsRole を選択し、CloudWatchFullAccess の権限をアタッチします。

Lambda 関数は、次のキーパラメータを使い PutMetricData を呼び出すようになります。

    • Namespace: PerformanceInsights が指定されます。
    • Dimensions: ディメンションとして InstanceId が追加されます。
    • MetricName/Value/Timestamp: Performance Insights API が返す値が指定されます。

修正後の Lambda 関数は次のとおりです。

import time
import boto3

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():
    response = rds_client.describe_db_instances()

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


def get_resource_metrics(instance):
    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
        )

コード修正が終わったら、ScheduledTest のイベント名で正常に動作するか再度確認してください。

CloudWatch でのデータ表示

以上で修正は終わりです。 CloudWatch に戻ると、次のスクリーンショットどおりに PerformanceInsights というカスタム名前空間が追加したメトリクスと並び新しく表示されているはずです。

結論

このブログ記事では、Performance Insights API と Lambda 関数を接続することで、Performance Insights のカウンターメトリクスを CloudWatch に挿入する方法をご紹介いたしました。加えて、Performance Insights API について、特に GetResourceMetrics を手短かに解説いたしました。

次回のブログでは、Performance Insights API について、さらに詳しく説明しようと考えています。

 


著者について

Andrew McNair はシニアソフトウェアエンジニアとして、Amazon Web Services の RDS で働いています。お客様が、データベースのパフォーマンスを詳しく調べられる Performance Insights は、彼のチームが開発しました。