🥒

開発環境で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.rbconfig.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でテストしたり、メッセージの取り出しなどについても書けたらいいなと考えています。

DGBTテックブログ

Discussion