Amazon Web Services ブログ

AWS CloudFormation を使用して Amazon DynamoDB テーブルとインデックスの Auto Scaling を設定する方法

AWS リソースをデプロイする上でのベストプラクティスは、Infrastructure as Code を扱う構成システムを使用することです。Infrastructure as Code は、開発者とオペレーションを連携させてアプリケーション配信を自動化する DevOps プラクティスの成功の鍵です。インフラストラクチャ全体を AWS CloudFormation テンプレートのコードとしてモデリングすることで、次のような利点があります。

  • クラウドソース用の信頼できる唯一の情報源を確立
  • 開発環境とテスト環境のデプロイメントの自動化
  • 災害対策の準備
  • バージョン管理などのコード管理ツールとの統合

Amazon DynamoDB などのサーバレスデータベースと組み合わせて AWS CloudFormation テンプレートを使用することで、アジャイルな DevOps プロセスを採用した開発チームの高速なオペレーションを大幅に簡素化できます。この記事では、実際の例と、AWS CloudFormation を使用して DynamoDB の Auto Scaling を設定する方法をご説明します。

注意: この記事では、ソリューションについて説明するためのスクリーンキャストを作成しました。どうぞお試しください。

DynamoDB の Auto Scaling

DynamoDB は、あらゆる規模で一貫性のある 1 桁ミリ秒台のレイテンシーを必要とするアプリケーション向けの、完全マネージド型で、高速の、スケーラブルかつ柔軟なクラウドデータベースサービスです。2017 年、AWS は DynamoDB に Auto Scaling 機能を追加しました。Auto Scaling を使用すると、予測不能なパフォーマンスのニーズを持つアプリケーションの管理を簡素化することができます。Auto Scaling では、テーブルとグローバルセカンダリインデックスの読取りおよび書込み容量の上限と下限、およびターゲット使用率を構成できます。

AWS CloudFormation

開発者と管理者は AWS CloudFormation を使用し、JSON 形式または YAML 形式のテキストファイルでクラウドインフラストラクチャを定義できます。DynamoDB テーブル、Amazon S3 バケット、Amazon Simple Notification Service (Amazon SNS) のトピックなどのリソースを、AWS マネジメントコンソールを使って作成したり構成したりする代わりに、開発者と管理者はそのリソースをコードのように扱うことができます。AWS CloudFormation テンプレートをソース管理に登録して、他のコードファイルと同じように管理できます。テンプレートファイルを使用してプログラムですべてのリソースを作成することで、環境全体で再現性と一貫性を実現し、コンソールの使用を検出とトラブルシューティングに限定することが可能です。

ユースケース

この記事では、Web アプリケーションのユーザープロファイルを格納する単一のテーブルの作成について説明します。アプリケーションのユーザーは、世界中に広がっています。マーケティング部門がサインアップを促進するキャンペーンを実施しようとしているが、ユーザー数がどれくらいになるかは把握するのが難しいという状況を想定しましょう。ユーザー数は数千人かもしれませんし、数百万人かもしれません。変動する同時ユーザー数に基づいて、データベースが任意の増減を確実に処理できるようにする必要があります。ユーザーは所在する都市ごとにグループ分けされ、サインアップ日に応じてサポートリクエストが優先されます。そのため、都市名とサインアップの日付でソートできる必要があります。

データモデル

作成するテーブルには、userId という名前のプライマリパーティションキーと、city という名前のパーティションキーと signupDate という名前のソートキーを持つ 1 つのグローバルセカンダリインデックスが含まれます。インデックスにより、開発者は Query API を使用して、サインアップ日付でソートされた特定の都市のユーザーリストを、効率的に取得することができます。

AWS CloudFormation テンプレート

テーブル名 (userTableName) とインデックス名 (userIndexName) のパラメータを使ってテンプレートを開始します。これで、スタック (単一のユニットとして管理できる AWS リソースの集合) を作成する人が、必要に応じて名前を変更することができます。

AWSTemplateFormatVersion: "2010-09-09"
Parameters: 
 userTableName: 
  Type: String
  Default: "myapp.User"
  Description: "Name of the user table"
 userIndexName: 
  Type: String
  Default: "user-city-index"
  Description: "Name of the user index"

AWS CloudFormation リソース (AWS :: DynamoDB :: Table) を作成して、テーブルの構成を開始します。

AWS::DynamoDB::Table.
Resources: 
 tableMyAppUser: 
  Type: "AWS::DynamoDB::Table"
  Properties: 
   TableName: 
    Ref: userTableName
   AttributeDefinitions: 
    - 
     AttributeName: userId
     AttributeType: S
    - 
     AttributeName: city
     AttributeType: S
    - 
     AttributeName: signupDate
     AttributeType: S

上記のコードで、プライマリキーの一部またはインデックスの一部である属性を定義します。ただし、ユーザープロファイルの一部として保存する可能性のある他のフィールドは、定義する必要はありません。これは、DynamoDB はスキーマレスなので、ランタイム中にテーブルの定義を変更せずに非キー属性を追加できるためです。

   KeySchema: 
    - 
     AttributeName: userId
     KeyType: HASH
   ProvisionedThroughput: 
    ReadCapacityUnits: 5
    WriteCapacityUnits: 5

プロビジョニングしたスループット設定 (ReadCapacityUnitsWriteCapacityUnits) は、Auto Scaling の開始点としてテーブルに設定される初期値です。

次に、グローバルインデックスを定義します。これを定義することで、都市ごとにデータをクエリし、サインアップ日別にソートされたユーザーのリストを取得することができます。

   GlobalSecondaryIndexes: 
    - 
     IndexName: 
      Ref: userIndexName
     KeySchema: 
      - 
       AttributeName: city
       KeyType: HASH
      - 
       AttributeName: signupDate
       KeyType: RANGE
     Projection: 
      ProjectionType: ALL
     ProvisionedThroughput: 
      ReadCapacityUnits: 5
      WriteCapacityUnits: 5

ProjectionTypeALL を指定すると、インデックス自体にすべての属性を格納するように DynamoDB を設定できます。これにより、インデックスを使用する Query コマンドを作成する際にセカンダリルックアップが不要になります。

次にテンプレートで定義するのは、ScalingRole です。これは AWS Identity and Access Management (IAM) のロールで、Auto Scaling のスケーラブルターゲットを変更することを許可します。

 ScalingRole: 
  Type: "AWS::IAM::Role"
  Properties: 
   AssumeRolePolicyDocument: 
    Version: "2012-10-17"
    Statement: 
     - 
      Effect: Allow
      Principal: 
       Service: 
        - 
         "application-autoscaling.amazonaws.com"
      Action: 
       - 
        "sts:AssumeRole"
   Path: "/"
   Policies: 
    - 
     PolicyName: root
     PolicyDocument: 
      Version: "2012-10-17"
      Statement: 
       - 
        Effect: Allow
        Action:          
         - "dynamodb:DescribeTable"
         - "dynamodb:UpdateTable"
         - "cloudwatch:PutMetricAlarm"
         - "cloudwatch:DescribeAlarms"
         - "cloudwatch:GetMetricStatistics"
         - "cloudwatch:SetAlarmState"
         - "cloudwatch:DeleteAlarms"
        Resource: "*"

前述のポリシーで、Amazon CloudWatch の役割にすでにお気づきかもしれません。CloudWatch は、AWS リソースの集中監視サービスです。DynamoDB では、テーブルの消費量の情報を継続的に保持するメカニズムが必要なため、DynamoDB の Auto Scaling で CloudWatch が必要になります。

次に、テンプレートの Auto Scaling の部分です。最初にスケーラブルターゲットを構成して、ユーザーテーブルの書き込みの上限と下限を設定します。

 UserTableWriteCapacityScalableTarget: 
  Type: "AWS::ApplicationAutoScaling::ScalableTarget"
  Properties: 
   MaxCapacity: 100
   MinCapacity: 5   
   ResourceId: !Sub table/${userTableName}
   RoleARN: !GetAtt ScalingRole.Arn
   ScalableDimension: "dynamodb:table:WriteCapacityUnits"
   ServiceNamespace: dynamodb

このように、ScalableTargetは、最小 5 回から最大 100 回までの書き込み容量単位の間でスケーリングするように構成されています。

次に、スケーリングポリシーを作成します。スケーリングポリシーでは、スケーリングターゲットを参照しターゲット消費量を設定します。

 UserTableWriteScalingPolicy: 
  Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
  Properties: 
   PolicyName: WriteAutoScalingPolicy
   PolicyType: TargetTrackingScaling
   ScalingTargetId: 
    Ref: UserTableWriteCapacityScalableTarget
   TargetTrackingScalingPolicyConfiguration: 
    TargetValue: 70
    ScaleInCooldown: 60
    ScaleOutCooldown: 60
    PredefinedMetricSpecification: 
     PredefinedMetricType: DynamoDBWriteCapacityUtilization

TargetValue はパーセンテージで、DynamoDB がいつスケールイン、またはスケールアウトするかを示します。ここでは 70 に設定されています。これは、現在のプロビジョニングされたスループットの 70% という意味です。たとえば、現在の書き込み容量単位の設定が 10 で、60 秒間 に 毎秒 7 回を超える書き込み容量単位を消費するとします。消費が現在の設定の 70% を超えたため、スケールアウトイベントが発生します。新しくプロビジョニングされた容量は、現在の消費量がプロビジョニングされた容量の 70% に相当するように設定されます。現在、14 回の書き込み容量単位を消費している場合、Auto Scaling では、プロビジョニングされた書き込み容量を 20 に設定します。これは、14 が 20 の 70% に相当するからです。

ScaleInCooldownScaleOutCooldown は、プロビジョニングされたスループットを変更するまで待機する秒数です。これらの値を調整して、DynamoDB が短期間に発生する変更に対して、積極的になりすぎないよう設定することができます。

今度は、グローバルセカンダリインデックスのスケーラブルターゲットとスケーリングポリシーを作成する必要があります。AWS CloudFormation テンプレートのこの箇所は、テーブル設定とほぼ同じなので、ここでは全体を示すことはしません。代わりに、重要な違いをいくつか挙げたいと思います。

 UserIndexWriteCapacityScalableTarget: 
  Type: "AWS::ApplicationAutoScaling::ScalableTarget"
  Properties: 
   MaxCapacity: 100
   MinCapacity: 5
   ResourceId: !Sub table/${userTableName}/index/${userIndexName}
   RoleARN: !GetAtt ScalingRole.Arn
   ScalableDimension: "dynamodb:index:WriteCapacityUnits"
   ServiceNamespace: dynamodb

テーブルの ResourceId と同じResourceId/index/${userIndexName} が追加されており、ScalableDimension はインデックスの書き込み容量単位に設定されています。

これでテーブルとインデックスの書き込み用の Auto Scaling が構成されたので、読み取りに対して同じ手順を繰り返します。読み込み容量の詳細については、AWS CloudFormation テンプレート (JSONYAML) を参照してください。

スタックを作成する

テンプレートが完成したので、次にスタックを作成して、期待されるリソースの取得を確認できます。

  1. [Launch Stack] を選択し、AWS CloudFormation コンソールを開きます。
    Launch stack
  2. [テンプレートの選択] ページで、[次へ] を選択します。
  3. [詳細の指定] ページで、スタック、インデックス、およびテーブルに別の名前を指定できます。次に、[次へ] を選択します。
  4. [オプション] ページの設定は、本記事ではスキップします。[Next] を選択します。
  5. 最後に、テンプレートによってアカウントに IAM リソースが作成される場合があることを承認する必要があります。チェックボックスをオンにして、[作成] を選択します。

テンプレートが完成したので、次にスタックを作成して、期待されるリソースの取得を確認できます。スタックの作成には 2 分程度かかります。

AWS CloudFormation がスタックを作成したら、DynamoDB コンソールに移動します。新しく作成されたテーブルを選択し、以下のスクリーンショットのように [キャパシティー] タブを選択して Auto Scaling の設定詳細を表示します。Auto Scaling の設定詳細を含む [キャパシティー] タブのスクリーンショット

[インデックス] タブで、読み込みと書き込みの両方で Auto Scaling が有効になっていることを確認できます (Auto Scaling 列に READ_AND_WRITE と表示されます)。

読み取りと書き込みの両方で Auto Scaling が有効になっていることを示す [インデックス] タブのスクリーンショット

Auto Scaling では、消費スループットとプロビジョニングされたスループットに関連する CloudWatch アラームも作成されました。スケーリングポリシーごとに 4 件のアラーム (合計 16 件) が表示されます。以下のスクリーンショットを作成するために、スクリプトを実行してデータをテーブルに追加してから、一連の ScanQuery オペレーションを送信し、アラームが OK 状態を表示するのに十分な使用を提供しました。スタックを作成後、テーブルにアイテムを追加する前に CloudWatch に直接移動すると、いくつかのアラームが INSUFFICIENT_DATA 状態になります。

Auto Scaling によって作成された CloudWatch アラームのスクリーンショット

まとめ

AWS CloudFormation テンプレートを使用して、Auto Scaling を有効に設定した DynamoDB テーブルが作成できました。 このテンプレートを、自分のテーブル用に変更して使用してください。

コンソールを使って新しいテーブルを変更しないようにしましょう。代わりに、テンプレートを変更して AWS CloudFormation で変更セットを作成してください。変更セットを使用すると、変更を実行する前にスタックへの変更を表示できます。AWS CloudFormation は現在、テンプレートとテンプレートで定義されている実際のリソースの間の構成のドリフトを検出することができません。そのため、手動で変更を行った後からテンプレートを変更しようとすると、変更セットが失敗する場合があります。

このブログ記事に関するコメントは、以下のコメントセクションからお送りください。この記事で紹介したソリューションについて質問がある場合は、DynamoDB forum で新しいスレッドを開始してください。


著者について

Eric Z. Beard はアマゾン ウェブ サービスのパートナーソリューションアーキテクトです。 AWS のお客様と協力して、データベースプロジェクトに関する指導と技術支援を行い、AWS をしている場合にソリューションの価値の向上を目指しています。