I wrote an article about how to use ZeroMQ with Scala in OS X but there is no need of polluting our system with a bunch of internal libraries which are only required for building ZeroMQ. So I decided to create a docker image with all that process bundled into a shippable and ready-to-use docker container. The idea is simple, I want to develop my programs in my favorite (local) IDE but to run it in an environment with all the required infrastructure containerized. I have already created a sample Hello0MQ project that I can use to test the idea. So first thing to do was to create that nicely packed docker image that can be found here. This includes the ZeroMQ library v4.1.4 and also the JZMQ and SBT. Then, when I want to execute my program, I can use a mapped volume to expose my project from my system to the docker container that will run it. The unique downside I found when doing this is that I cannot compile the code locally as I do not have the JZMQ binding installed (so the zmq.jar is not present). But I can do in one command but using the container as you will see.

Using Docker instead of polluting our environment

This guide assumes you have installed docker (I am using v1.9.1) and docker-machine (I am using v0.5.1) running on OS X (El Capitan).

Create a virtualbox instance with docker engine running on it. Inside the VM we will run our programs:

    docker-machine create -d virtualbox --virtualbox-memory 4096 --virtualbox-boot2docker-url=https://github.com/boot2docker/boot2docker/releases/download/v1.9.0/boot2docker.iso dm-zmq

Note: I found a problem with the latest version of the Boot2Docker VM image which caused the creation of our image to get stuck when setting up the java certificates so the image was not created (I needed to exit the process). Also, I needed to increase the in-memory tmpfs mount point in order to be able to install all the packages needed for the building of the requisites. Otherwise, I was getting E: You don't have enough free space in /var/cache/apt/archives/ error.

Then, set up the shell environment variables to point to the just created docker machine:

    eval $(docker-machine env dm-zmq)

This way, each docker command will be using the docker engine running on the VM named dm-zmq. As already said, we are going to use an image called zmq-sbt. This image has already installed the libzmq and libjzmq libraries in /usr/local/lib and zmq.jar java package in /usr/local/share/java. Those path will be needed in order to create use ZeroMQ in our Scala project (zmq.jar will be required in your build.sbt to declare the dependency).

In order to test this, I have created a GitHub repository with a sample project to play with. This project is composed of a couple of process, a client and a server which are using REQ/REP ZeroMQ pattern to exchange messages on port 5555. If you look at the build.sbt, you will realize that it is using this declaration:

unmanagedJars in Compile ++=
    (file("/usr/local/share/java/") * "zmq.jar").classpath

which points to the place where zmq.jar is located inside the docker container.

So, let's start by cloning the git repository:

    mkdir ~/zmq-sbt
    cd ~/zmq-sbt
    git clone https://github.com/oscar-martin/Hello0MQ.git

Then, we will need two shell sessions, one for running the client and another for the server. Note that the docker image contains an ENTRYPOINT which value is sbt and it contains a JVM property for instructing where to load both ZeroMQ and JZMQ libraries when sbt runs. In the first session (you could keep using the same session you have used so far), run this:

    eval $(docker-machine env dm-zmq)
    cd ~/zmq-sbt
    docker run -it --name server -v `pwd`/Hello0MQ:/project -v ~/.ivy2:/root/.ivy2 -v ~/.sbt:/root/.sbt -p 5555:5555 oscarmartin/zmq-sbt

This will create an SBT prompt inside the container where to issue SBT commands. In order to compile and run the server, run:

    > project server
    > run

This will create the server and will listening on port 5555. A couple of important things to comment. On one side, we are mapping the container port 5555 (where our server will listen to) to the VM port 5555. So each time someone connects to VM in port 5555, that will reach the server running inside our container. On the other side, we are using different mapped volumes.

  • `pwd`/Hello0MQ:/project
  • ~/.ivy2:/root/.ivy2
  • ~/.sbt:/root/.sbt

The first one is used for the container to be able to work with your project. The other two mapped volumes help us to boot up the container much faster once the ivy2 and sbt cache folder are filled up.

Create a second shell session and run this:

    eval $(docker-machine env dm-zmq)
    cd ~/zmq-sbt
    docker run -it --name client -v `pwd`/Hello0MQ:/project -v ~/.ivy2:/root/.ivy2 -v ~/.sbt:/root/.sbt oscarmartin/zmq-sbt

This will create an SBT prompt inside the container where to issue SBT commands. You will need to set the IP address of the VM in where your docker container is running on. Execute this for getting the IP address:

    docker-machine ip dm-zmq

In order to compile and run the client, run:

    > project client
    > run <dm-zmq IP>

You could also have been used the internal IP address of the container as both docker containers live in the same docker engine which means they are visible each other in the docker bridge. Let's try it:

docker inspect --format '{{ .NetworkSettings.IPAddress }}' server

So that IP can be used when running the client. In order to test it, you should remove the running containers. Exit them and execute this in one shell session:

    docker rm -f `docker ps -aq`

Then, execute the same commands as before but use the container IP instead of the VM IP. You can also remove the port mapping from the server docker container as it is not needed in that test (this means to remove the -p 5555:5555 from the command that start the server container). It should work. A more realistic example would be to create two different VM (virtualboxes locally) and to create a docker cluster (Swarm) between them and finally to create a docker-network where to "plug" your docker containers in order for them to use that network.

Once you execute it, you should exit the docker container and run it again instead of using the same SBT session because otherwise it will throw “UnsatisfiedLinkError: Native Library already loaded in another classloader which seems to be related to the way SBT handles the runtime environment.

Finally, if all you want to do is to compile your source code, just issue this command:

    docker run -t --rm  -v `pwd`/Hello0MQ:/project -v ~/.ivy2:/root/.ivy2 -v ~/.sbt:/root/.sbt oscarmartin/zmq-sbt compile

You can use oscarmartin/zmq-sbt image as a SBT process already configured to load ZeroMQ libraries:

    docker run -t --rm  -v `pwd`/Hello0MQ:/project -v ~/.ivy2:/root/.ivy2 -v ~/.sbt:/root/.sbt oscarmartin/zmq-sbt <sbt command>

Last, I will try to make the docker image a bit thinner than it actually is ;-)