In this blog, we are going to containerize the application we created in "Building URL Shortener API Using Flask + MySQL" using docker-compose. First, we will learn What is Docker? Why do we need to use it?
What is Docker?
To understand what is Docker we need to know why we use Virtual Machines and what are containers?
VMs are basically used to operate multiple operating systems on the same piece of hardware at the same time. Without virtualization, you would need several separate physical units of computers to run several operating systems. It is essential to use different operating systems because several services within an application may require a specific OS to run.
VMs work great, they do exactly what they are supposed to do, But there is one problem. VMs can take up a lot of system resources because each VM runs not just a full copy of an operating system, but a virtual copy of all the hardware that the operating system needs to run. This quickly adds up to a lot of RAM and CPU cycles. That’s still economical compared to running separate actual computers, but for some applications, it can be overkill. This is where containers come into play.
Containers are built on top of the host Operating Systems (Typically Windows or Linux) and they share binary files, core libraries and the OS kernel with each other and the host. Sharing of the resources reduces the need to reproduce the operating system code and means that a server can run multiple workloads with a single operating system installation. Containers are thus exceptionally light. They are only megabytes in size and take just seconds to start. Compared to containers, VMs take minutes to run and are significantly larger than an equivalent container.
Docker is a Linux utility that can efficiently create, ship, and run containers. Now, let's look at what is docker-compose. According to the official website of docker:
Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration
In a nutshell, Compose is a tool which can be used to deploy multiple containers at the same time with a single command.
Dockerizing Application
Now that you are all caught up let's dockerize our Flask+MySQL URL Shortener API that we built in the previous blog. If you haven't already installed them yet, download and install Docker and docker-compose
We are going to be needing two containers, one to run our Flask application and another one for our MySQL server and database. It is considered best practice to have only one service per container for various reasons like scalability and crash handling.
Project Layout
Let's have a look at the files we are going to be creating to containerize our application.
.
├── app
│ ├── Dockerfile
│ ├── application.py
│ └── requirements.txt
├── database
└── docker-compose.yml
Docker requires you to store all the instructions to run an application inside a file named Dockerfile
which is what we have in app/
directory. Now we don't need to run any applications for the MySQL container, We only need to start the server and store the database which can be achieved directly through docker-compose.yml
and that's why you don't see a Dockerfile
in database/
.
Containerizing Flask Application
We will add instructions to run our Flask application in the Dockerfile. To install all the dependencies we are going to add all our python libraries to a file named requirements.txt so we can install them using a single command.
FROM python:latest
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["application.py"]
Instructions provided to the container to run our application:
- First we are going to install the latest version of Python.
- We are going to copy content from
/app
directory where ourapplication.py
is stored. - Switch to
/app
directory - Install libraries from requirements.txt
- Start python CLI
- Run application.py
That should run our flask application.
Creating Compose file
As I mentioned above docker-compose is used to deploy multiple containers at once which saves us a lot of work and time. In order to use it, we need to create a file called docker-compose.yml where we will add instructions on how to create each container.
version: '3'
services:
flask-app:
container_name: flask-app
build: app
restart: always
ports:
- '5000:5000'
volumes:
- ./app:/app
networks:
- backend
mysql-db:
container_name: mysql-db
image: mysql:5.7
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: urlshortener
ports:
- '3308:3306'
volumes:
- ./database:/var/lib/mysql
networks:
- backend
networks:
backend:
volumes:
app:
database:
A couple of things to notice here, services is where we define our containers. First one is flask-app. We are directly importing the Dockerfile we created in the app directory using the command build: app
(Docker expects a "Dockerfile" in the specified directory) so we don't need to add any additional commands here. However, we are going to map a few things with our host system like port, we will map containers 5000 port to ours because flask runs on 5000, we will also map our app directory with containers. Here we are also creating a network called backend through which all the services will be running so we can cross-reference each service in another.
For MySQL container since we did not create any Dockerfile, we will need to install the service manually that's why you see the line image: mysql:5.7
which install MySQL 5.7 server on the container. Here we are also adding some environment variables for MySQL password and name of the database that we are going to be using.
In the end, we will define the directories we used and as well as networks.
Now let's run it. Before you can actually run it you need to build them using
docker-compose build --no-cache
If you are running for the first time you don't need --no-cache. Now, let us run it
docker-compose up -d
That should run the containers successfully, here -d is used to "detach" which basically lets you exit the logs after successfully deploying the containers.
We are not done yet! The MySQL server you started is quite fresh and doesn't have any tables yet. So we are going to access the bash terminal of MySQL containers, run mysql
command and create our schema. To log in to bash terminal of the MySQL container use:
docker exec -it mysql-db bash
This command basically tells docker to log in to the interactable terminal (-it) of container mysql-db
with bash
Now that you are in the container's bash terminal, let's start the MySQL CLI using
mysql -uroot -proot
Here my user is root by default and password we defined in docker-compose. Now, you should be able to access the databases. You can test it using
SHOW DATABASES;
Now you can go ahead and finish building the schema that you want.
at this point, you should be able to access your API successfully on http://localhost:5000
. You can test it out. Now to stop the containers you can use
docker-compose down
Read the official docs to see what else you can do with docker-compose. That's it for this blog, If you have any questions ask them in the comment section.
Conclusion
In this blog, you learned about VMs, Containers and what exactly are docker and docker-compose and how to containerize a Flask API which uses MySQL in multiple containers using docker-compose. All the code used above is available at:
For the upcoming blogs, I will be showing how to implement message queues and caching layer using Redis in Python-Flask and how to deploy a separate container for it using compose.
Subscribe to my newsletter if you are interested in learning more about DevOps, Frontend Development, Backend Development, Machine Learning and as well as Freelancing. Follow me on Twitter @abbasmdj.