Note: I'm migrating from gonzalo123.com to here. When I finish I'll swap the DNS to here. The "official" blog will be always gonzalo123.com

      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)

      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);
      

      That’s our Python script

      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)
      

      And our Silex frontend

      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();
      

      using this twig template

      <!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" 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>
          <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
      </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>
      <script>
      var socket = io.connect('//localhost:8080');
       
      $(function () {
          socket.on('time', function (data) {
              $('#display').html(data);
          });
      });
      </script>
      </body>
      

      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)

      FROM node:argon
       
      RUN mkdir -p /mnt/src
      WORKDIR /mnt/src/node
       
      EXPOSE 8080 6400
      

      Now the python container

      FROM python:2
       
      RUN pip install httplib2
       
      RUN mkdir -p /mnt/src
      WORKDIR /mnt/src/python
      

      And finally Frontend contailer (apache2 with Ubuntu 16.04)

      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
      

      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

      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
      

      And that’s all. We only need to start our containers

      docker-compose up --build -d
      

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

      Full source code available within my github account

      comments powered by Disqus