開発環境でRails→SQSの動作確認をするためにElasticMQを構築する
こんにちは。
株式会社DGビジネステクノロジー NaviPlusレビューチームの飯塚です。
今回はRails×SQSの動作確認をするためにElasticMQを開発環境に構築した話をまとめようと思います。
背景
NaviPlusレビューには、元々Resque/Redisを使ってジョブを捌いておりました。
以下のような課題を感じており、別のジョブ実行環境としてSQSで構築してみようというモチベーションがあり導入を決めました。
- 長年の運用により複雑になったresque-pool
- リソース管理の手間(緊急時にジョブを止めるためにECS Execからコマンド実行が必要)
- resque pluginのレガシー化
SQSを使ってジョブを実行する環境を構築するにあたり、テストや開発の手軽さを重視したい場合はElasticMQ, ちゃんと本番を意識した動作確認を含めたい場合はSQSというように使い分けられるようにしたかったので、最終的には以下のようにEnvの有無でSQS/ElasticMQを使い分けるようにしました。
ちなみにLocalStackも候補になりましたが、ElasticMQを採用したのは以下の2つの理由からです。
- 今回はSQSの動作検証ができればよく、他のAWSサービスはスコープ外だった
- ElasticMQの方が構築が楽だった
他のAWSサービスも気にする必要が出てきた時は改めて検討したいなと思います。
登場技術の軽い説明
SQSとは
Amazon Simple Queue Serviceの略称で、完全マネージド型のメッセージキューです。
Resqueとの比較
Resqueの場合だと、バックエンドとしてRedisを使うためサーバー構築が必要になります。つまりサーバーの管理(スケーリング・モニタリングとか)が必要になりますが、SQSはそういうのを楽できます。
名称 | バックエンド | アダプター |
---|---|---|
SQS | SQS | aws-sdk-sqsまたはそれを含むaws-activejob-sqsのGemが必要 |
Resque | Redis | Rails標準 |
ElasticMQとは
ElasticMQとは、SQS互換のメッセージキューシステムのこと。SQSのモックのような使い方ができます。
実装
SQSの構築
SQS設定はAWSマネジメントコンソールからポチポチして作成しました。
- IAMユーザを作成。開発環境なのでポリシーを直接アタッチで
SQSFullAccess
を許可。 - IAMユーザーのアクセスキー・シークレットアクセスキーも作っておく(これを使って開発環境からSQSにアクセスします)
- SQSのアクセスポリシーは「指定されたAWSアカウント、IAMユーザー、ロールのみ」を選択し、上で作ったIAMユーザーのARNを入力
ElasticMQ側の構築
ElasticMQの環境を構築していきます。
公式のdockerイメージを使うと楽です。
- docker-compose.yml
services:
(略)
environment
SQS_AWS_REGION: ${SQS_AWS_REGION}
SQS_AWS_ACCESS_KEY_ID: ${SQS_AWS_ACCESS_KEY_ID}
SQS_AWS_SECRET_ACCESS_KEY: ${SQS_AWS_SECRET_ACCESS_KEY}
SQS_ENDPOINT: ${SQS_ENDPOINT}
elasticmq:
image: softwaremill/elasticmq
ports:
- "9324:9324"
- "9325:9325"
volumes:
- type: bind
source: ./[任意のディレクトリ]/elasticmq/custom.conf
target: /[任意のディレクトリ]/elasticmq.conf
- docker/elasticmq/custom.conf
ElasticMQ上につくるキューの設定を書く場所。
CLIで作成することもできますが、設定で書いておきます。公式リポジトリのREADMEに書いてあるのでそれを参考にしました。
本来はSQSの設定と合わせる必要がありそう。
queues {
my_example_queue {
defaultVisibilityTimeout = 10 seconds
delay = 5 seconds
receiveMessageWait = 0 seconds
deadLettersQueue {
name = "my_example_queue_dead_letters"
maxReceiveCount = 3
}
fifo = false
contentBasedDeduplication = false
}
my_example_queue_dead_letters { }
}
ちょっとした説明
設定名 | 名称 | 説明 |
---|---|---|
defaultVisibilityTimeout | 可視性タイムアウト | 他のコンシューマからメッセージを一時的に見えなくする時間 |
deadLettersQueue | デッドレターキュー | 正常に処理されなかったメッセージを隔離して置いておく場所 |
これでElasticMQ側の実装は完了です。
問題なければDocker環境が立ち上がるはず。
http://localhost:9325/
にアクセスすれば画面でキューの状態を確認できます!
ActiveJob側の設定
- Gemfile
以下のGemにaws-sdk-sqsも含まれている
This gem also brings in the following AWS gems:
aws-sdk-sqs
gem 'aws-activejob-sqs'
- app/jobs/xxx_job.rb
メインでResqueを使っているためself.queue_adapter
で個別に指定しています。SQSをメインにする場合はconfig/environments/xxxx.rb
でconfig.active_job.queue_adapter = :sqs
にすると良いです。
class XXXJob < ApplicationJob
# 他のJobはResqueを使っているためadapterは個別で指定
self.queue_adapter = :sqs
queue_as :my_example
def perform
# 処理の中身
end
end
- config/initializers/aws_active_job_sqs.rb
ENVの有無を確認したかったのでpresence
メソッドを使用
本番・検証環境では、エラーを返してくれればいいのでENV.fetch
を使用
Aws::ActiveJob::SQS.configure do |config|
config.logger = Rails.logger
config.max_messages = 5
config.client = if Rails.env.development? || Rails.env.test? # Rails.env.local?でも可
Aws::SQS::Client.new(
region: ENV['SQS_AWS_REGION'].presence || 'elasticmq',
access_key_id: ENV['SQS_AWS_ACCESS_KEY_ID'].presence || 'x',
secret_access_key: ENV['SQS_AWS_SECRET_ACCESS_KEY'].presence || 'x',
endpoint: ENV['SQS_ENDPOINT'].presence || 'http://elasticmq:9324'
)
else
Aws::SQS::Client.new(
region: ENV.fetch('SQS_AWS_REGION', 'ap-northeast-1'),
access_key_id: ENV.fetch('SQS_AWS_ACCESS_KEY_ID', nil),
secret_access_key: ENV.fetch('SQS_AWS_SECRET_ACCESS_KEY', nil),
endpoint: ENV.fetch('SQS_ENDPOINT', nil)
)
end
end
- config/aws_active_job_sqs.yml
backpressure: 5
max_messages: 3
queues:
my_example:
url: '<%= ENV["SQS_ENDPOINT"].presence || "http://elasticmq:9324/queue/my_example_queue" %>'
あとは.envに環境変数を突っ込んだり突っ込まなかったりすれば、Railsコンソールなどで XXXJob.perform_later
を実行時にSQSやElasticMQのキューに入るようになります。
rails-sqs(dev)> TestJob.perform_later
Enqueued TestJob (Job ID: [Job ID]) to Sqs(my_example)
=>
#<TestJob:0x0000ffff973609d8
@_halted_callback_hook_called=nil,
@arguments=[],
@exception_executions={},
@executions=0,
@job_id="[Job ID]",
@priority=nil,
@queue_name="my_example",
@scheduled_at=nil,
@successfully_enqueued=true,
@timezone="UTC">
なるべく本番環境に近づけつつ、キューの動作確認ができるのでElasticMQはとても有用ですね!
今回は環境構築してエンキューするまでの話を書きました。時間がとれたらElasticMQでテストしたり、メッセージの取り出しなどについても書けたらいいなと考えています。
Discussion