Ultimate Docker and FastAPI Guide
BrianKibe
Posted on November 15, 2021
Ultimate Docker and FastAPI Guide
When deploying FastAPI applications a common approach is to build a Linux container image. It's normally done using Docker
You can then deploy that container image in one of a few possible ways.
Using Linux containers has several advantages including security, replicability, simplicity, and others.
What is a Container
Containers (mainly Linux containers) are a very lightweight way to package applications including all their dependencies and necessary files while keeping them isolated from other containers (other applications or components) in the same system.
Linux containers run using the same Linux kernel of the host (machine, virtual machine, cloud server, etc). This just means that they are very lightweight (compared to full virtual machines emulating an entire operating system).
This way, containers consume little resources, an amount comparable to running the processes directly (a virtual machine would consume much more).
Containers also have their own isolated running processes (commonly just one process), file system, and network, simplifying deployment, security, development, etc.
What is a Container Image
A container is run from a container image.
A container image is a static version of all the files, environment variables, and the default command/program that should be present in a container. Static here means that the container image is not running, it's not being executed, it's only the packaged files and metadata.
In contrast to a "container image" that is the stored static contents, a "container" normally refers to the running instance, the thing that is being executed.
When the container is started and running (started from a container image) it could create or change files, environment variables, etc. Those changes will exist only in that container, but would not persist in the underlying container image (would not be saved to disk).
A container image is comparable to the program file and contents, e.g. python and some file main.py.
And the container itself (in contrast to the container image) is the actual running instance of the image, comparable to a process. In fact, a container is running only when it has a process running (and normally it's only a single process). The container stops when there's no process running in it.
Container Images
Docker has been one of the main tools to create and manage container images and containers.
And there's a public Docker Hub with pre-made official container images for many tools, environments, databases, and applications.
For example, there's an official Python Image.
And there are many other images for different things like databases, for example for:
• PostgreSQL
• MySQL
• MongoDB
• Redis, etc.
By using a pre-made container image it's very easy to combine and use different tools. For example, to try out a new database. In most cases, you can use the official images, and just configure them with environment variables.
That way, in many cases you can learn about containers and Docker and re-use that knowledge with many different tools and components.
So, you would run multiple containers with different things, like a database, a Python application, a web server with a React frontend application, and connect them together via their internal network.
All the container management systems (like Docker or Kubernetes) have these networking features integrated into them.
Containers and Processes
A container image normally includes in its metadata the default program or command that should be run when the container is started and the parameters to be passed to that program. Very similar to what would be if it was in the command line.
When a container is started, it will run that command/program (although you can override it and make it run a different command/program).
A container is running as long as the main process (command or program) is running.
A container normally has a single process, but it's also possible to start subprocesses from the main process, and that way you will have multiple processes in the same container.
But it's not possible to have a running container without at least one running process. If the main process stops, the container stops.
Build a Docker Image for FastAPI
Okay, let's build something now! 🚀
I'll show you how to build a Docker image for FastAPI from scratch, based on the official Python image.
This is what you would want to do in most cases, for example:
• Using Kubernetes or similar tools
• When running on a Raspberry Pi
• Using a cloud service that would run a container image for you, etc.
Package Requirements
You would normally have the package requirements for your application in some file.
It would depend mainly on the tool you use to install those requirements.
The most common way to do it is to have a file requirements.txt with the package names and their versions, one per line.
You would of course use the same ideas you read in About FastAPI versions to set the ranges of versions.
Docker and other tools build these container images incrementally, adding one layer on top of the other, starting from the top of the Dockerfile and adding any files created by each of the instructions of the Dockerfile.
Docker and similar tools also use an internal cache when building the image, if a file hasn't changed since the last time building the container image, then it will re-use the same layer created the last time, instead of copying the file again and creating a new layer from scratch.
Just avoiding the copy of files doesn't necessarily improve things too much, but because it used the cache for that step, it can use the cache for the next step. For example, it could use the cache for the instruction that installs dependencies with:
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
Deployment Concepts
Let's talk again about some of the same Deployment Concepts in terms of containers.
Containers are mainly a tool to simplify the process of building and deploying an application, but they don't enforce a particular approach to handle these deployment concepts, and there are several possible strategies.
The good news is that with each different strategy there's a way to cover all of the deployment concepts. 🎉
Let's review these deployment concepts in terms of containers:
• HTTPS
• Running on startup
• Restarts
• Replication (the number of processes running)
• Memory
• Previous steps before starting
HTTPS
If we focus just on the container image for a FastAPI application (and later the running container), HTTPS normally would be handled externally by another tool.
It could be another container, for example with Traefik, handling HTTPS and automatic acquisition of certificates.
Tip
Traefik has integrations with Docker, Kubernetes, and others, so it's very easy to set up and configure HTTPS for your containers with it.
Alternatively, HTTPS could be handled by a cloud provider as one of their services (while still running the application in a container).
Running on Startup and Restarts
There is normally another tool in charge of starting and running your container.
It could be Docker directly, Docker Compose, Kubernetes, a cloud service, etc.
In most (or all) cases, there's a simple option to enable running the container on startup and enabling restarts on failures. For example, in Docker, it's the command line option --restart.
Without using containers, making applications run on startup and with restarts can be cumbersome and difficult. But when working with containers in most cases that functionality is included by default. ✨
Replication - Number of Processes
If you have a cluster of machines with Kubernetes, Docker Swarm Mode, Nomad, or another similar complex system to manage distributed containers on multiple machines, then you will probably want to handle replication at the cluster level instead of using a process manager (like Gunicorn with workers) in each container.
One of those distributed container management systems like Kubernetes normally has some integrated way of handling replication of containers while still supporting load balancing for the incoming requests. All at the cluster level.
In those cases, you would probably want to build a Docker image from scratch as explained above, installing your dependencies, and running a single Uvicorn process instead of running something like Gunicorn with Uvicorn workers.
Load Balancer¶
When using containers, you would normally have some component listening on the main port. It could possibly be another container that is also a TLS Termination Proxy to handle HTTPS or some similar tool.
As this component would take the load of requests and distribute that among the workers in a (hopefully) balanced way, it is also commonly called a Load Balancer.
Posted on November 15, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.