以前まで、AWS の SNS や SQS のサービスを利用するには AWS の契約が必須だと思っていたのですが、LocalStack の存在を知り、ローカルの Docker 上でも擬似環境を構築できることが判明しました。
これにより、費用面で難色を示されて AWS のテスト環境をなかなか用意してもらえない職場でも、ローカルで AWS に近い環境を試すことができます。
EC2(AmazonLinux)は CentOS、Aurora(RDS)は MySQL サーバで代用できたとしても、その他多くの AWS のサービスの擬似環境を用意するのには限界がありますからね。
今回は、ローカルスタックの構築とアプリケーションサービスの操作について紹介したいと思います。
・LocalStack の Docker コンテナの構築
・SNS のトピック操作(awscli)
・SQS のキュー操作(awscli)
SNS や SQS がどのようなものか知らない人は下記も参考にしてください。

LocalStackについて
LocalStack は、AWS のモックフレームワークを提供してくるアプリケーションです。
冒頭で SNS や SQS と書きましたが、以下のサービスを localhost のそれぞれのポートで起動してくれます。
API Gateway : 4567
Kinesis : 4568
DynamoDB : 4569
DynamoDB Streams : 4570
Elasticsearch : 4571
S3 : 4572
Firehose : 4573
Lambda : 4574
SNS : 4575
SQS : 4576
Redshift : 4577
Elasticsearch : 4578
SES : 4579
Route53 : 4580
CloudFormation : 4581
CloudWatch : 4582
よって、SNS であれば http://localhost:4575 のエンドポイントに対してトピック(topic)の操作ができますし、SQS であれば http://localhost:4576 が対象になります。
LocalStackをコンテナで動かす
LocalStack の利用方法はいくつかありますが、今回は冒頭でも書いた通り Docker で動かします。
DockerHub にイメージがあるので、それを利用してみましょう。
docker pull してもいいのですが、ローカルの Docker コンテナたちは docker-compose.yml で管理しているのでこちらに定義します。完全に自分都合ですが・・・。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | version: '3' services: my-localstack: container_name: my-localstack image: localstack/localstack ports: - "4567-4582:4567-4582" - "${PORT_WEB_UI-9080}:${PORT_WEB_UI-8080}" environment: - DEFAULT_REGION=ap-northeast-1 - SERVICES=sqs,sns - DATA_DIR=${DATA_DIR- } - PORT_WEB_UI=${PORT_WEB_UI- } - DOCKER_HOST=unix:///var/run/docker.sock |
docker-compose.yml を保存したら LocalStack を起動してみます。初回なので –build しておきますか。
1 | $ docker-compose up -d --build my-localstack |
AWS-CLIの実行環境を整える
これで LocalStack は使えるようになりましたが、実際に SNS や SQS の操作をするには以下の 2 つの作業が必要になります。
・awscliのインストール
・AWSの認証プロファイルの作成
Linux の場合は、AmazonLinux なら awscli が標準で組み込まれています。
CentOS の場合は以下で書きましたので参考にしてください。

Mac の場合は brew でインストールすることになります。
1 2 3 | $ brew update $ brew install awscli |
コンテナの環境変数でデフォルトは東京リージョンとしましたが、別途、SNS や SQS などの各サービスを操作するためのプロファイルを用意する必要があり、ここでもリージョンは指定しておきます。
早速、作ります。「Access Key ID」や「Secret Access Key」はダミーの値で問題ないようなので、ここでは「dummy」としておきます。
プロファイル名は仮に「local」としておきます。
1 2 3 4 5 6 | $ aws configure --profile local AWS Access Key ID [None]: dummy AWS Secret Access Key [None]: dummy Default region name [None]: ap-northeast-1 Default output format [None]: json |
これで、各サービスを操作できる状態となりました。
LocalStackのSQSをコマンドラインから操作する
SQS の操作をする前に、以下を作成しておきます。
・SNSのトピックスの作成
・SQSのキューの作成
・SNSのトピックからキューにメッセージを送信する権限付与
・SNSのトピックとSQSのキューの連携設定(サブスクライバ)
必要に応じて「デッドレターキュー」の作成もしておくといいかもしれませんね。
ここでは仮に、それぞれ 1 個ずつ作成して SNS と SQS を連動させてみます。
トピック名もキュー名もリラックマ(rilakkuma)とします。ワンライナーで書かずにシェルスクリプトにした方がわかりやすかったですね。
途中 ¥(バックスラッシュ)で折り返していますが、少しコマンドが見にくいかも・・・。
1 2 3 4 5 6 7 8 9 10 11 12 13 | $ aws --profile local --endpoint-url http://localhost:4575 sns create-topic --name 'topic-rilakkuma' $ aws --profile local --endpoint-url http://localhost:4576 sqs create-queue --queue-name 'queue-rilakkuma' $ aws --profile local --endpoint-url http://localhost:4575 sns add-permission ¥ --topic-arn arn:aws:sns:ap-northeast-1:123456789012:topic-rilakkuma ¥ --action-name sqs:SendMessage --aws-account-id dummy --label permission-queue-rilakkuma $ aws --profile local --endpoint-url http://localhost:4575 sns subscribe ¥ --topic-arn arn:aws:sns:ap-northeast-1:123456789012:topic-rilakkuma ¥ --protocol sqs --notification-endpoint arn:aws:sqs:ap-northeast-1:queue:queue-rilakkuma ¥ | jq -r .SubscriptionArn ¥ | xargs aws --endpoint-url http://localhost:4575 --profile local sns set-subscription-attributes --attribute-name FilterPolicy --attribute-value '{"Type":"KUMA"}' --subscription-arn |
SQSの操作をする
上記で作成したキューがキュー一覧に表示されるか確認してみます。
キューをたくさん登録している場合は、–queue-name-prefix をつけてプレフィックス指定で検索してみてください。
1 2 3 4 5 6 7 | $ aws sqs list-queues --profile local --endpoint-url http://localhost:4576 --queue-name-prefix queue { "QueueUrls": [ "http://localhost:4576/queue/queue-rilakkuma" ] } |
このキューに受信可能なメッセージが何件あるか確認します。
1 2 3 4 5 6 7 8 | $ aws sqs get-queue-attributes --profile local --endpoint-url http://localhost:4576 ¥ --queue-url http://localhost:4576/queue/queue-rilakkuma --attribute-names ApproximateNumberOfMessages { "Attributes": { "ApproximateNumberOfMessages": "1" } } |
他で受信されて、一定時間アクティブではないキューの件数を確認します。
1 2 3 4 5 6 7 8 | $ aws sqs get-queue-attributes --profile local --endpoint-url http://localhost:4576 ¥ --queue-url http://localhost:4576/queue/queue-rilakkuma --attribute-names ApproximateNumberOfMessagesNotVisible { "Attributes": { "ApproximateNumberOfMessagesNotVisible": "0" } } |
キューのメッセージを 1 件受信してみます。
(長いのでBody以降を省略します)
1 2 3 4 5 6 7 8 9 10 11 12 13 | $ aws sqs receive-message --profile local --endpoint-url http://localhost:4576 ¥ --queue-url http://localhost:4576/queue/queue-rilakkuma { "Messages": [ { "MessageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "ReceiptHandle": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx#xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "MD5OfBody": "xxxxxxxxxxxxxxxxxxxxxxxx", "Body": "" } ] } |
メッセージを受信して削除します。受信したメッセージの ReceiptHandle を指定して削除です。
以下の例では受信したメッセージを直接キューから削除していますが、特定のキューメッセージを削除したい場合は ReceiptHandle の値を確認してから、別途削除コマンドを実行してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ aws sqs receive-message --profile local --endpoint-url http://localhost:4576 ¥ --queue-url http://localhost:4576/queue/queue-rilakkuma ¥ | jq -r .Messages[0].ReceiptHandle ¥ | xargs -IRH aws sqs delete-message --profile local --endpoint-url http://localhost:4576 ¥ --queue-url http://localhost:4576/queue/queue-rilakkuma --receipt-handle RH { "Messages": [ { "MessageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "ReceiptHandle": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx#xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "MD5OfBody": "xxxxxxxxxxxxxxxxxxxxxxxx", "Body": "xxxxxxxxxxxxxxxxxxxxxxxx" } ] } |
SNS を経由してキューを登録する場合は、直接キューに対してメッセージを送ることはないですが、コマンドでは試しておきましょう。
1 2 | $ aws sqs send-message --profile local --endpoint-url http://localhost:4576 ¥ --queue-url http://localhost:4576/queue/queue-rilakkuma --message-body '{"Name":"RILAKKUMA","Like":"DANGO"}' |
SQS に対して、開発中に確認したいとしたらこのくらいでしょうか。
SNSの操作をする
今回はサブスクリプションの設定で、SNS のトピックにパブリッシュされたメッセージを SQS に登録するようにしました。
その際、attribute でフィルタ設定をしています。以下の部分ですね。
–attribute-value {“Type”:”KUMA”}
これを設定することで、トピックにパブリッシュされたものの中で、Type が KUMA 以外のものは SQS にキュー登録しないようにできます。
queue-rilakkuma 以外に queue-kiiroitori を作ったとしたら、以下のように Type の値によって別々のキューに振り分けすることもできるのです。
・TypeがKUMA: queue-rilakkuma
・TypeがTORI: queue-kiiroitori
リラックマをサンプルにしたので、知らない人はわかりませんね。もっと一般的なものを例にすれば良かったですね。すみません・・・。
リラックマ:KUMA:queue-rilakkuma
コリラックマ:KUMA:queue-rilakkuma
キイロイトリ:TORI:queue-kiiroitori
よって、SNS のパブリッシュは以下のようなコマンドになります。
1 2 3 4 5 | $ aws sns publish --endpoint-url http://localhost:4575 --topic-arn arn:aws:sns:ap-northeast-1:123456789012:topic-rilakkuma --profile local --message-attributes '{"Type":{"DataType":"String","StringValue":"KUMA"}}' --message '{"Name":"RILAKKUMA","Like":"DANGO"}' $ aws sns publish --endpoint-url http://localhost:4575 --topic-arn arn:aws:sns:ap-northeast-1:123456789012:topic-rilakkuma --profile local --message-attributes '{"Type":{"DataType":"String","StringValue":"KUMA"}}' --message '{"Name":"KORILAKKUMA","Like":"ICHIGO"}' $ aws sns publish --endpoint-url http://localhost:4575 --topic-arn arn:aws:sns:ap-northeast-1:123456789012:topic-rilakkuma --profile local --message-attributes '{"Type":{"DataType":"String","StringValue":"TORI"}}' --message '{"Name":"KIIROITORI","Like":"MONEY"}' |
topic-rilakkuma に対してパブリッシュしたものが、attribute の Type によってキューの振り分け先が決めれるということです。
仮に Type が PERSON だったとしたら、フィルタで弾かれるのでキューには登録されません。
SNS をコマンドから使うとしたら Publish くらいでいいでしょうか。