Rails
docker
ElasticMQ
docker-compose
shoryuken

docker-composeでShoryukenのローカル開発環境を構築するのに嵌った

tl;dr

  • ローカルでShoryukenの開発をするためにSQSのmockとなるものをたてたい
    • 簡単のためにdocker-composeを使いたい
  • SQSの代替としてElasticMQを使う
    • motoやlocalstackだと罠がある

Shoryukenをローカル開発するときの問題点

Shoryukenを起動するにはSQSに繋ぐ必要がありますがローカルからAWSに繋げたくないので、SQSコンパチのミドルウェアをなんらかローカルで起動する必要がありまが、Shoryukenのwikiをみるとmotoを使えばできるよ、と書いてあります。

たしかにmotoをインストールして、そこにつなぐようにすればローカルで開発できますが、開発環境構築に手間がかかってしまうので、やっぱりdocker-compose up一発で済ませたいところです。

Dockerでmotoを使う上での問題点

docker-composeするためにmotoのDockerイメージがほしいわけなんですが、motoのofficialなイメージはありません。1
なにかいいのないかな?って思ってたところにlocalstackの存在を思いだしました。
こちらはofficialなイメージが存在します。
いい感じです。

localstackとdocker-compose upの問題点

localstackのrepositoryにはdocker-compose.ymlがおいてあるので、これを参考にShoryuken workerとlinkさせればうまくいきそうです。

以下のような感じでしょうか。

docker-compose.yml
version: '3.2'

services:
  worker:
    build: .
    command: ["shoryuken", "-C", "config/shoryuken.yml"]
    tmpfs:
      - /app/tmp
    volumes:
      - .:/app
      - bundle_install:/usr/local/bundle
    depends_on:
      - sqs
    environment:
      RAILS_ENV: ${RAILS_ENV:-development}
      SQS_URL: http://sqs:4576

  sqs:
    image: localstack/localstack
    environment:
      - SERVICES=sqs
      - DOCKER_HOST=unix:///var/run/docker.sock
    ports:
      - 4576:4576
    volumes:
      - localstack:/tmp/localstack
      - /var/run/docker.sock:/var/run/docker.sock

volumes:
  bundle_install:
  localstack:

しかしながら、これでdocker-compose upしてもworkerがSQSに接続できず、起動しません。
どうやらlocalstackはcontainerが立ち上がってすぐにSQSに繋げるわけではなく、container起動後指定したSERVICES起動していく仕組み2らしく、depends_onを指定していてもworkerから繋げません。
Shoryukenは起動時にSQSに接続して、queueが存在するかどうか確認してエラーだったら落ちてしまうのでこれだと使えません。

試してないですが、これはmotoも同じ3(たぶん)なので、別の手段を考える必要があります。
(追記)
Dockerのofficialドキュメントに記述がありました。
wait-if-forなどでwrapすることで、TCPコネクションをみて起動するのがよさそうです。
これだとlocalstackでもいけるかも。(confのmount次第)

ElasticMQを使う

localstackのSQS部分はElasitcMQを起動しているんですが、localstackを挟まずに直でElasticMQを使うようにしてみます。
結論として、これならdepends問題は解消されます。

ElasitcMQのDockerイメージはs12v/elasticmqを使うことにします。

docker-compose.yml
version: '3.2'

services:
  worker:
    build: .
    command: ["shoryuken", "-C", "config/shoryuken.yml"]
    tmpfs:
      - /app/tmp
    volumes:
      - .:/app
      - bundle_install:/usr/local/bundle
    depends_on:
      - sqs
    environment:
      RAILS_ENV: ${RAILS_ENV:-development}
      SQS_URL: http://sqs:9324

  sqs:
    image: s12v/elasticmq
    ports:
      - 9324:9324

volumes:
  bundle_install:

実はこれでもだめで、SQS自体には繋げるのですがまだcreate queueされてないのでやはりworkerが落ちてしまうのです。
worker起動前にcreate queueの処理を挟むか?とか考えますが、他にいい方法はないのでしょうか?

elasticmq.confをmountして解決する

色々ググってMocking SQS with ElasticMQ and docker-composeという記事を発見4しました。

Done! Enjoy your queue on port 9324! Happy messaging!
Not so fast, we are still not done here: as we are most likely using elasticMQ for testing, we also do not want to manually create our queues. We can provide a config file describing the queues we want to be in place:

ってことでconfig fileをおいておけばqueueが勝手に作られるらしい。
以下のようなconfigを用意します。

elasticmq.conf
node-address {
    protocol = http
    host = sqs
    port = 9324
    context-path = ""
}
rest-sqs {
    enabled = true
    bind-port = 9324
    bind-hostname = "0.0.0.0"
    sqs-limits = strict
}
generate-node-address = false
queues {
   default{ }
}

queuesの中に作っておきたいqueue5を書いておくのと、node-address.hostにdocker-compose経由で参照するhost名指定します。
そしてこれをmountする。

docker-compose.yml
version: '3.2'

services:
  worker:
    build: .
    command: ["shoryuken", "-C", "config/shoryuken.yml"]
    tmpfs:
      - /app/tmp
    volumes:
      - .:/app
      - bundle_install:/usr/local/bundle
    depends_on:
      - sqs
    environment:
      RAILS_ENV: ${RAILS_ENV:-development}
      SQS_URL: http://sqs:9324

  sqs:
    image: s12v/elasticmq
    ports:
      - 9324:9324
    volumes:
      - ./config/elasticmq.conf:/etc/elasticmq/elasticmq.conf

volumes:
  bundle_install:
$ docker-compose up -d
$ aws --endpoint-url=http://localhost:9324 sqs list-queues
{
    "QueueUrls": [
        "http://sqs:9324/queue/default"
    ]
}

無事workerも起動しました:smile:


  1. 野良イメージはありますが、野良なのであんまり使いたくない 

  2. docker-composeのlogをみるとわかる。ちなみにSQS起動後 aws --endpoint-url=http://localhost:4576 sqs list-queues とかすると普通に繋げる 

  3. localstack同様に複数のAWS Serviceを起動するものなので 

  4. インターネッツは偉大 

  5. shoryuken.ymlに書いておくqueue