A sure-shot way to configure Flask application with Postgres on Docker(On Mac).

Containerizing our application is now become the habit for all the projects. As we get exposed to different technology stack, we face new challenges in implementing common solutions.

Not only for CI/CD, we containerize for other reasons as well. We often build a sizeble inventory of projects. We may not end up using them immediately. We may use them intermittently and especially when we have proof-of-concept type of projects. When the time comes to clone these projects for repository, build them and start using for our purpose, a lot of libraries and patches that we used earlier become obsolete. We need to do too much of a circus in restoring our projects back to the stage they are left at. Containerizing application relieves us from all these hurdles and time wasters - a sin in the era of lean development. Hence, it is best practice now to employ technologies like Docker in every project.

What am i upto now

I have another unqiue technology stack combination - Python flask application, Mac OS and Postgress DB. In this article, I am going to show you how to -

  1. Create a Python Flask application.
  2. Add models to the the application using SQLAlchemy which is an ORM.
  3. Set up docker on your system.
  4. Create a docker container for the flask app.
  5. Create a docker container for the postgres database.
  6. Connect the two containers using a port mapping and network.
  7. Create a persistent data storage for the application using docker volumes.
  8. Migrate the models to the Postgres database on Docker volume.

It is a long list so lets get started right away.

Create a Python Flask application locally.

First let us create the flask app locally and migrate it to the database. After that we will see how to do the same thing on Docker. Create the directory structure as below.

Leave the venv folder for now as this will be automatically created when we create the virtual environment which is the next step. Open the folder in the terminal.

Create the virtual environment.

python3 -m venv venv

Here venv is the name of the virtual environment. I am assuming here that you know about the virtual environment as this article is focussed more on deploying the application on Docker. If you want a refresher on that, here is a link.

Activate the virtual environment.

source venv/bin/activate

Install the necessary packages

pip3 install flask
pip3 install flask-sqlalchemy
pip3 install psycopg2

Lets create a simple flask application now with just one model.

model.py

import os
import sys
from sqlalchemy.exc import SQLAlchemyError
sys.path.append(os.getcwd() + '/..')
from Example.app import db


class User(db.Model):
    __table_args__ = {'extend_existing' : True}
    id = db.Column(db.Integer(), primary_key = True)
    user_name = db.Column(db.String(255))
    user_email = db.Column(db.String(255))
    status = db.Column(db.String(255))

app.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:pineapple123@localhost:5432/Example'
db = SQLAlchemy(app)
db.init_app(app)

if __name__ == '__main__':
    app.run()

This is where we have given the path to the postgres database server running on localhost, port 5432. 'Example' is the name of the database you want to migrate your models to.

manage.py

import sys
import os
sys.path.append(os.getcwd() + '/..')

from app import db
from Example.User.model import *

db.create_all()
db.session.commit()

Now that we have created the application code, lets migrate the model and run the flask application server.

python3 manage.py
python3 app.py

We have successfully run the application on our local systems. We can see that we can reach the server at the address http://127.0.0.1:5000/ . Now we have to understand what we did here. Its very essential for serving the same app on Docker.

We are right now interacting with a single operating system. That is, the operating system installed on our own machine. You can view a single Docker container as an operating system in itself. So how can two operating systems installed on different machines talk to each other ? They need to know each other's address. For that purpose, we have the IP Address. To identify a particular application on that OS, we need to know the port it is listening to. So IP + Port Number is all that we need to talk to a application running somewhere on the network. I am telling all this because each Docker container is an OS for the application running on it. Even if the containers are created by the same Docker engine, they communicate with each other using IP and Port number only.

Something about Dockerfile, Images and Containers.

Since this article is not about Docker, I will explain the container set-up process briefly.

First we create a Dockerfile using which image is created. Now this image can be used to pop up different containers which are virtually the same but different identities in themselves. Each can be used to host an application.

This is a very vague description of the Docker stuff. Will be writing all that in another blog.

Architecture of the application.

One more thing. Containers are created and destroyed on the fly. Any thing we save in a container will be wiped off completely after it is destroyed. So we need a mechanism for storing the data permanently. Persistent storage in another words.

As you can see in the above diagram that the Docker is installed on the virtual machine on the host OS. Now this virtual machine is running the Docker engine. So in this example, we will create two containers. One for flask and the other one for Postgres. We will also create a volume for persistent data storage.

Creating the Dockerfile.

Since the Dockerfile is the recipe of creating the image, we need to pass it to Docker engine. Create a file named Dockerfile in the same folder 'Flask_docker'.

FROM ubuntu:18.10

RUN apt-get update
RUN apt-get install -y python3 python3-dev python3-pip
RUN apt-get install -y libffi6
RUN apt-get install -y libffi-dev
RUN apt-get install -y build-essential
RUN pip3 install flask
RUN pip3 install flask-sqlalchemy
RUN pip3 install psycopg2

WORKDIR .

COPY . .

EXPOSE 5000

ENTRYPOINT ["python3", "./Example/app.py"]

There is a slight change in the app.py file.

app.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:pineapple123@localhost:15432/Example'
db = SQLAlchemy(app)
db.init_app(app)

if __name__ == '__main__':
    app.run()

Creating the Flask image

docker image build -t flask .

This will create a image by the name flask that can be used to create containers.

Pull the postgres image.

docker pull postgres

Create a Docker volume

Now that we have both the images locally on our Docker, we can start making containers out of them. But before we do that, lets create a volume.

docker volume create postgresdata

Setting up the application

Now that we have everything ready, lets start the containers for hosting our application.

docker container run --net=host -p 5000:5000 flask

Here --net=host flag sets all the requests coming from localhost to this container. Port 5000 on localhost will be mapped to port 5000 inside the container.

docker container run --name postgres_server -v postgresdata:/var/lib/postgresql/data -p 15432:5432 postgres

This Docker command will start the container from the postgres image. It mounts the volume we created 'postgresdata' to the default location where the postgres stores the data.

At this point of time you have both the containers running. Lets list them by the command -

docker container ls

You have a flask application setup to use postgres database. Also the data in the database is persistent and will not be wiped off with the container. How can we be sure that they both are connected to each other ? Lets see that next.

Checking the connection between the containers.

Open two new tabs on terminal because going to work inside the container itself.

There are two things that we are going to do. First we are going to create the 'Example' database on the postgres container. Second, we are going to migrate the ORM models that we created in our flask application to this database.

Create the database.

docker exec -it 'container-id-of-postgres-container' bash

Substitute your postgres container id above.

su - postgres
psql
create database "Example";

The database is ready but it is still not having any tables. That will be our next step. On a new terminal tab -

docker exec -it 'container-id-of-flask-container' bash
cd Example
python3 manage.py

Now there should be a table in the 'Example' database that we just saw. Connect to that database and check the columns in the 'user' table.

That's it for this article.