🍡

SQSをイベントソースとしたLambdaをTerraformで定義する

2022/12/02に公開

こんにちは、@thousan_da です。

本記事では、Terraformを使って、AWS Lambda, Amazon SQSのリソースを定義する方法を紹介します。
また、SQSをLambdaのイベントソースとして設定します。

これにより、SQSにデータがエンキューされるとLambdaが実行されるようになります。

LambdaとSQSの連携に関する公式資料はこちら
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-sqs.html

はじめに

最低限定義する必要があるリソースは以下の3つです。順に説明していきます。

resource "aws_lambda_function"
resource "aws_sqs_queue"
resource "aws_lambda_event_source_mapping"

Lambda本体を定義します

resource "aws_lambda_function" "my_sugoi_function" {
  filename      = "main.zip"  // 実行したいプログラム群を圧縮したもの
  function_name = "my_sugoi_function"  // Lambda関数名
  description   = "すごい関数です"
  handler       = "build/handler"  // 言語によって異なるので下で説明します
  role          = aws_iam_role.role_for_my_sugoi_function.arn
  runtime       = "go1.x"
  timeout       = 300
  memory_size   = 1024
  environment {
    variables = {
      SUGOI_ENV = "環境変数を設定できます"
    }
  }

  tags = {
    sugoi_tag = "タグをつけられます"
    env       = "prod"
  }
}

参考

Terraform公式ドキュメント
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function

handlerに設定する値

ランタイム (言語) ごとに異なります。
スクリプト言語の場合は {ファイル名}.{関数名} という仕様となっている場合が多いようです。

  • Node.js: {ファイル名}.{関数名}
  • Python: {ファイル名}.{関数名}
  • Go: {実行ファイル名}

Goの場合は、コンパイル後の実行ファイルのパスです。

参考

Python の Lambda 関数ハンドラー
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-handler.html

role

SQSへのアクセスを許可したIAMロールを紐づけておきましょう。
以下の権限が必要です。

  • sqs:ReceiveMessage
  • sqs:DeleteMessage
  • sqs:GetQueueAttributes

参考

Amazon SQS での Lambda の使用 / 実行ロールのアクセス許可
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-sqs.html#events-sqs-permissions

SQSを定義します

resource "aws_sqs_queue" "my_sugoi_queue" {
  name = "my_sugoi_queue"
  // 保持期間を秒で指定 (この例では3日間)
  message_retention_seconds = 60 * 60 * 24 * 3
  // 可視性タイムアウト: lambda_timeout + batch_window + 30s とすると良いらしい
  visibility_timeout_seconds = 335
  // 
  receive_wait_time_seconds = 5
  
  // FIFOキューにしたい場合は以下を指定する
  // fifo_queue = true
  
  // 再実行に関する設定
  redrive_policy = jsonencode({
    // 失敗したら以下のデッドレターキューにデータをエンキューする
    deadLetterTargetArn = aws_sqs_queue.dlq.arn
    // 最大2回まで実行する (同じメッセージを3回目に受け取ったらデッドレターキューに送る)
    maxReceiveCount     = 2
  })
  tags = {
    sudoi_tag = "タグをつけられます"
    env       = "prod"
  }
}

参考

Terraform公式ドキュメント
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue

各種数値

message_retention_seconds

メッセージの保持期間を秒で指定します。エンキューされてから、この秒数が過ぎたメッセージは消去されます。

visibility_timeout_seconds

可視性タイムアウトを秒で指定します。これが一番よくわからないと思います。

SQSは、メッセージを配信した際、その瞬間にメッセージを消去するわけではありません。一時的に見えない状態にして保持しておきます。
SQS => Lambdaの例では、SQSからLambdaにメッセージが渡されると、渡したメッセージはSQS上で見えなくなります。その後、Lambdaがそのメッセージを正常に処理したことが確認されてから消去します。

この「一時的に見えない状態にしておくこと」が可視性タイムアウトです。

また、一定期間正常に処理されたことが確認できない場合、一時的に見えなくしておいたデータを再度見えるようにして配信します。

visibility_timeout_seconds に設定するのは「配信して見えない状態になってから再度配信できる状態に戻すまでの時間」です。

参考

Amazon SQS可視性タイムアウト
https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html

receive_wait_time_seconds

この値により、SQSに対してキュー内のメッセージをデキューするリクエストが来た際、キューが空の場合の挙動が変わります。

0以上、20以下の値を設定することができ、

0に設定すると、キューが空でも即座にレスポンスを返します。この挙動をショートポーリングと呼びます。

1以上に設定すると、キューが空のときは設定した秒数だけ待ってからレスポンスを返します。この挙動をロングポーリングと呼びます。

参考

Amazon SQS ショートポーリングとロングポーリング
https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html
Amazon SQSのロングポーリング設定でコストを99%削減した話
https://tech.uzabase.com/entry/2021/02/22/124454

FIFOキューにしたい場合

fifo_queue = true を指定すると、FIFOキューとして作成できます。

SQSには2種類のキューがあります。

  • 標準キュー: レコードの順序が入れ替わったり、同じレコードが複数回配信されたりすることがたまにあるが、処理が高速
  • FIFOキュー: 順序が保証されており、同じメッセージが複数回配信されることもないが、標準キューよりも遅い

ちなみに2021年3月にFIFOキューに高スループットが正式リリースされ、処理速度が向上したそうです。

デッドレターキューについて

Lambdaでの処理でなんらかのエラーが発生した場合、エラーとなった実行のトリガーとなったメッセージをデッドレターキュー (Dead Letter Queue; DLQ) と呼ばれる別のキューに送る機能があります。

LambdaのイベントソースとしてSQSを指定します

これを定義することで、SQSにデータがエンキューされるとLambdaが実行されるようになります。

resource "aws_lambda_event_source_mapping" "mapping_for_sugoi_function" {
  // バッチサイズ: 一度に処理する最大レコード数
  batch_size = 10
  // バッチウィンドウ: ここで設定した秒数が経過したらバッチサイズまでレコード数が貯まらなくても実行する
  maximum_batching_window_in_seconds = 5
  // SQSのARN (S3やSNSといったSQS以外のリソースをイベントソースにしたい場合はそれらのARN)
  event_source_arn = aws_sqs_queue.my_sugoi_queue.arn
  // LambdaのARN (function_nameというパラメータだけどARNを渡します)
  function_name    = aws_lambda_function.my_sugoi_function.arn
}

参考

Terraform公式ドキュメント
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_event_source_mapping

バッチについて

バッチサイズ batch_size は、SQSからLambdaに対してひとまとまりで届くレコード数です。標準キューの場合は最大10,000、FIFOキューの場合は最大10を設定できます。
バッチウィンドウ mximum_batching_window_in_seconds は、Lambdaの実行までの待ち時間です。SQSに溜まっているメッセージ数がバッチサイズに満たない場合でも、ここで指定した時間が経過したらLambdaを実行します。

終わりに

この記事は "Voicy Advent Calendar 2022" 2日目の記事でもあります。
もしよろしければ、他の記事も読んでみてください!
https://qiita.com/advent-calendar/2022/voicy

3日目は「M1チップのMacbookをVentureからMontereyへダウングレードした話」の予定です!

Voicyテックブログ

Discussion