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

# tl;dr

- ローカルで[Shoryuken](https://github.com/phstc/shoryuken)の開発をするためにSQSのmockとなるものをたてたい
 - 簡単のためにdocker-composeを使いたい
- SQSの代替として[ElasticMQ](https://github.com/adamw/elasticmq)を使う
 - motoやlocalstackだと罠がある

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

Shoryukenを起動するにはSQSに繋ぐ必要がありますがローカルからAWSに繋げたくないので、SQSコンパチのミドルウェアをなんらかローカルで起動する必要がありまが、Shoryukenの[wiki](https://github.com/phstc/shoryuken/wiki/Using-a-local-mock-SQS-server)をみると[moto](https://github.com/spulec/moto)を使えばできるよ、と書いてあります。

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

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

docker-composeするためにmotoのDockerイメージがほしいわけなんですが、motoのofficialなイメージはありません。[^1]
[^1]: 野良イメージはありますが、野良なのであんまり使いたくない
なにかいいのないかな?って思ってたところに[localstack](https://github.com/localstack/localstack)の存在を思いだしました。
こちらは[officialなイメージ](https://hub.docker.com/r/localstack/localstack/)が存在します。
いい感じです。

## localstackと`docker-compose up`の問題点

localstackのrepositoryには[docker-compose.yml](https://github.com/localstack/localstack/blob/master/docker-compose.yml)がおいてあるので、これを参考にShoryuken workerとlinkさせればうまくいきそうです。

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

```yaml: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から繋げません。
[^2]: docker-composeのlogをみるとわかる。ちなみにSQS起動後 `aws --endpoint-url=http://localhost:4576 sqs list-queues` とかすると普通に繋げる
Shoryukenは起動時にSQSに接続して、queueが存在するかどうか確認してエラーだったら落ちてしまうのでこれだと使えません。

試してないですが、これはmotoも同じ[^3](たぶん)なので、別の手段を考える必要があります。
[^3]: localstack同様に複数のAWS Serviceを起動するものなので

(追記)
Dockerのofficial[ドキュメント](https://docs.docker.com/compose/startup-order/)に記述がありました。
[wait-if-for](https://github.com/vishnubob/wait-for-it)などでwrapすることで、TCPコネクションをみて起動するのがよさそうです。
これだとlocalstackでもいけるかも。(confのmount次第)

# ElasticMQを使う

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

ElasitcMQのDockerイメージは[s12v/elasticmq](https://hub.docker.com/r/s12v/elasticmq/)を使うことにします。

```yaml: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](https://blog.linkme.it/mocking-sqs-with-elasticmq-and-docker-compose-2eee960fe15)という記事を発見[^4]しました。
[^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`の中に作っておきたいqueue[^5]を書いておくのと、`node-address.host`にdocker-compose経由で参照するhost名指定します。
[^5]: `shoryuken.yml`に書いておくqueue
そしてこれをmountする。

```yaml: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:
```

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

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