The purpose of this blog is to demonstrate how to dockerize a NodeJS and a Python application and how it is possible to make them talk together via a middleware like ActiveMQ.

NodeJS

Let’s write a very simple node software as shown below:

console.log("Hello World!");
setInterval(function () {
  console.log("Hello From NodeJS Timer !");
},1000);

Save the file as app.js.

It is off course possible to run it in a console with the following command:

node app.js

Let’s us a container to run the application. There two choices, the code can be added inside a container via a Dockerfile or the the application can be run on a path  outside the container. As we have a very simple software, we will follow this path.

In order to orchestrate our integration, we will write a docker-compose.yml file like follows:

version: '2'
services:
  nodejs1:
    image: node:4
    container_name: nodejs1
    volumes:
      - /Users/snuids/Blog/NodePythonDemo/NodeJS:/usr/src/app
    working_dir: /usr/src/app
    command: node app.js
    restart: always

Don’t forget to change the volume path to the folder where your app.js file is.

Trigger the docker-compose via the following command in the docker-compose.yml folder:

docker-compose up

The log should show the following display:

screen-shot-2016-10-22-at-14-31-17

Python

Let’s write the same program in python. The code could look like follows:

from threading import Timer

print "Hello World"

def timerFunction():
 print "Hello from Python Timer"
 t = Timer(1.0, timerFunction)
 t.start()

timerFunction()

Once again, you can send the program by typing:

python app.py

But we will use a python container to run it. So let’s add it in our docker-compose file:

version: '2'
 services:

nodejs1:
 image: node:4
 container_name: nodejs1
 volumes:
 - /Users/snuids/Blog/NodePythonDemo/NodeJS:/usr/src/app
 working_dir: /usr/src/app
 command: node app.js
 restart: always

python1:
 image: python:2
 container_name: python1
 volumes:
 - /Users/snuids/Blog/NodePythonDemo/Python:/usr/src/app
 working_dir: /usr/src/app
 command: python -u app.py
 restart: always

Once again trigger the docker-compose file:

docker-compose up

The log should show the following display:

screen-shot-2016-10-22-at-15-02-15

ActiveMQ

You can add an ActiveMQ in the mix by firing an already packaged container as the one we are using that includes a more modern console as the one included in the default ActiveMQ.

The docker-compose.yml file becomes:

version: '2'
services:

nodejs1:
 image: node:4
 container_name: nodejs1
 volumes:
 - /Users/snuids/Blog/NodePythonDemo/NodeJS:/usr/src/app
 working_dir: /usr/src/app
 command: node app.js
 restart: always
 links:
 - amqc

python1:
 image: python:2
 container_name: python1
 volumes:
 - /Users/snuids/Blog/NodePythonDemo/Python:/usr/src/app
 working_dir: /usr/src/app
 command: python -u app.py
 restart: always
 links:
 - amqc

amqc:
 image: snuids/activemq-amqcmonitoring:latest
 ports:
 - "8161:8161"
 - "61616:61616"
 - "61614:61614"
 - "61613:61613"
 container_name: amqc

Note that the python and the nodejs containers link the amqc container. It is now possible to access ActiveMQ via the following address:

http://localhost:8161/AMQC

You should see the following page:

screen-shot-2016-10-22-at-15-11-00

By default the login and password are admin.

Publishing from NodeJS

In order to send a message to ActiveMQ from node, the easiest protocol is stomp.

Add the required library by typing the following command at the same level as the app.js file:

npm install stompy

The node program becomes:

var stomp = require('stompy');
var config={
host: 'amqc',
port: 61613,
login: 'admin',
passcode: 'admin'
}

client = stomp.createClient(config);

console.log("Hello World!");

setInterval(function () {
console.log("Hello From NodeJS Timer !");
client.publish('/topic/FROM_NODE_JS', 'Hello From NodeJS Timer !');
},1000);

You can check that the nodejs is correctly sending messages via the internal AMQC client as shown in the following screenshot:

screen-shot-2016-10-22-at-15-26-12

Subscribing from Python

The first step is to install the stompy library via the pip package manager.

pip install stomp.py

The problem is that this command must be executed in the container in order for the python framework to get access to it.

An easy way but not really professional to solve that, is to execute it inside the container via a bash with a command like:

docker exec -it python1 bash

And launch the command from the container as shown below:

screen-shot-2016-10-22-at-16-17-48

This is of course a very bad idea because this step must be redone every time the container is destroyed and of course if the docker-compose file is deployed somewhere else. The best solution would have been to create a specific container with the required library.

Let’s do it the right way via a docker file as follows. Save the following file as Dockerfile.

FROM python:2
MAINTAINER snuids
RUN pip install stomp.py

Then build the image with the command:

docker build .
snuids@AMAMBookPro2 ~/Blog/NodePythonDemo/PythonDockerFile $docker build .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM python:2
 ---> 77cf0ea98df6
Step 2 : MAINTAINER snuids
 ---> Using cache
 ---> bd6dbadca26d
Step 3 : RUN pip install stompy
 ---> Using cache
 ---> dbbafe7b33d3
Successfully built dbbafe7b33d3
snuids@AMAMBookPro2 ~/Blog/NodePythonDemo/PythonDockerFile $vi Dockerfile 
snuids@AMAMBookPro2 ~/Blog/NodePythonDemo/PythonDockerFile $docker build .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM python:2
 ---> 77cf0ea98df6
Step 2 : MAINTAINER snuids
 ---> Using cache
 ---> bd6dbadca26d
Step 3 : RUN pip install stomp.py
 ---> Running in 6ddfb467e652
Collecting stomp.py
  Downloading stomp.py-4.1.13-py2.py3-none-any.whl
Installing collected packages: stomp.py
Successfully installed stomp.py-4.1.13
 ---> d63d98ded64a
Removing intermediate container 6ddfb467e652
Successfully built d63d98ded64a
snuids@AMAMBookPro2 ~/Blog/NodePythonDemo/PythonDockerFile $docker tag d63d98ded64a pythonwithstompy

So now there is a new image that we can use in docker-compose for our python software.

Modify the docker compose file to use it:

version: '2'
services:

nodejs1:
 image: node:4
 container_name: nodejs1
 volumes:
 - /Users/snuids/Blog/NodePythonDemo/NodeJS:/usr/src/app
 working_dir: /usr/src/app
 command: node app.js
 restart: always
 links:
 - amqc

python1:
 image: pythonwithstompy
 container_name: python1
 volumes:
 - /Users/snuids/Blog/NodePythonDemo/Python:/usr/src/app
 working_dir: /usr/src/app
 command: python -u app.py
 restart: always
 links:
 - amqc

amqc:
 image: snuids/activemq-amqcmonitoring:latest
 ports:
 - "8161:8161"
 - "61616:61616"
 - "61614:61614"
 - "61613:61613"
 container_name: amqc

And then to modify the python program as follows:

import stomp
from threading import Timer

print "Hello World"

count=0;

class MyListener(stomp.ConnectionListener):
    def on_error(self, headers, message):
        print('Received an error "%s"' % message)
    def on_message(self, headers, message):
        print('Received a message "%s"' % message)

conn=stomp.Connection([("amqc", 61613)])
conn.set_listener('', MyListener())
conn.start()
conn.connect('admin','admin',wait=True)

conn.subscribe(destination='/topic/FROM_NODE_JS', id=1, ack='auto')

def timerFunction():
    global count;
    str="Hello from Python Timer %d" %(count)
    print str
    conn.send(body=str, destination='/topic/FROM_PYTHON')
    count=count+1
    t = Timer(1.0, timerFunction)
    t.start()

timerFunction()

Subscribing from NodeJS

Subscribing from NodeJS is quite easy as shown in the next code:

var stomp = require('stompy');
 var config={
 host: 'amqc',
 port: 61613,
 login: 'admin',
 passcode: 'admin'
 }
 var count=0;

client = stomp.createClient(config);

console.log("Hello World!");

setInterval(function () {
 var str="Hello From NodeJS Timer !"+count
 console.log(str);
 client.publish('/topic/FROM_NODE_JS',str);
 count++;
 },1000);

client.subscribe('/topic/FROM_PYTHON', function (msg) {
 console.info(msg);
 });

Restart all the containers with the following command:

docker-compose down

Restart all with this one

docker-compose up -d

Check the log of the nodejs container with this one:

docker logs nodejs1

Check the log of the python container with this one:

docker logs python1