How to containerize a Flask Python application using Docker
Published: 2024-04-23 22:28:44

Before [Docker](https://docs.docker.com/), developers, and operations engineers had a huge setback when deploying an application to the servers because dependencies were to be installed on every machine to run the desired application. This approach created a lot of errors and misunderstandings.

Docker came in to solve this problem by containerizing the application to a single image which incorporates all its dependencies and configurations thus reducing errors and the amount of work and expertise to install on different machines. Images can run smoothly regardless of your operating system, it is lightweight since it can be scaled up or down and containers can be easily managed and automated.

##Pre-requisites 

Docker Installed and running.
Basic understanding of Python and Docker concepts
Visual Studio Code
Bash terminal

##Setting up the Python application

In this project, you will design a Python application using the Flask framework to handle HTTP requests. It’s a framework used to create web applications and has built-in features in development servers.

###Project Files

In this tutorial, I will show and guide you on how to create a simple Flask app that will include the following:

- A basic structure for the Flask application
- A route that handles GET requests and returns a simple response.
- Running the Python Flask app on the terminal by using Python commands.

Follow these steps to design the application.

###Set up an ideal environment

Make sure you have the latest Python and Flask installed.
Create a new directory for your Flask app.
Navigate to that directory in your terminal.

Create and activate a virtual environment

```
python -m venv venv
```


On windows, use venv\Scripts\activate

```
source venv/bin/activate
```


###Install Flask

Run this command in your terminal

```
pip install Flask
```


###Create a Flask app

In your project directory, create a new file and call it `web.py`
In `web.py`, `import Flask` and create an instance of the Flask application.
Define a route that listens to GET requests on the root URL ‘/’ and returns a simple message.
	
Your code should look like this;

```
from flask import Flask

#Create an instance of the Flask application
web = Flask(__name__)

#Define a route for the root URL ("/")
@web.route('/')

def index():
	return 'Hello DevOps Engineer, This is your Python Flask App. Congratulations!'

# Run the application
if __name__ = '__main__':`
	`web.run(host="0.0.0.0", port=int("3000"), debug=True)

```


In the above code snippet;

You import the Flask `class` and create an instance of the Flask application named `web`. 

The route decorator `@web.route(‘/’)` specifies that the function `index()` returns a simple message: `“Hello, DevOps Engineer. This is your Python Flask App. Congratulations!”` 

The `web.run()` method starts the application whereas `debug=True` enables debugging mode.

###Run the app

For the app to run, execute the following command in your terminal.

```
python web.py
```


On the terminal, you should see an output indicating that your Flask app is running and how to access it via this link [URL](http://127.0.0.1:3000/).

Navigate to your browser with the specified URL and see your Flask app in action.

##Dockerfile
A Dockerfile is a text document that encompasses all the commands a developer will call on the command line for it to build an image. It consists of the base image of your app, the directory of your app, the dependencies document, the environment that can also be specified, the port for your app, and relevant commands to run the image after building.

###How to create a Dockerfile.

Since the Server is running, stop the server by pressing `Ctrl + C` on your keyboard. 

Navigate to the home directory and create a Dockerfile by running the following command;

```
touch Dockerfile
```

After the successful creation of the Dockerfile, the next step is creating commands for them to build an image. 

###Creating Dockerfile Instructions for the Python application

The Dockerfile have the following commands:


```

# Use the official Python image as a base
FROM python

# Set the working directory in the container
WORKDIR /app

# Copy the requirements.txt file to the working directory
COPY . /app

# Install the required dependencies
RUN pip install -r requirements.txt

EXPOSE 3000

# Define the command to run the app and expose the port the app runs on
CMD python ./web.py
```


###Explanation of each Dockerfile instruction

```
FROM python
```

Use the official `Python base image`. Not specifying the Python image, it will automatically use the latest Python-based image.

```
WORKDIR /app
```

Sets the working directory to `/app`.

```
COPY . /app
```

This command copies all files from the current directory (Dockerfile location) to the `/app` directory in the container.

```
RUN pip install -r requirements.txt
```

This command will install the dependencies specified in the `requirements.txt` and ensure all required packages are installed.

```
EXPOSE 3000
```

This command exposes port `3000`

```
CMD python ./web.py
```

This command defines the command to run the `Flask app`.

Since there is no file named `requirements.txt`, it can be created easily by the command `pip freeze` which lists all the dependencies of the image. To store the dependencies on a file you have to specify the text file running this command on your terminal.

```
pip freeze > requirements.txt
```

##Building the Docker image

Now, after the `Dockerfile` is set and no typo errors, create an image by running this command on your terminal:

```
docker build -t <username:version> 
```

`docker build` is a docker command to build the image.

`-t` will tag the image.

`username` is your docker registry username plus your desired container name separated by a forward slash.

`version` can be expressed in different styles e.g. `v1.0.0, 0.0.1 RELEASE` among others.

Assume my command to build my container was this; 

```
docker build -t joshmurih/webapp:0.0 .
```

##Running the Container

After successfully creating a `Docker image`, it is necessary to run the container to check for errors and make sure that it works as expected so that the container can later be pushed to the `Docker hub` for deployment.

###Suitable commands to run the image

On the terminal run the following command:

```
docker container run -d -p 3000:3000 <username:version>
```

`docker container run`: This is a Docker command to run images.

`-d` means you run the image in “detach” mode. (You cannot be able to visit the site and test the docker image on your terminal).

`-p 3000:3000`: This command means that you are binding port 3000 to the docker default port.

`<username:version>` This will be the exact name you defined when creating the Docker image. 

###Testing the final containerized application

To verify the image is running in the background, run this command on your terminal.

```
docker container ls
```

It displays all the containers that are running. The current container is the first one. 

Open your browser and visit this `URL`: 

After successfully running the URL, you should see the data below otherwise repeat the process counter checking for minor typos and other errors.


##Docker Hub

Docker Hub is a software platform built for developers to find, use, and share containers from anywhere on the continent.

For your team to use the final image you have already created, you have to push the container to `Docker Hub` for deployment and they in return will `docker pull` the final containerized application to their operating system for further use. 

###Pushing the docker image for deployment

Since you created the `docker image` with the tag of your `Docker Hub username` there is no need to tag it again. 

Proceed to `docker login` and enter the required username and password when prompted. 

Once logged in you can push your `image` to `Docker Hub` using the following command:

```
docker push <username:version>
```

##Conclusion 

Containerized applications run on isolated environments thus making it easier to manage dependencies and avoid conflicts between applications. Its portability and lightweight nature make deployment and testing easier. Since the containers share the host operating system’s kernel, utilisation of hardware resources creates efficiency. Lastly, the containers can be started and stopped quickly. This speed enables the developers to run new updates hence boosting the market of new features.