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:
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:
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:
You should see the following page:
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:
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:
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
Leave a Reply