📬

ElasticMQを使ってローカルでAWS SQSをモックする

2024/11/26に公開

なぜモックするのか

ローカルでLambdaを実行する際に、AWSのSQSと連携せずに動作確認を行いたい。
ElasticMQはSQS互換のメッセージキューシステム。こちらをローカルで動作させていきます。

ElasticMQのセットアップ

ElasticMQのDockerイメージが公開されているので、今回はDocker Composeを使ってセットアップしていきます。

docker-compose.yml 作成

docker-compose.yml
version: "3"

services:
  elasticmq:
    container_name: elasticmq
    image: softwaremill/elasticmq:latest
    ports:
      - "9324:9324"
      - "9325:9325"
    networks:
      - sam-local
networks:
  sam-local:
    external: true

今回、sam localを利用して動作確認をしていきます。
sam localのコンテナからElasticMQのコンテナへ通信できるように、docker networkを作成しましょう。
docker network create sam-local

ElasticMQ起動

docker-composeでElasticMQを起動する。
docker-compose up -d

以下のURLで管理画面を開くことができる。
http://localhost:9325/

今は何のキューも作成していないので、寂しいですね。
ではキューを作成していきましょう。

動作確認

キューを作成

--endpoint-urlを指定することで、AWSではなくローカルのElasticMQへ接続できます。
aws sqs create-queue --queue-name test-queue --endpoint-url http://localhost:9324

作成されたキューを確認(管理画面でもOK)
aws sqs list-queues --endpoint-url http://localhost:9324

{
    "QueueUrls": [
        "http://localhost:9324/000000000000/test-queue"
    ]
}

AWS CLIで動作確認

メッセージの送受信

以下のコマンドでメッセージを送信してみましょう。
aws sqs send-message --queue-url http://localhost:9324/queue/test-queue --message-body "ElasticMQ Test Message" --endpoint-url http://localhost:9324

管理画面を見るとメッセージが送信されたことが確認できます。

では次に、以下のコマンドで送信されたメッセージを受信してみましょう。
aws sqs receive-message --queue-url http://localhost:9324/queue/test-queue --endpoint-url http://localhost:9324

{
    "Messages": [
        {
            "MessageId": "65ba8f9b-3c22-4220-9e92-b0f5b469ef85",
            "ReceiptHandle": "65ba8f9b-3c22-4220-9e92-b0f5b469ef85#06e57300-cb75-45fb-ae7c-27b349179980",
            "MD5OfBody": "b336d90ba4892d57a9cc3140502d4634",
            "Body": "ElasticMQ Test Message"
        }
    ]
}

先ほど送ったメッセージを受信できましたね。

メッセージの削除

管理画面を確認すると、受信しただけではメッセージは削除されないようです。
以下のコマンドでメッセージを削除します。
aws sqs delete-message --queue-url http://localhost:9324/queue/test-queue --receipt-handle 65ba8f9b-3c22-4220-9e92-b0f5b469ef85#06e57300-cb75-45fb-ae7c-27b349179980 --endpoint-url http://localhost:9324
--receipt-handleは先ほど受信したときの値を入力

削除されたことを管理画面で確認できました。

Lambdaで動作確認

では次は実際にLambdaからElasticMQにメッセージを送信してみましょう。
sam localを使って、ローカル実行させたLambdaからメッセージを送信してみます。

sam template

template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sqs-test

  Sample SAM Template for sqs-test

Parameters:
  AwsEndpointSQSUrl:
    Type: String
    Default: ""
  
Globals:
  Function:
    Timeout: 10

Resources:
  SQSTestFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: sqs-test/
      Handler: app.lambdaHandler
      Runtime: nodejs20.x
      Architectures:
        - x86_64
      Environment:
        Variables:
          AWS_ENDPOINT_URL_SQS: !Ref AwsEndpointSQSUrl

AWS_ENDPOINT_URL_SQSという環境変数にElasticMQのURLを渡してあげることで、コード上でendpointを指定せずにローカルのElasticMQと通信するように設定してあげています。

Lambdaのコード

app.mjs
import { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs";

const client = new SQSClient();
const SQS_QUEUE_URL = "http://elasticmq:9324/queue/test-queue";

export const lambdaHandler = async (event, context) => {
  const command = new SendMessageCommand({
    QueueUrl: SQS_QUEUE_URL,
    MessageBody: "ElasticMQ Test Message on Lambda",
  });

  const response = await client.send(command);
  console.log(response);
  return response;
};

単純にメッセージを送信するだけの処理を作成しました。

SQS_QUEUE_URLにはElasticMQに作成したキューのURLを渡しています。
※ localhostじゃないの?と思うかもしれませんが、sam localはコンテナ上で動作しているので、外部コンテナに接続するためにElasticMQのコンテナのホスト名を指定する必要があります。

実行してみよう

では先ほどのLambdaをローカルで実行してみましょう。
まずはsam buildして...

完了したら以下のコマンドで実行しましょう。
sam local invoke --parameter-overrides AwsEndpointSQSUrl=http://elasticmq:9324 --docker-network sam-local

--docker-networkで先ほど作ったdocker networkを指定しましょう。(指定しないとElasticMQへ通信できずエラーとなります。)
--parameter-overridesでは環境変数の値を渡しています。

実行してエラー無くレスポンスが返ってくれば、メッセージが送信できているはずです。

管理画面でメッセージが届いていることが確認できました!

では再度CLIでメッセージを受信してみましょう。
aws sqs receive-message --queue-url http://localhost:9324/queue/test-queue --endpoint-url http://localhost:9324

{
    "Messages": [
        {
            "MessageId": "c33ed69a-63e7-40e3-addf-3aba9da37d2d",
            "ReceiptHandle": "c33ed69a-63e7-40e3-addf-3aba9da37d2d#71b77844-5504-463d-9bfe-655827a10947",
            "MD5OfBody": "d47936c65dcf06da701ae504dff350f3",
            "Body": "ElasticMQ Test Message on Lambda"
        }
    ]
}

無事、Lambdaから送信したメッセージを受信できました!

参考記事

https://note.shiftinc.jp/n/n5954af51eaaf
https://devops-blog.virtualtech.jp/entry/20240614/1718341862

Discussion