Amazon Web Services ブログ

Amazon DynamoDB テーブルのストレージをモニタリングするサーバーレスソリューション

Amazon DynamoDB をアプリケーションの NoSQL データベースサービスとして使用する場合に、DynamoDB テーブルが使用するストレージ使用量を追跡したいとしましょう。DynamoDB は、サービスメトリックを Amazon CloudWatch に公開します。CloudWatch は、これらのメトリックをモニタリングおよび分析し、アラームを設定し、さらに AWS リソースの変更に対して自動的に反応します。現在のところ、DynamoDB は、ThrottledRequestsConsumedWriteCapacityUnits、および ConsumedReadCapacityUnits を含む、多くの有用なメトリックを CloudWatch に送信します。

DynamoDB テーブルのストレージ使用状況を分析し、DynamoDB ワークロードが使用するストレージ使用量の履歴を把握し、ストレージコストを管理することは重要です。例えば、Time to Live (TTL) を使用して、不要なデータや陳腐化したデータを期限切れにしたいとします。そのような場合、ストリームを使用してそのデータを Amazon S3 または Amazon Glacier にアーカイブすることができるのです。この記事では、DynamoDB テーブルのストレージ使用状況をモニタリングする方法について解説します。

ソリューションの概要

DynamoDB は、6 時間ごとにテーブルのストレージ使用状況を更新します。この情報は、DynamoDB DescribeTable API を使用することで取得できます。テーブルのサイズを決定したら、カスタム CloudWatch メトリックを作成して、データを CloudWatch にプッシュできます。このソリューションでは、AWS CloudFormation を使用して、DynamoDB テーブルのストレージ使用状況をモニタリングするワンクリックデプロイメントアーキテクチャを作成する方法を解説します。これはインフラストラクチャをコードモデルとして実装するために必要です。

次の Python スクリプトと AWS CloudFormation テンプレートは、この GitHub リポジトリで入手できます。ソリューションの概要プロセスの仕組みは、上の図のようになります。(このソリューションは、DynamoDB ワークロードのあるすべての AWS リージョンでデプロイし、DynamoDB テーブルのストレージ使用量を継続してモニタリングすることを忘れないでください。)

  1. Amazon CloudWatch Events は、AWS Lambda 関数をスケジュールに従って実行します。
  2. Lambda 関数は、DescribeTable API を使って、リージョン内のすべての DynamoDB テーブルのサイズを確認します。
  3. Lambda 関数はテーブルサイズ情報を、カスタム CloudWatch メトリックに格納します。
  4. Lambda は、AWS CloudFormation テンプレートが作成したカスタムの AWS Identity and Access Management (IAM) ロールを使用して、DynamoDB および CloudWatch にアクセスします。
  5. AWS CloudFormation は、IAM ロールと Lambda 関数の作成、イベントのスケジューリングなど、ソリューション全体のデプロイを行います。

DynamoDB テーブルのストレージ使用状況をモニタリングする

DynamoDB テーブルのストレージ使用状況をモニタリングするには、次の手順に従います。

  1. ddbstoragemon.py というファイルを作成し、次のコードをファイルに貼り付けます。このサンプルコードでは、特定の AWS リージョン内のすべてのテーブルに対して DescribeTable API を呼び出し、API レスポンスから TableSizeBytes を読み取り、その情報を DynamoDBStorageMetrics と呼ばれるカスタムの CloudWatch メトリックに配置します。
    #Python Code.Version: Python 3.6
    # Copyright 2017-2018 Amazon.com, Inc. or its affiliates.All Rights Reserved.
    #
    # Licensed under the Apache License, Version 2.0 (the "License").ライセンスに従わない限り、このファイルを使用することはできません。License のコピーは、次の場所にあります。
    #
    #    http://thinkwithwp.com/apache2.0/
    #
    # またはこのファイルに添付されている "license" ファイルにあります。このコードは、明示的にも黙示的にも、いかなる種類の保証や条件もなく、"現状有姿" で配布されます。License の下での言語に基づくアクセス権限と制限については、License を参照してください。
    # version 0.1
    
    
    from __future__ import print_function
    from datetime import date, datetime, timedelta
    import json
    import boto3
    import time
    from botocore.exceptions import ClientError
    import os
    
    ddbRegion = os.environ['AWS_DEFAULT_REGION']
    ddbTable = 'hashrange'
    ddbClient = boto3.client('dynamodb', region_name=ddbRegion)
    
    
    cwClient = boto3.client('cloudwatch')
    
    def lambda_handler(event, context):
        
        response = ddbClient.list_tables()
        if 'LastEvaluatedTableName' in response:
            LastEvaluatedTableName = response['LastEvaluatedTableName']
        else:
            LastEvaluatedTableName = ''
        
        listTables=response['TableNames']
        for tablename in listTables:
            responseTableSize= ddbClient.describe_table(TableName=tablename)
            TableSizeBytes=responseTableSize['Table']['TableSizeBytes']
            print(tablename,'-',responseTableSize['Table']['TableSizeBytes'])
            responseCW = cwClient.put_metric_data(Namespace='DynamoDBStorageMetrics',
                MetricData=[
                    {
                        'MetricName': 'StorageMetrics',
                        'Dimensions': [
                            {
                                'Name': 'tablename',
                                'Value': tablename
                            },
                        ],
                        'Timestamp': datetime.now(),
                        'Value': TableSizeBytes,
                        'Unit': 'Bytes',
                        'StorageResolution': 1
                    },
                ]
                )
            
    			
        while (LastEvaluatedTableName != ''):
            response = ddbClient.list_tables(ExclusiveStartTableName=LastEvaluatedTableName)
            if 'LastEvaluatedTableName' in response:
                LastEvaluatedTableName = response['LastEvaluatedTableName']
            else:
                LastEvaluatedTableName = ''
                
            listTables=response['TableNames']
            for tablename in listTables:
                responseTableSize= ddbClient.describe_table(TableName=tablename)
                TableSizeBytes=responseTableSize['Table']['TableSizeBytes']
                print(tablename,'-',responseTableSize['Table']['TableSizeBytes'])
        
                responseCW = cwClient.put_metric_data(Namespace='DynamoDBStorageMetrics',
                MetricData=[
                    {
                        'MetricName': 'StorageMetrics',
                        'Dimensions': [
                            {
                                'Name': 'tablename',
                                'Value': tablename
                            },
                        ],
                        'Timestamp': datetime.now(),
                        'Value': TableSizeBytes,
                        'Unit': 'Bytes',
                        'StorageResolution': 1
                    },
                ]
                )
  2. ddbstoragemon.py ファイルを圧縮し、ddbstoragemon.zip として保存します。
  3. .zip ファイルを Amazon S3 にアップロードする

AWS CloudFormation を使用して、コードをデプロイする

コードが Amazon S3 に配置されたので、AWS CloudFormation を使ってソリューション全体をデプロイします。

  1. 次の CloudFormation サンプルコードをファイルに貼り付け、ファイルを ddbStoreMonCF.json として保存します。このコードでは、次の項目が作成されます。
    1. DynamoDB DescribeTable API にアクセスし、DynamoDB テーブルサイズを取得し、その情報を CloudWatch に格納するための IAM ロール。
    2. Amazon S3 に格納された Python スクリプトを使用する Lambda 関数。
    3. 6 時間ごとに Lambda 関数をトリガーする CloudWatch Events ルール。

    AWS CloudFormation JSON テンプレート

    {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Description": "Sample template to monitor a DynamoDB table's storage use- V0.02",
        "Parameters": {
            "BucketName":{
                "Type": "String",
                "Description": "Enter bucket name"
                },
            "FileName":{
                "Type": "String",
                "Description": "Enter file name"
                }
            },
        "Resources": {
            "LambdaServiceExecutionRole": {
                "Properties": {
                    "AssumeRolePolicyDocument": {
                        "Statement": [
                            {
                                "Action": [
                                    "sts:AssumeRole"
                                ],
                                "Effect": "Allow",
                                "Principal": {
                                    "Service": [
                                        "lambda.amazonaws.com"
                                    ]
                                }
                            }
                        ],
                        "Version": "2012-10-17"
                    },
                    "Path": "/",
                    "Policies": [{
                        "PolicyName": "root",
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [{
                                "Effect": "Allow",
                                "Action": ["cloudwatch:PutMetricData","logs:CreateLogStream","logs:PutLogEvents"],
                                "Resource": "*"
                            },
                            {
                                "Effect": "Allow",
                                "Action": [ "dynamodb:ListTables", "dynamodb:DescribeTable"],
                                "Resource": "*"
                            }]
                        }
                    }]
                },
                "Type": "AWS::IAM::Role"
            },
            "scheduledEvent": {
                "Properties": {
                    "Description": "CloudWatch Events event to trigger the Lambda function",
                    "ScheduleExpression": "rate(6 hours)",
                    "State": "ENABLED",
                    "Targets": [
                        {
                            "Arn": { "Fn::GetAtt" : ["ddbstoragemon", "Arn"] },
                            "Id": "DDBTarget"
                        }
                    ]
                },
                "Type": "AWS::Events::Rule"
            },
            "LambdaInvokePermission": {
              "Type": "AWS::Lambda::Permission",
              "Properties": {
                "FunctionName" : { "Fn::GetAtt" : ["ddbstoragemon", "Arn"] },
                "Action": "lambda:InvokeFunction",
                "Principal": "events.amazonaws.com",
                "SourceArn" : { "Fn::GetAtt" : ["scheduledEvent", "Arn"] }
              }
            },
            "ddbstoragemon": {
                "Properties": {
                    "Code": {
                        "S3Bucket": {"Ref": "BucketName"},
                        "S3Key": {"Ref": "FileName"}
                        
                    },
                    "Handler": "ddbstoragemon.lambda_handler",
                    "MemorySize": 128,
                    "Role": {
                        "Fn::GetAtt": [
                            "LambdaServiceExecutionRole",
                            "Arn"
                        ]
                    },
                    "Runtime": "python3.6",
                    "Timeout": 300
                },
                "Type": "AWS::Lambda::Function"
            }
            }
        }
  2. AWS マネジメントコンソールにサインインし、AWS CloudFormation コンソールに移動します。
  3. [Create Stack] を選択します。
  4. Select Template」ページで、[Upload a template to Amazon S3] を選択します。[Browse] を選択し、先に作成した ddbStoreMonCF.json テンプレートを選択します。
  5. [Next] を選択します。以前に Amazon S3 にアップロードした .zip ファイルの Stack name、Amazon S3 BucketNameFileName を入力します。スタックの詳細を指定するスクリーンショット
  6. [Next] を選択します。「Options」ページでデフォルト設定を維持して、[Next] を選択します。
  7. [I acknowledge that AWS CloudFormation might create IAM resources] チェックボックスを選択し、[Create] を選択します。「AWS CloudFormation が IAM リソースを作成する可能性があることを確認します」チェックボックスを選択し、[Create] を選択する

AWS CloudFormation はテンプレート定義に基づいてリソースを作成します。数分後、次のスクリーンショットに示すように、CloudFormation が、スタックを正常に作成したことを確認する必要があります。正常に作成されたスタックのスクリーンショット

設定をテストする

AWS CloudFormation テンプレートが作成した CloudWatch ルールは、6 時間ごとにトリガーし、関連するメトリックを CloudWatch に保存します。構成が正常に動作していることを確認するため、簡単なテストを行いましょう。

  1. CloudWatch コンソールに移動し、ナビゲーションペインで [Rules] を選択します。
  2. AWS CloudFormation スタック ddbstoragemon をその名前の中に持つルールを探して、それを選択します。CloudWatch ルールの詳細のスクリーンショット
  3. このルールは、6 時間ごとに Lambda 関数をトリガーするようにスケジュールされています。AWS CloudFormation で作成された Lambda 関数を開くには、Resource名を選択します。
  4. AWS Lambda コンソールで、[Test] を選択します。[Configure test event] ウィンドウで、イベント名を入力し、デフォルト値を受け入れ、[Create] を選択します。
  5. [Test] を選択して、関数を実行します。[Test] を選択して関数を実行する
    Lambda 関数は、その AWS リージョンのすべての DynamoDB テーブルのストレージメトリックを確認し、その情報をカスタム CloudWatch メトリック名前空間 DynamoDBStorageMetrics に配置します。
  6. CloudWatch コンソールに戻ります。[All metrics] タブ (Custom Namespaces の下にある) で、DynamoDBStorageMetrics を選択します。[All metrics] タブで DynamoDBStorageMetrics を選択する
  7. [TableName] を選択し、リストからテーブルを選択してストレージの使用状況を確認します。この例では、本番テーブルに関するメトリックを取得するため、数日間このソリューションを実行しました。

次のスクリーンショットは、先週の hashrange1 テーブルのストレージメトリックを示しています。hashrange1 テーブルの先週のストレージメトリクスのスクリーンショット

そして次のスクリーンショットは、hashrange1 テーブルの現在のサイズを示しています。hashrange1 テーブルの現在のサイズのスクリーンショット

これらのメトリックは、例えば次のような是正処置を取るのに役立ちます。

  1. テーブルが突発的な増大したかを、確認することができます。このような場合は、増大が「新しい標準」を表すのか、あるいは最近のコード変更により不具合が発生したのかを知るために、開発チームに確認する必要があります。
  2. テーブルが許容可能なストレージサイズを超えている場合は、関連する CloudWatch メトリックにアラームを設定できます。

まとめ

この記事では、サーバーレスアプリケーションをデプロイして、DynamoDB テーブルのストレージをモニタリングする方法を紹介しました。例えば、テーブルが指定サイズを超えた時に、Lambda からオペレーションチームに Amazon SNS 通知を送信することで、このソリューションを強化できます。

このソリューションの実装について質問がある場合は、下のコメント欄で尋ねてください。


著者について

Masudur Rahaman Sayem はアマゾン ウェブ サービスのソリューションアーキテクトです。 AWS を使用している場合にソリューションの価値を向上させる手助けとなるために、AWS の顧客と協力してデータベースプロジェクト上の指導や技術支援を行っています。