Playing with Docker, Silex, Python, Node and WebSockets

I’m learning Docker. In this post I want to share a little experiment that I have done. I know the code looks like over-engineering but it’s just an excuse to build something with docker and containers. Let me explain it a little bit.

The idea is build a Time clock in the browser. Something like this:

Clock

Yes I know. We can do it only with js, css and html but we want to hack a little bit more. The idea is to create:

  • A Silex/PHP frontend
  • A WebSocket server with socket.io/node
  • A Python script to obtain the current time

WebSocket server will open 2 ports: One port to serve webSockets (socket.io) and another one as a http server (express). Python script will get the current time and it’ll send it to the webSocket server. Finally one frontend(silex) will be listening to WebSocket’s event and it will render the current time.

That’s the WebSocket server (with socket.io and express)
[sourcecode language=”js”]
var
express = require(‘express’),
expressApp = express(),
server = require(‘http’).Server(expressApp),
io = require(‘socket.io’)(server, {origins: ‘localhost:*’})
;

expressApp.get(‘/tic’, function (req, res) {
io.sockets.emit(‘time’, req.query.time);
res.json(‘OK’);
});

expressApp.listen(6400, ‘0.0.0.0’);

server.listen(8080);
[/sourcecode]

That’s our Python script

[sourcecode language=”python”]
from time import gmtime, strftime, sleep
import httplib2

h = httplib2.Http()
while True:
(resp, content) = h.request("http://node:6400/tic?time=" + strftime("%H:%M:%S", gmtime()))
sleep(1)
[/sourcecode]

And our Silex frontend
[sourcecode language=”php”]
use Silex\Application;
use Silex\Provider\TwigServiceProvider;

$app = new Application([‘debug’ => true]);
$app->register(new TwigServiceProvider(), [
‘twig.path’ => __DIR__ . ‘/../views’,
]);

$app->get("/", function (Application $app) {
return $app[‘twig’]->render(‘index.twig’, []);
});

$app->run();
[/sourcecode]

using this twig template

[sourcecode language=”html”]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Docker example</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&quot; integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href="css/app.css" rel="stylesheet">
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script&gt;
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script&gt;
</head>
<body>
<div class="site-wrapper">
<div class="site-wrapper-inner">
<div class="cover-container">
<div class="inner cover">
<h1 class="cover-heading">
<div id="display">
display
</div>
</h1>
</div>
</div>
</div>
</div>
<script src="//localhost:8080/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script&gt;
<script>
var socket = io.connect(‘//localhost:8080’);

$(function () {
socket.on(‘time’, function (data) {
$(‘#display’).html(data);
});
});
</script>
</body>
</html>
[/sourcecode]

The idea is to use one Docker container for each process. I like to have all the code in one place so all containers will share the same volume with source code.

First the node container (WebSocket server)

[sourcecode language=”text”]
FROM node:argon

RUN mkdir -p /mnt/src
WORKDIR /mnt/src/node

EXPOSE 8080 6400
[/sourcecode]

Now the python container
[sourcecode language=”text”]
FROM python:2

RUN pip install httplib2

RUN mkdir -p /mnt/src
WORKDIR /mnt/src/python
[/sourcecode]

And finally Frontend contailer (apache2 with Ubuntu 16.04)

[sourcecode language=”text”]
FROM ubuntu:16.04

RUN locale-gen es_ES.UTF-8
RUN update-locale LANG=es_ES.UTF-8
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update -y
RUN apt-get install –no-install-recommends -y apache2 php libapache2-mod-php
RUN apt-get clean -y

COPY ./apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf

RUN mkdir -p /mnt/src

RUN a2enmod rewrite
RUN a2enmod proxy
RUN a2enmod mpm_prefork

RUN chown -R www-data:www-data /mnt/src
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_LOCK_DIR /var/lock/apache2
ENV APACHE_PID_FILE /var/run/apache2/apache2.pid
ENV APACHE_SERVERADMIN admin@localhost
ENV APACHE_SERVERNAME localhost

EXPOSE 80
[/sourcecode]

Now we’ve got the three containers but we want to use all together. We’ll use a docker-compose.yml file. The web container will expose port 80 and node container 8080. Node container also opens 6400 but this port is an internal port. We don’t need to access to this port outside. Only Python container needs to access to this port. Because of that 6400 is not mapped to any port in docker-compose

[sourcecode language=”text”]
version: ‘2’

services:
web:
image: gonzalo123/example_web
container_name: example_web
ports:
– "80:80"
restart: always
depends_on:
– node
build:
context: ./images/php
dockerfile: Dockerfile
entrypoint:
– /usr/sbin/apache2
– -D
– FOREGROUND
volumes:
– ./src:/mnt/src

node:
image: gonzalo123/example_node
container_name: example_node
ports:
– "8080:8080"
restart: always
build:
context: ./images/node
dockerfile: Dockerfile
entrypoint:
– npm
– start
volumes:
– ./src:/mnt/src

python:
image: gonzalo123/example_python
container_name: example_python
restart: always
depends_on:
– node
build:
context: ./images/python
dockerfile: Dockerfile
entrypoint:
– python
– tic.py
volumes:
– ./src:/mnt/src
[/sourcecode]

And that’s all. We only need to start our containers
[sourcecode language=”bash”]
docker-compose up –build -d
[/sourcecode]

and open our browser at: http://localhost to see our Time clock

Full source code available within my github account

9 thoughts on “Playing with Docker, Silex, Python, Node and WebSockets


  1. Hello, very interesting! Just my two cents 😉

    – As the compose file talks about ‘composition’ of containers, I would hide container implementation details on it. I mean, better specify container entrypoint on Dockerfiles (ENTRYPOINT sentence).
    – You can group RUN operations on Dockerfile to generate less layers and smaller containers. For example, instead of three RUN sentences invoking apt-get, have only one RUN apt-get update && apt-get install && apt-get clean.


    1. Thanks. I use entrypoint in docker-compose especially because node containers. I prefer to put code in a mounted container (and share it between containers). That means that code isn’t available (I cannot run npm start) until docker-compose is executed. Anyway I agree with you.

Leave a Reply