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.