Using ZeroMQ with Docker

Last time we built three client-server Node.js apps that were talking to each other using ZeroMQ. However, running both client and server on localhost is a little bit lame. Let’s put them into containers! They’ll still be lame, but now with Docker around.

So, here’s the plan: let’s see what we need to do to last week’s fire-and-forget ZeroMQ app, so its client and server can work and communicate from within Docker containers.

The plan.

Firstly, let’s bring back code for client and server in order to see what we have to deal with.

The server (very slightly modified):

And the client:

The server sends “Ping” message every two seconds and the client happily receives it. Not exactly a rocket science. Here’s what I think needs to be done:

  • The first obvious problem is hardcoded IP address. Even if I decide to keep using port 3000 and make server app to listen at all network interfaces ( tcp://*:3000 ), client’s ‘connect’ address still has to be parametrized.
  • Client and server Docker images won’t create themselves, so we need two Dockerfiles for that.
  • When server app starts in a container, it’ll have a dynamic IP address assigned to it. But in order to pass it as a parameter to the client I need to know it in advance. That would’ve been a problem, if containers couldn’t talk to each other using their names instead of IPs. But they need to be connected to user-defined network first. If only we had a tool (*cough* docker-compose) that can easily assign names to containers, while attaching them to user-defined network.

These three steps should be enough.

Parametrize client’s ‘connect’ address

Containers, Docker and docker-compose provide simple way to define environmental variables. It’s also not hard to read them from Node.js app. Sounds like we found ourselves the way to pass server’s container name to the client. This change, as well as additional console.log for potential troubleshooting, results something like this (see highlighted rows):

Because now client can be told to connect to any port, let’s parametrize server’s ‘bind’ address as well:

Behold! The apps are ready.

Create Dockerfile

Dockerfile contains steps necessary for building the image of app container. We have two apps, so there’re two Dockerfiles to create:

Folders structure

Dockerfile itself will be quite trivial: use official node image, copy app files on top of it, install dependences, open ports and start client/server whenever container starts. Here’s what I ended up with for the server:

Installing dependences ( RUN ... ) is the only tricky step. Firstly, I want to reinstall all NPM modules, that’s what the first and the last lines of RUN section are for. Secondly, ZeroMQ has a dependency – libzmq-dev , but before we can install it, apt-get  itself should be updated. node image tries to be as small as possible, so apt-get’s sources lists will be empty and it’ll have no idea where to find zeromq-dev.

Let’s build it and run:

So far, so good. Client’s Dockerfile is almost identical to server’s – just another JavaScript file to run.

Prepare docker-compose.yml file

We need to assign names to containers and attach them to user-defined network – otherwise they won’t be able to talk to each other by name. The easiest way to do that is through docker-compose – a tool for managing distributed app as a whole. I’ll put app names, links to Dockerfiles and env variables to  docker-compose.yml , and through some ancient magic docker-compose will be able to launch it. I don’t have to do anything about networks in it – compose attaches containers to newly created user-defined network by default.

Now I put config file along with client and server folders and here’s how it looks in the end:

No tricky parts at all. Let’s run it:

It works! What’s cool about it, if I wanted to create more clients or servers just to see what difference it makes, I’d just copy-pasted few lines in docker-compose.yml and that would do it.

Summary

We briefly took a look at how to convert existing client-server app that uses ZeroMQ for communication into set of Docker containers that are still able to talk. That appeared to be relatively easy. We had to replace hardcoded IP addresses with environmental variables, create Dockerfile for both client and server and use docker-compose to launch them. Compose wasn’t strictly speaking necessary for that, but it simplified name and network management, as well as made it easy to specify values for environmental vars. Complete working example of code above can be found here.

4 thoughts on “Using ZeroMQ with Docker

  1. Your fire-and-forget code example is missing the client and server .js files and package.json. Thank you for the example but I cannot get it running without the package.jsons

    1. Yup, you’re right. I wonder how I managed to screw that up: git was thinking that server and client directories were submodules. I reuploaded the files and it should be fine now.
      Thanks for letting me know.

  2. Very useful, I borrow the idea of passing the address by environment variable.
    But I didn’t managed to install zmq on the latest node image, libzmq-dev not found and libzmq3-dev gives errors when I install my npm module. I had to first download the zmq package and compile it.
    Thanks Pavel!

Leave a Reply

Your email address will not be published. Required fields are marked *