eviry tech & service blog

「株式会社エビリー」の社員ブログです。弊社では、クラウド型動画配信サービス「millvi」、ソーシャル動画データ及び分析サービス「kamui tracker」、YouTube総合メディア「かむなび」を開発・提供しています。https://eviry.com/

定期的にDynamoDBにリクエストを送る仕組みをterraformを使って構築する。

これは旧eviry tech blogから移行した記事です。

eviry開発のtkです。

AWS DynamoDBというNoSQLを提供してくれているサービスは、キャパシティを設定することで読み込み・書き込みをさばくことができます。
また、このキャパシティはオートスケーリングに対応しているので、急なリクエストの増加にも対応することが可能です。

ところが、このキャパシティのオートスケーリングは関して注意が必要です。
それは、リクエストが全くなくなるとスケールアウトしないから。
これは公式のドキュメントでも言及されています。

DynamoDB Auto Scaling によるスループットキャパシティの自動管理 - Amazon DynamoDB

これの対処法として、Cloudwatch Eventを使ってlambda経由で定期的にDynamoDBに定期的にリクエストを送り続ける、というものがあります。
今回はこの構築をterraformでやってみました。

まず、ファイル構成は以下のようになります。

.
├── lambda_function
│   └── main.py
└── main.tf

lambda_functionディレクトリにmain.pyを配置し、これをAWS Lambdaに登録します。

main.pyの実装は以下のようになっています。
今回は書き込みキャパシティのスケールアウトを想定したので、念の為書き込みリクエストを送るようにしています。
テーブル名やキー名などは適宜書き換えてください。

import boto3
import datetime

dynamodb = boto3.resource('dynamodb')

def lambda_handler(event, context):
    table = dynamodb.Table('tablename')

    table.put_item(
        Item={
            'tablekeyname': 'tablekeyvalue'
        }
    )

    return {
        'statusCode': 200,
        'body': 'Send Request success'
    }

次に、tfファイルは次のようになります。
ここも環境に合わせてcredentialsファイルへのパスとprofile名を変更してください。

provider "aws" {
  region = "ap-northeast-1"
  shared_credentials_file = "/pass/to/credentials"
  profile = "profilename"
}

resource "aws_iam_role" "lambda_exec" {
  name = "AWSDynamoDBForLambda"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "putitem_dynamodb_policy" {
  name = "AWSDynamoDBPutItemAccess"

  description = "Provides putitem access for Amazon DynamoDB"
  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:PutItem"
            ],
            "Resource": "*"
        }
    ]
}
EOF
}

resource "aws_iam_policy_attachment" "dynamodb_policy_to_lambda" {
  name = "dynamodb_policy_to_lambda"

  policy_arn = "${aws_iam_policy.putitem_dynamodb_policy.arn}"
  groups = []

  users = []
  roles = ["${aws_iam_role.lambda_exec.id}"]
}

data "archive_file" "lambda_function" {
  type = "zip"
  source_dir = "lambda_function"
  output_path = "lambda_function.zip"
}

resource "aws_lambda_function" "send_putitem_lambda" {
  function_name = "SendPutRequestToDynamoDB"

  filename = "${data.archive_file.lambda_function.output_path}"

  role = "${aws_iam_role.lambda_exec.arn}"

  handler = "main.lambda_handler"
  runtime = "python3.6"
}

resource "aws_cloudwatch_event_rule" "lambda" {
  name = "SendPutRequestEventToDynamoDBBy5MIN"
  description = "send put request event to dynamoDB by 5 min"
  schedule_expression = "rate(5 minutes)"
}

resource "aws_cloudwatch_event_target" "lambda" {
  rule = "${aws_cloudwatch_event_rule.lambda.name}"
  target_id = "send_putitem_lambda"
  arn = "${aws_lambda_function.send_putitem_lambda.arn}"
}

resource "aws_lambda_permission" "allow_cloudwatch_to_call_lambda" {
  statement_id = "AllowExecuteFromCloudWatch"
  action = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.send_putitem_lambda.function_name}"
  principal = "events.amazonaws.com"
  source_arn = "${aws_cloudwatch_event_rule.lambda.arn}"
}

これで追加されるものは

  • Lambda関数用ロールの作成
  • DynamoDBにPutItemリクエストを実行するためのpolicyの作成
  • Lambda関数用ロールへのpolicyの追加
  • lambda_functionディレクトリをzip圧縮
  • Lambda関数の作成
  • CloudWatch Eventのルールの作成
  • CloudWatch EventのルールとLambda関数を紐づけ
  • CloudWatch EventのルールにLambda関数の呼び出しを許可

これを使用すると、「5分毎にlambdaを実行し、DynamoDBに対して書き込みリクエストを送出する」環境ができます。
また、lambda_functionディレクトリをzipに圧縮しLambda関数として登録するので、手動でzipを作る必要はありません。

あとはterraformを実行し、環境構築を行います。

terraform init
terraform apply

以上です。