Intro
Welcome to part 2 of the mini series about running a webservice.
In part 1, we learned how to use the FastAPI framework to quickly and easily write an application to serve a HTTP REST API in python.
In this article, we will learn how to create a Docker container for our app, which will make it much easier to deploy later on.
Overview
Docker, or container technology in general, allows us to bundle all required dependencies with our app.
This has huge benefits regarding the actual operation of the application: We never have to worry if the modules, libraries etc. we need are installed in the correct version on the machine that ends up running our code. You can think of containers as a sort of very lightweight VM, which contains everyting we need to run our app.
For a more detailed intro to Docker and containers, check the Complete Intro to Containers .
Project Structure
Let’s modify our project structure a little bit to keep everything organized neatly:
tree
.
├── .venv
├── ip_calc
│ └── main.py
├── README.md
└── requirements.txt
The python file main.py was moved into a directory with the projects name, to keep the code separate from the configuration files we will need in the future.
The file README.md contains a little description of the project written in Markdown.
The Dockerfile
To build our own container, we need a way to tell Docker how exactly it should create the container.
The Dockerfile (See DOCS) does exactly that - it is simply a text file containing a list of instructions for Docker to execute when building the container image.
Let’s have a look at our Dockerfile, which has comments to describe what every command does:
# Use python3.8.10 on alpine (from dockerhub) as the base image
FROM python:3.8.10-alpine
# Copy the contents of 'ip_calc'into the container, into directory '/app'
COPY ./ip_calc /app
# Copy the requirement.txt into the container
COPY ./requirements.txt /app/requirements.txt
# Change the containers working directory (like 'cd /app')
WORKDIR /app
# Upgrade pip itself
RUN pip install --upgrade pip
# Install the modules needed to run the app - defined in requirements.txt
RUN pip install -r requirements.txt
# Expose the containers TCP port 8000 so the webserver can accept incoming connections
EXPOSE 8000
# Start the uvicorn webserver listening on all interfaces on TCP port 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
FROM pulls the python3.8.10 docker base image running on alpine linux from the default docker container registry (dockerhub).
Then we COPY our code into the container, change the WORKDIR and RUN commands to install all required modules.
Since our container will be running a webserver, we have to EXPOSE a port to the outside, on which the webserver can listen for incoming connections.
Finally, CMD defines the command (+ arguments) that will be executed when the container is started.
Building our container image
Now that we have a Dockerfile, we can go ahead and build our container image:
sudo docker build -t ip_calc .
The -t ip_calc tells docker to “tag” the container image it is about to build with the name ip_calc (This makes it easier to reference container images, because we can define human-readable names instead of the hash values automatically assigned by docker).
The dot at the end tells it to look for the Dockerfile in the current working directory.
There will be quite a bit output from docker and pip, but the last line should read
Successfully tagged ip_calc:latest
which means that our container image has been built and can now be accessed under the name ip_calc.
To start the container, execute the following command (you can omit the :latest part of the tag, it is used by default):
sudo docker run -p 8000:8000 ip_calc
You should see some output similar to before when we were running the app un-containerized:
INFO: Started server process [1]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
Tadaa, we are now running our app in a docker container!

Wrapping up
In this article, we learned how we can easily containerize our application with docker with just a few instruction in a Dockerfile.
Our project structure has changed a little bit (we moved the application code into a separate directory and created the Dockerfile):
tree
.
├── .venv
├── Dockerfile
├── ip_calc
│ └── main.py
├── README.md
└── requirements.txt
In the upcoming articles we will learn how to use git & GitLab, how to push our container image to a container registry, how to deploy our container to a live server and some more cool stuff.
See you around!