New RelicでAmazon DynamoDBのモニタリング統合をやってみた

記事タイトルとURLをコピーする

New Relic 使ってみた情報をシェアしよう! by New Relic Advent Calendar 2023 シリーズ1、18日目のエントリー記事です。

qiita.com

こんにちはマネージドサービス部 大城です。

New RelicでAmazon DynamoDBのモニタリング統合をやってみました。

DynamoDB作成からNew Relicへの連携、ダッシュボード作成まで一通り書いていきます。画像多めで少し長いのでお急ぎの方は下のまとめを見てください。

DynamoDB作成

まずはDynamoDBを作成します。今回AWSのドキュメント通りに Music テーブルを作成しました。

ステップ 1: テーブルを作成します - Amazon DynamoDB

AWSマネージメントコンソールにログイン、東京リージョンに切替後、AWS CloudShell にて下記を実行します。

スロットリング発生の検証をしたいので ReadCapacityUnits=1,WriteCapacityUnits=1 で作成しています。

aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions \
        AttributeName=Artist,AttributeType=S \
        AttributeName=SongTitle,AttributeType=S \
    --key-schema \
        AttributeName=Artist,KeyType=HASH \
        AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=1,WriteCapacityUnits=1 \
    --table-class STANDARD

動作確認のためアイテムをputします。

aws dynamodb put-item \
    --table-name Music  \
    --item \
        '{"Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Call Me Today"}, "AlbumTitle": {"S": "Somewhat Famous"}, "Awards": {"N": "1"}}'

aws dynamodb put-item \
    --table-name Music  \
    --item \
        '{"Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Howdy"}, "AlbumTitle": {"S": "Somewhat Famous"}, "Awards": {"N": "2"}}'

アイテムがgetできるか確認します。

aws dynamodb get-item --consistent-read \
    --table-name Music \
    --key '{ "Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Call Me Today"}}'

# -> jsonが返ってきたらOK

AWS Integrationの設定

DynamoDBに限らずAWSのマネージドサービスをNew RelicでモニタリングするにはCloudWatchに出力されるメトリクスをNew Relicに連携する必要があります。

方法としては「API ポーリング」「CloudWatch メトリクスストリーム」の2つがあります。「API ポーリング」は非推奨となっているため「CloudWatch メトリクスストリーム」で設定していきます。

「API ポーリング」「CloudWatch メトリクスストリーム」の特徴については、弊社福田の記事もご参照ください。

blog.serverworks.co.jp

連携用のAPI key作成

New RelicにメトリクスをプッシュするためAPI keyを発行します。今回DynamoDB-Integrationという名前で作りました。

CloudWatch メトリクスストリームの設定

New Relic上で Infrastructure > AWS > DynamoDB をクリックします。

Use metric streams をクリックします。

IAMロールを作成するようガイド出るので、AWSマネジメントコンソールよりIAMロールを作成します。

ReadOnlyAccess をアタッチするようガイドが出るのでアタッチします。

IAMロールに名前をつけます。

ガイドでは「NewRelicInfrastructure-Integrations」を推奨していますが、今回「NewRelicInfrastructure-Integrations-test」としました。

AWS Budgetsのポリシー作成の案内が出ますが、今回不要なのでスキップしました。

AWS Integrationの名前を任意でつけます、なんでも良さそうですが今回AWSアカウントIDを設定しました。

作成したIAMロールのARNを入力します。

AWSマネジメントコンソールにサインインしている状態でNew Relic画面上の「Use the CloudFormation Template」をクリックすると、CloudFormationの画面に遷移します。

上で発行したNew RelicのAPI keyを入力しCloudFormationを実行します。

実行が終わると下のリソースが作成されます。

CloudWatch メトリクスストリームの名前空間指定

そのままだとCloudWatch すべての名前空間が転送対象となります。

連携が必要な名前空間だけに絞ります。今回は「AWS/DynamoDB: All metrics names」のみにしました。

ダッシュボード作成

ダッシュボードをテンプレートから作成

用意されているテンプレートを基にダッシュボードを作成してみました。

Dashboards > Create a dashboard をクリックします。

Browse pre-built dashboards をクリックします。

「dynamodb」を検索し出てきたテンプレートをクリックします。

「View dashboard」をクリックします。

ダッシュボード内のNRQL見直し

テンプレートで上手く値が取れないものがあったので、少しNRQLを見直しました。見直ししたNRQLを記載します。

(長いのでクリックしてNRQLを表示してください)

① Request latency, by operation

SELECT average(`aws.dynamodb.SuccessfulRequestLatency.get`) as 'GetItem', average(`aws.dynamodb.SuccessfulRequestLatency.put`) as 'PutItem', average(`aws.dynamodb.SuccessfulRequestLatency.delete`) as 'DeleteItem', average(`aws.dynamodb.SuccessfulRequestLatency.update`) as 'UpdateItem', average(`aws.dynamodb.SuccessfulRequestLatency.query`) as 'Query', average(`aws.dynamodb.SuccessfulRequestLatency.scan`) as 'Scan', average(`aws.dynamodb.SuccessfulRequestLatency.batchGet`) as 'BatchGetItem', average(`aws.dynamodb.SuccessfulRequestLatency.batchWrite`) as 'BatchWriteItem' FROM Metric TIMESERIES

② Throttled requests

SELECT sum(`aws.dynamodb.WriteThrottleEvents`) as 'Write', sum(`aws.dynamodb.ReadThrottleEvents`) as 'Read' FROM Metric

③ System Errors

select sum(`aws.dynamodb.SystemErrors.batchWrite`) as batchWriteError, sum(`aws.dynamodb.SystemErrors.delete`) as deleteError, sum(`aws.dynamodb.SystemErrors.get`) as getError, sum(`aws.dynamodb.SystemErrors.put`) as putError, sum(`aws.dynamodb.SystemErrors.query`) as queryError, sum(`aws.dynamodb.SystemErrors.scan`) as scanError, sum(`aws.dynamodb.SystemErrors.update`) as updateError FROM Metric TIMESERIES

④ User Errors

SELECT sum(`aws.dynamodb.UserErrors.byRegion`) FROM Metric TIMESERIES

⑤ Write throttled requests

SELECT sum(`aws.dynamodb.WriteThrottleEvents`) as 'Write Throttled Requests' FROM Metric TIMESERIES

⑥ Read throttled requests

SELECT sum(`aws.dynamodb.ReadThrottleEvents`) as 'Read Throttled Requests' FROM Metric TIMESERIES

⑦ WriteCapacity %

SELECT 100 * average(`aws.dynamodb.ConsumedWriteCapacityUnits`) / average(`aws.dynamodb.ProvisionedWriteCapacityUnits`) as WriteCapacityPer FROM Metric TIMESERIES

⑧ ReadCapacity %

SELECT 100 * average(`aws.dynamodb.ConsumedReadCapacityUnits`) / average(`aws.dynamodb.ProvisionedReadCapacityUnits`) as ReadCapacityPer FROM Metric TIMESERIES

付録

DynamoDBでSystemErrorやスロットリングを発生させる方法を参考までに記載します。

普段DynamoDBをオペレーションすることがないので、実は一番ここが苦労したところでした。

SystemError

SystemErrorを発生させてNew Relicに連携されるか確認します。実際にDynamoDBをオペレーションしてSystemErrorを発生させるのが、少し面倒だったので直接CloudWatchに書き込みました。

AWS CloudShellよりaws cliを実行してメトリクスの追加を行います。

コマンド

aws cloudwatch put-metric-data \
  --namespace 'AWS/DynamoDB' \
  --metric-name 'SystemErrors' \
  --value "10" \
  --unit 'Count' \
  --dimensions "TableName=Music","Operation=BatchWriteItem"

aws cloudwatch put-metric-data \
  --namespace 'AWS/DynamoDB' \
  --metric-name 'SystemErrors' \
  --value "10" \
  --unit 'Count' \
  --dimensions "TableName=Music","Operation=DeleteItem"

aws cloudwatch put-metric-data \
  --namespace 'AWS/DynamoDB' \
  --metric-name 'SystemErrors' \
  --value "10" \
  --unit 'Count' \
  --dimensions "TableName=Music","Operation=GetItem"

aws cloudwatch put-metric-data \
  --namespace 'AWS/DynamoDB' \
  --metric-name 'SystemErrors' \
  --value "10" \
  --unit 'Count' \
  --dimensions "TableName=Music","Operation=PutItem"


aws cloudwatch put-metric-data \
  --namespace 'AWS/DynamoDB' \
  --metric-name 'SystemErrors' \
  --value "10" \
  --unit 'Count' \
  --dimensions "TableName=Music","Operation=Query"

aws cloudwatch put-metric-data \
  --namespace 'AWS/DynamoDB' \
  --metric-name 'SystemErrors' \
  --value "10" \
  --unit 'Count' \
  --dimensions "TableName=Music","Operation=Scan"

aws cloudwatch put-metric-data \
  --namespace 'AWS/DynamoDB' \
  --metric-name 'SystemErrors' \
  --value "10" \
  --unit 'Count' \
  --dimensions "TableName=Music","Operation=UpdateItem"

書き込みスロットリング

WriteCapacityUnits=1でテーブルを作成しているため、ある程度書き込みを行えばすぐにスロットリングが発生します。書き込み用のrubyスクリプトを作りました。

put_items.rb

require 'aws-sdk-dynamodb'
require 'securerandom'

REGION = 'ap-northeast-1'
TABLE_NAME = 'Music'
from_count = ARGV[0].to_i
to_count = ARGV[1].to_i

dynamodb_client = Aws::DynamoDB::Client.new(region: REGION)
(from_count.to_i..to_count.to_i).each do |i|
  s = SecureRandom.uuid.to_s
  table_item = {
    table_name: TABLE_NAME,
    item: {
      Artist: s,
      SongTitle: s,
      AlbumTitle: s,
      Awards: i.to_s
    }
  }
  dynamodb_client.put_item(table_item)
  puts "put_item #{table_item.to_s}"
end

1000行のアイテムをテーブルに投入するには下のように実行します。

ruby put_items.rb 1 1000

読み込みスロットリング

こちらもReadCapacityUnits=1 でテーブルを作成しているため、ある程度読み込みを行えばすぐにスロットリングが発生します。読み込み用のrubyスクリプトを作りました。

get_items.rb

require 'aws-sdk-dynamodb'

REGION = 'ap-northeast-1'
TABLE_NAME = 'Music'

dynamodb_client = Aws::DynamoDB::Client.new(region: REGION)
scan_resp = dynamodb_client.scan({table_name: TABLE_NAME})
scan_resp.items.each do |item|
  prams = {
    key: {
      "Artist" => item["Artist"], 
      "SongTitle" => item["SongTitle"], 
    }, 
    table_name: TABLE_NAME,
  }
  get_resp = dynamodb_client.get_item(prams)
  puts "get_item #{get_resp.item.to_s}"
end

これを下記のように実行します。

ruby get_items.rb

まとめ

  • DynamoDBを新規で作成する
  • CloudWatch メトリクスストリームでAWS Integrationを設定する
  • テンプレートを基にダッシュボードを作成することができる
  • CloudWatchにメトリクスが追加されるとNew Relic側にも反映される

ここまで読んでいただきありがとうございます。この記事が誰かの役に立ったら幸いです。

APMに加えてAWSマネージドサービスもNew Relicに連携することでオブザーバビリティの幅が広がると思います。ぜひ試してみてください。

大城 慶明 (記事一覧)

マネージドサービス部

2022年10月サーバーワークスに入社、沖縄からリモート勤務。AWSを勉強中。沖縄では大城が多いので「よっさん」と呼ばれています。AWS12冠。X @yo_ohshiro