Flask, Docker & React: Microservices done right Pt. 2

In this part we’ll be looking at containerisation, in particular containerising our app we built in Part 1. Rather than go into the pros and cons of using Docker (over say a traditional VM), a good primer would be the following articles – Virtualisation and Containers Intro and 6 Docker Basics.

Getting Started

Along with the toolset installed from the previous post, this part of the series uses the following tools and technologies:

Part 2.
  • VirtualBox version 5.1.22
  • Docker version 17.06.0-ce
  • Docker Compose version 1.14.0
  • Docker Machine version 0.12.0

Note: Your version of docker might differ.

Objectives

  1. Configure and run services locally with Docker, Docker Compose, and Docker Machine
  2. Utilise volumes to mount your code into a container

Configuring Docker

To begin, we double check that that docker has been installed correctly.

(env) ➜ matrix-pt2 git:(master) ✗ docker -v
Docker version 17.06.0-ce, build 02c1d87
(env) ➜ matrix-pt2 git:(master) ✗ docker-compose -v
docker-compose version 1.14.0, build c7bdf9e
(env) ➜ matrix-pt2 git:(master) ✗ docker-machine -v
docker-machine version 0.12.0, build 45c69ad
(env) ➜ matrix-pt2 git:(master) ✗ vboxmanage -v
5.1.22r115126

With all the necessary tools installed, we then have to create Docker host that we can connect a client too:

(env) ➜ matrix-pt2 git:(master) ✗ docker-machine create -d virtualbox dev
(env) ➜ matrix-pt2 git:(master) ✗ eval "$(docker-machine env dev)"

From this stackoverflow post, the purpose of the eval command is as follows:

Using eval executes each of the export commands that are executed as result of “docker-machine env dev”, instead of just writing them to the console, so it’s a quick way of setting up your docker host environment variables.

Next up, we’ll add a DockerFile to the root of our project:

FROM python:3.6.1

# set working directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# add requirements (to leverage Docker cache)
ADD ./requirements.txt /usr/src/app/requirements.txt

# install requirements
RUN pip install -r requirements.txt

# add app
ADD . /usr/src/app

# run server
CMD python manage.py runserver -h 0.0.0.0

Then add a docker-compose.yml file to the root:

version: '2.1'

services:

  matrix:
    container_name: matrix
    build: .
    volumes:
      - '.:/usr/src/app'
    ports:
      - 5001:5000 # expose ports - HOST:CONTAINER

This config will create a container called matrix, from the Dockerfile. The volumes tag is where the real magic happens. The volume is used to mount the code into the container. Given that this is still a development environment, we would still like hot-reload functionality allowing for code changes to be reflected when changes are made locally (the host). Without this option, any changes made would require a complete re-build after each code change.

Next we’re going to build our image from our docker config files:

(env) ➜  matrix-pt2 git:(master) ✗ docker-compose build

Please note: On first run of the above command, Docker might take a little while to run – it has to pull down the python image. Subsequent runs will be considerably quicker due to the same image being cached locally.

Once completed, start up the container with the following command:

(env) ➜  matrix-pt2 git:(master) ✗ docker-compose up -d

The -d flag is used to run the containers in the background.

We’ll now grab the associated IP of the running container (mine is 192.168.99.100):

(env) ➜  matrix-pt2 git:(master) ✗ docker-machine ip dev

In your browser, navigate to http://192.168.99.100:5001/matrix (replacing 192.168.99.100 with your own IP) – you should now see the same {hello:world} JSON response as before.

Next, add an environment variable to the docker-compose.yml file to load the app config for the DEV environment (lines 12 and 13):

version: '2.1'

services:

  matrix:
    container_name: matrix
    build: .
    volumes:
      - '.:/usr/src/app'
    ports:
      - 5001:5000 # expose ports - HOST:CONTAINER
    environment:
      - APP_SETTINGS=project.config.DevelopmentConfig # Configures APP to be DEV aware

Consequently, update project/__init__.py, to pull in the newly added environment variables (lines 3, 9, 10 and 11):

# project/__init__.py

import os
from flask import Flask, jsonify

# instantiate the app
app = Flask(__name__)

# set config from ENV variable
app_settings = os.getenv('APP_SETTINGS')
app.config.from_object(app_settings)


@app.route('/matrix', methods=['GET'])
def matrix():
    return jsonify({
        'hello': 'world'
    })

Finally update the container by re-running the compose up command:

(env) ➜  matrix-pt2 git:(master) ✗ docker-compose up -d

And there you have it. A completely containerised flask app that hot reloads on code changes. As per usual, all code samples can be found on the github page.

Leave a Reply

Your email address will not be published. Required fields are marked *