How I Fixed: Server channel error: 406, message: PRECONDITION_FAILED – inequivalent arg ‘type’ for exchange ‘my_exchange’ in vhost ‘/’: received ‘fanout’ but current is ‘direct’

Not a fun way to start a Saturday morning. With a bit of spare time this morning I wanted to continue some refactoring work on a tool I’ve been working on for checking broken links on any given website.

The project is quite cool (in my opinion), using a bunch of interesting software / tech such as RabbitMQ with Symfony’s Messenger component, STOMP for real time stuff, React with Hooks, Tailwinds for CSS… and a bunch more buzz-wordy, CV helping stuff that keeps me gainfully employed.

Anyway, the first thing I did was spin up the Symfony docker containers that run the various services to handle incoming broken link checking requests. And as ever, I ran a composer update to bring Symfony up to 4.3.x.

I’m not sure if bumping up to Symfony 4.3 was the cause of this problem. I suspect not. It’s been a while since I’ve worked on this part of the code, but it was all working the last time I brought the project up. And it’s working live and online, too, so something has gone awry.

Anyway, after the composer update completed successfully:

composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)

Prefetching 49 packages 🎶 💨
  - Downloading (100%)

Package operations: 7 installs, 42 updates, 1 removal
  - Removing symfony/contracts (v1.0.2)
  - Updating symfony/flex (v1.2.3 => v1.2.5): Loading from cache
  - Installing symfony/service-contracts (v1.1.2): Loading from cache
  - Installing symfony/polyfill-php73 (v1.11.0): Loading from cache
  - Updating symfony/console (v4.2.8 => v4.3.0): Loading from cache
  - Installing symfony/event-dispatcher-contracts (v1.1.1): Loading from cache
  - Updating symfony/event-dispatcher (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/css-selector (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/dom-crawler (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/messenger (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/process (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/serializer (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/routing (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/finder (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/filesystem (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/debug (v4.2.8 => v4.3.0): Loading from cache
  - Installing symfony/polyfill-intl-idn (v1.11.0): Loading from cache
  - Installing symfony/mime (v4.3.0): Loading from cache
  - Updating symfony/http-foundation (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/http-kernel (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/dependency-injection (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/config (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/var-exporter (v4.2.8 => v4.3.0): Loading from cache
  - Installing symfony/cache-contracts (v1.1.1): Loading from cache
  - Updating symfony/cache (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/framework-bundle (v4.2.8 => v4.3.0): Loading from cache
  - Installing symfony/translation-contracts (v1.1.2): Loading from cache
  - Updating symfony/validator (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/yaml (v4.2.8 => v4.3.0): Loading from cache
  - Updating nikic/php-parser (v4.2.1 => v4.2.2): Loading from cache
  - Updating symfony/translation (v4.2.8 => v4.3.0): Loading from cache
  - Updating nesbot/carbon (2.17.1 => 2.19.0): Loading from cache
  - Updating illuminate/contracts (v5.8.15 => v5.8.19): Loading from cache
  - Updating illuminate/support (v5.8.15 => v5.8.19): Loading from cache
  - Updating symfony/inflector (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/property-access (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/property-info (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/monolog-bridge (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/dotenv (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/phpunit-bridge (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/expression-language (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/stopwatch (v4.2.8 => v4.3.0): Loading from cache
  - Updating composer/xdebug-handler (1.3.2 => 1.3.3): Loading from cache
  - Updating symfony/var-dumper (v4.2.8 => v4.3.0): Loading from cache
  - Updating twig/twig (v2.9.0 => v2.11.0): Loading from cache
  - Updating symfony/twig-bridge (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/debug-bundle (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/twig-bundle (v4.2.8 => v4.3.0): Loading from cache
  - Updating symfony/web-profiler-bundle (v4.2.8 => v4.3.0): Loading from cache
  - Updating roave/security-advisories (dev-master 1dfa887 => dev-master 4c0ba8a)
Writing lock file
Generating autoload files
ocramius/package-versions:  Generating version class...
ocramius/package-versions: ...done generating version class

What about running composer global require symfony/thanks && composer thanks now?
This will spread some 💖  by sending a ★  to the GitHub repositories of your fellow package maintainers.

Executing script cache:clear [OK]
Executing script assets:install public [OK]

I tried to run my messenger consumer:

www-data@1fbf5db0f719:~/app.checkforbrokenlinks.com$ bin/console messenger:consume --bus messenger.bus.fetch fetch -vvv

                                                                                                                        
 [OK] Consuming messages from transports "fetch".                                                                       
                                                                                                                        

 // The worker will automatically exit once it has received a stop signal via the messenger:stop-workers command.       

 // Quit the worker with CONTROL-C.                                                                                     


In AmqpReceiver.php line 56:
                                                                                                                                                             
  [Symfony\Component\Messenger\Exception\TransportException]                                                                                                 
  Server channel error: 406, message: PRECONDITION_FAILED - inequivalent arg 'type' for exchange 'fetch' in vhost '/': received 'fanout' but current is 'di  
  rect'                                                                                                                                                      
                                                                                                                                                             

Exception trace:
 () at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/AmqpReceiver.php:56
 Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver->getEnvelope() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/AmqpReceiver.php:47
 Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver->get() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Worker.php:92
 Symfony\Component\Messenger\Worker->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Worker/StopWhenRestartSignalIsReceived.php:54
 Symfony\Component\Messenger\Worker\StopWhenRestartSignalIsReceived->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php:224
 Symfony\Component\Messenger\Command\ConsumeMessagesCommand->execute() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Command/Command.php:255
 Symfony\Component\Console\Command\Command->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:939
 Symfony\Component\Console\Application->doRunCommand() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/framework-bundle/Console/Application.php:87
 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:273
 Symfony\Component\Console\Application->doRun() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/framework-bundle/Console/Application.php:73
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:149
 Symfony\Component\Console\Application->run() at /var/www/app.checkforbrokenlinks.com/bin/console:39

In Connection.php line 348:
                                                                                                                                                             
  [AMQPExchangeException (406)]                                                                                                                              
  Server channel error: 406, message: PRECONDITION_FAILED - inequivalent arg 'type' for exchange 'fetch' in vhost '/': received 'fanout' but current is 'di  
  rect'                                                                                                                                                      
                                                                                                                                                             

Exception trace:
 () at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/Connection.php:348
 AMQPExchange->declareExchange() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/Connection.php:348
 Symfony\Component\Messenger\Transport\AmqpExt\Connection->setup() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/Connection.php:311
 Symfony\Component\Messenger\Transport\AmqpExt\Connection->get() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/AmqpReceiver.php:54
 Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver->getEnvelope() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Transport/AmqpExt/AmqpReceiver.php:47
 Symfony\Component\Messenger\Transport\AmqpExt\AmqpReceiver->get() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Worker.php:92
 Symfony\Component\Messenger\Worker->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Worker/StopWhenRestartSignalIsReceived.php:54
 Symfony\Component\Messenger\Worker\StopWhenRestartSignalIsReceived->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/messenger/Command/ConsumeMessagesCommand.php:224
 Symfony\Component\Messenger\Command\ConsumeMessagesCommand->execute() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Command/Command.php:255
 Symfony\Component\Console\Command\Command->run() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:939
 Symfony\Component\Console\Application->doRunCommand() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/framework-bundle/Console/Application.php:87
 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:273
 Symfony\Component\Console\Application->doRun() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/framework-bundle/Console/Application.php:73
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/www/app.checkforbrokenlinks.com/vendor/symfony/console/Application.php:149
 Symfony\Component\Console\Application->run() at /var/www/app.checkforbrokenlinks.com/bin/console:39

messenger:consume [-l|--limit LIMIT] [-m|--memory-limit MEMORY-LIMIT] [-t|--time-limit TIME-LIMIT] [--sleep SLEEP] [-b|--bus BUS] [-h|--help] [-q|--quiet] [-v|vv|vvv|--verbose] [-V|--version] [--ansi] [--no-ansi] [-n|--no-interaction] [-e|--env ENV] [--no-debug] [--] <command> [<receivers>...]

Knickers. It all blew up quite badly.

There’s a lot of info to process, and without some nice terminal colouring it’s all a bit of a blur.

The interesting line is:

Server channel error: 406, message: PRECONDITION_FAILED - inequivalent arg 'type' for exchange 'my_exchange' in vhost '/': received 'fanout' but current is 'direct'

What I think has gone wrong is that at some point in the past, I’ve switched over my RabbitMQ exchange to use direct, and by default, Symfony’s Messenger component will try to create an exchange with the type of fanout.

To clarify, my exchange and queue combo already exists at: amqp://{username}:{password}@rabbitmq:5672/%2f/fetch

It exists because I have previously configured my RabbitMQ instance to boot up with this exchange / queue combo ready and good to go.

Because Symfony’s Messenger component is not immediately aware that this queue will already exist, it tries to create it.

It cannot create it because the default type of exchange that Symfony’s Messenger component will try to use is fanout.

In order to make this work, I needed to manually specify the config that explicitly sets this exchange / queue combo to the desired setting of direct.

Finding this out via the documentation wasn’t super straightforward. Here’s a few of the steps I took:

bin/console config:dump-reference framework

This shows that for each framework.messenger.transports entry in your config/packages/messenger.yaml file, you can have a variety of additional settings.

As it was, my original config looked like this:

By providing just a DSN (by way of environment variables), all the default config would be used.

What I needed to do was swap over to this:

framework:
    messenger:
        transports:
             fetch:
                dsn: '%env(MESSENGER_TRANSPORT_DSN_FETCH)%'
                options:
                    exchange:
                        type: 'direct'
             scrape:
                dsn: '%env(MESSENGER_TRANSPORT_DSN_SCRAPE)%'
                options:
                    exchange:
                        type: 'direct'

And after doing so, it all started working again:

In short, this isn’t directly a Symfony / Symfony Messenger problem. It’s a config problem. The messaging could be a little more clear, as could the documentation for what things are viable as options.

The Baffling World Of RabbitMQ and Docker

Recently I decided to switch my entire Ansible-managed infrastructure (for one project) over to Docker – or specifically, docker-compose. Part of this setup needs RabbitMQ.

I had no trouble getting the official RabbitMQ image to pull and build. I could set a default username, password, and vhost. And all of this worked just fine – I could use this setup without issue.

However, as I am migrating an existing project, I already had a bunch of queues, exchanges, bindings, users… etc.

What I didn’t want is to have some manual step where I have to remember to import the definitions.json file whenever building – or rebuilding – the environment.

Ok, so this seems a fairly common use case, I should imagine. But finding a solution wasn’t as easy as I expected. In hindsight, it’s quite logical, but then… hindsight 🙂

Please note that I am not advocating using any of this configuration. I am still very much in the learning phase, so use your own judgement.

Here is the relevant part of my docker-compose.yml file :

version: '2'

services:

    rabbit1:
        build: ./rabbitmq
        container_name: rabbit1
        hostname: "rabbit1"
        environment:
          RABBITMQ_ERLANG_COOKIE: "HJKLSDFGHJKLMBZSFXFZD"
          RABBITMQ_DEFAULT_USER: "rabbitmq"
          RABBITMQ_DEFAULT_PASS: "rabbitmq"
          RABBITMQ_DEFAULT_VHOST: "/"
        ports:
          - "15672:15672"
          - "5672:5672"
        labels:
          NAME: "rabbit1"
        volumes:
          - "./rabbitmq/enabled_plugins:/etc/rabbitmq/enabled_plugins"
          - "./volumes/rabbitmq/rabbit1/data:/var/lib/rabbitmq:rw"

Then I went to my old / existing RabbitMQ server and from the “Overview” page, I went to the “Import / export definitions” section (at the bottom of the page), and did a “Download broker definitions”.

This gives a JSON dump, which as it contains a bunch of sensitive information, I have doctored for display here:

{
  "rabbit_version": "3.6.8",
  "users": [
    {
      "name": "rabbit_mq_dev_user",
      "password_hash": "somepasswordhash+yQtnMlaK6Iba",
      "hashing_algorithm": "rabbit_password_hashing_sha256",
      "tags": "administrator"
    }
  ],
  "vhosts": [
    {
      "name": "\/"
    }
  ],
  "permissions": [
    {
      "user": "rabbit_mq_dev_user",
      "vhost": "\/",
      "configure": ".*",
      "write": ".*",
      "read": ".*"
    }
  ],
  "parameters": [
    
  ],
  "policies": [
    
  ],
  "queues": [
    {
      "name": "a.queue.here",
      "vhost": "\/",
      "durable": true,
      "auto_delete": false,
      "arguments": {
        
      }
    },
    {
      "name": "b.queue.here",
      "vhost": "\/",
      "durable": true,
      "auto_delete": false,
      "arguments": {
        
      }
    }
  ],
  "exchanges": [
    {
      "name": "router",
      "vhost": "\/",
      "type": "direct",
      "durable": true,
      "auto_delete": false,
      "internal": false,
      "arguments": {
        
      }
    }
  ],
  "bindings": [
    {
      "source": "router",
      "vhost": "\/",
      "destination": "a.queue.here",
      "destination_type": "queue",
      "routing_key": "",
      "arguments": {
        
      }
    },
    {
      "source": "router",
      "vhost": "\/",
      "destination": "b.queue.here",
      "destination_type": "queue",
      "routing_key": "",
      "arguments": {
        
      }
    }
  ]
}

You could – at this point – go into your Docker-ised RabbitMQ, and repeat the process for “Import / export definitions”, do the “upload broker definitions” step and it should all work.

The downside is – as mentioned above – if you delete the volume (or go to a different PC) then unfortunately, your queues etc don’t follow you. No good.

Now, my solution to this is not perfect. It is a static setup, which sucks. I would like to make this dynamic, but for now, what I have is good enough. Please do shout up if you know of a way to make this dynamic, without resorting to a bunch of shell scripts.

Ok, so I take the definitions.json file, and the other config file, rabbitmq.config, and I copy them into the RabbitMQ directory that contains my Dockerfile:

➜  symfony-docker git:(master) ✗ ls -la rabbitmq 
total 24
drwxrwxr-x  2 chris chris 4096 Mar 24 21:13 .
drwxrwxr-x 10 chris chris 4096 Mar 25 12:11 ..
-rw-rw-r--  1 chris chris 1827 Mar 25 12:38 definitions.json
-rw-rw-r--  1 chris chris  130 Mar 25 12:55 Dockerfile
-rw-rw-r--  1 chris chris   54 Mar 24 20:53 enabled_plugins
-rw-rw-r--  1 chris chris  122 Mar 25 12:55 rabbitmq.config

For completeness, the enabled_plugins  file contents are simply:

[rabbitmq_management, rabbitmq_management_visualiser].

And the rabbitmq.config file is:

[
    {
        rabbitmq_management, [
            {load_definitions, "/etc/rabbitmq/definitions.json"}
        ]
    }
].

And the Dockerfile :

FROM rabbitmq:3.6.8-management

(yes, just that one line)

Now, to get these files to work seems like you would need to override the existing files in the container. To do this, I used additional config in the docker-compose volumes section:

    rabbit1:
        build: ./rabbitmq
        container_name: rabbit1
        hostname: "rabbit1"
        environment:
          RABBITMQ_ERLANG_COOKIE: "HJKLSDFGHJKLMBZSFXFZD"
          RABBITMQ_DEFAULT_USER: "rabbitmq"
          RABBITMQ_DEFAULT_PASS: "rabbitmq"
          RABBITMQ_DEFAULT_VHOST: "/"
        ports:
          - "15672:15672"
          - "5672:5672"
        labels:
          NAME: "rabbit1"
        volumes:
          - "./rabbitmq/enabled_plugins:/etc/rabbitmq/enabled_plugins"
          - "./rabbitmq/rabbitmq.config:/etc/rabbitmq/rabbitmq.config:rw"
          - "./rabbitmq/definitions.json:/etc/rabbitmq/definitions.json:rw"
          - "./volumes/rabbitmq/rabbit1/data:/var/lib/rabbitmq:rw"

Note here the new volumes.

Ok, so down, rebuild, and up:

docker-compose down && docker-compose build && docker-compose up -d
➜  symfony-docker git:(master) ✗ docker ps -a
CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS                      PORTS                                      NAMES
47c448ea31a2        composer                "/docker-entrypoin..."   30 seconds ago      Exited (0) 29 seconds ago                                              composer
f094719f6444        symfonydocker_nginx     "nginx -g 'daemon ..."   30 seconds ago      Up 29 seconds               0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   nginx
cfaf9b7328dd        symfonydocker_symfony   "docker-php-entryp..."   31 seconds ago      Up 30 seconds               9000/tcp                                   symfony
6b27600dc726        symfonydocker_rabbit1   "docker-entrypoint..."   31 seconds ago      Exited (1) 29 seconds ago                                              rabbit1
8301d6282c7d        symfonydocker_php       "docker-php-entryp..."   31 seconds ago      Up 29 seconds               9000/tcp                                   php
f6105aac9cfb        mysql                   "docker-entrypoint..."   31 seconds ago      Up 30 seconds               0.0.0.0:3306->3306/tcp                     mysql

The output is a bit messy, but the problem is the RabbitMQ container has already exited, but should still be running.

To view the logs for RabbitMQ at this stage is really easy – though a bit weird.

What I would like to do is to get RabbitMQ to write its log files out to my disk. But adding in a new volume isn’t solving this problem – one step at a time (I don’t have a solution to this issue just yet, I will add another blog post when I figure this out). The issue is that RabbitMQ is writing its logs to tty by default.

Anyway:

➜  symfony-docker git:(master) ✗ docker-compose logs rabbit1                                                         
Attaching to rabbit1
rabbit1     | /usr/local/bin/docker-entrypoint.sh: line 278: /etc/rabbitmq/rabbitmq.config: Permission denied

Ok, bit odd.

Without going the long way round, the solution here is – as I said at the start – logical, but not immediately obvious.

As best I understand this, the issue is the provided environment variables now conflict with the user / pass combo in the definitions file.

Simply commenting out the environment variables fixes this:

    rabbit1:
        build: ./rabbitmq
        container_name: rabbit1
        hostname: "rabbit1"
        environment:
          RABBITMQ_ERLANG_COOKIE: "HJKLSDFGHJKLMBZSFXFZD"
          # RABBITMQ_DEFAULT_USER: "rabbitmq"
          # RABBITMQ_DEFAULT_PASS: "rabbitmq"
          # RABBITMQ_DEFAULT_VHOST: "/"
        ports:
          - "15672:15672"
          - "5672:5672"
        labels:
          NAME: "rabbit1"
        volumes:
          - "./rabbitmq/enabled_plugins:/etc/rabbitmq/enabled_plugins"
          - "./rabbitmq/rabbitmq.config:/etc/rabbitmq/rabbitmq.config:rw"
          - "./rabbitmq/definitions.json:/etc/rabbitmq/definitions.json:rw"
          - "./volumes/rabbitmq/rabbit1/data:/var/lib/rabbitmq:rw"

Another down, build, up…

➜  symfony-docker git:(master) ✗ docker-compose down && docker-compose build && docker-compose up -d --remove-orphans
Stopping nginx ... done
Stopping symfony ... done
Stopping php ... done
Stopping mysql ... done
Removing nginx ... done
Removing composer ... done
Removing symfony ... done
Removing rabbit1 ... done
Removing php ... done
# etc

And this time things look a lot better:

➜  symfony-docker git:(master) ✗ docker ps -a                                                                        
CONTAINER ID        IMAGE                   COMMAND                  CREATED              STATUS                          PORTS                                                                                        NAMES
91d59e754d1a        composer                "/docker-entrypoin..."   About a minute ago   Exited (0) About a minute ago                                                                                                composer
b7db79270773        symfonydocker_nginx     "nginx -g 'daemon ..."   About a minute ago   Up About a minute               0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp                                                     nginx
bd74d10e444a        symfonydocker_rabbit1   "docker-entrypoint..."   About a minute ago   Up About a minute               4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp   rabbit1
720e91743aa6        symfonydocker_php       "docker-php-entryp..."   About a minute ago   Up About a minute               9000/tcp                                                                                     php
ec69b7c038e9        symfonydocker_symfony   "docker-php-entryp..."   About a minute ago   Up About a minute               9000/tcp                                                                                     symfony
717ed6cb180f        mysql                   "docker-entrypoint..."   About a minute ago   Up About a minute               0.0.0.0:3306->3306/tcp                                                                       mysql
➜  symfony-docker git:(master) ✗ docker-compose logs rabbit1
Attaching to rabbit1
rabbit1     | 
rabbit1     | =INFO REPORT==== 25-Mar-2017::13:17:23 ===
rabbit1     | Starting RabbitMQ 3.6.8 on Erlang 19.3
rabbit1     | Copyright (C) 2007-2016 Pivotal Software, Inc.
rabbit1     | Licensed under the MPL.  See http://www.rabbitmq.com/
rabbit1     | 
rabbit1     |               RabbitMQ 3.6.8. Copyright (C) 2007-2016 Pivotal Software, Inc.
rabbit1     |   ##  ##      Licensed under the MPL.  See http://www.rabbitmq.com/
rabbit1     |   ##  ##
rabbit1     |   ##########  Logs: tty
rabbit1     |   ######  ##        tty
rabbit1     |   ##########
rabbit1     |               Starting broker...

Hopefully that helps someone save a little time in the future.

Now, onto the logs issue… the fun never stops.