DEV Community

abbazs
abbazs

Posted on • Edited on

Developing a FastAPI Application in a Docker Container

In this tutorial, we will learn how to develop a Python FastAPI application in a Docker container. We will also use docker-compose to orchestrate the containerization of the application.

Prerequisites

Before we start, make sure you have the following installed on your machine:

  • Docker
  • docker-compose
  • Poetry
  • Python 3.11.2

Follow this tutorial to install prerequisites.

  1. Setting up the Project

Let's start by creating a new project directory and navigating to it:

   mkdir fastapi-docker
   cd fastapi-docker
Enter fullscreen mode Exit fullscreen mode
  1. Set the virtual env to be created inside the project directory
   poetry config virtualenvs.in-project true
Enter fullscreen mode Exit fullscreen mode
  1. Initialize the project using poetry and follow the instructions
   poetry init
Enter fullscreen mode Exit fullscreen mode

At the end of this step, you should have a pyproject.toml file that looks like this:

   [tool.poetry]
   name = "fastapi-docker-tutorial"
   version = "0.1.0"
   description = ""
   authors = ["Your Name <your@email.com>"]
   readme = "README.md"

   [tool.poetry.dependencies]
   python = "^3.11.2"
   fastapi = "^0.95.1"
   uvicorn = "^0.21.1"
   Jinja2 = "^3.1.2"

   [tool.poetry.group.dev.dependencies]
   black = "^23.3.0"
   mypy = "^1.2.0"

   [build-system]
   requires = ["poetry-core"]
   build-backend = "poetry.core.masonry.api"
Enter fullscreen mode Exit fullscreen mode
  1. Create a directory called app
   mkdir app
   cd app
Enter fullscreen mode Exit fullscreen mode
  1. Create a new file called app.py inside the app directory. This file will contain the FastAPI application code.
   from fastapi import FastAPI, Request
   from fastapi.responses import HTMLResponse
   from fastapi.templating import Jinja2Templates

   app = FastAPI()
   templates = Jinja2Templates(directory="templates")

   @app.get("/", response_class=HTMLResponse)
   async def read_root(request: Request):
       return templates.TemplateResponse("index.html", {"request": request})
Enter fullscreen mode Exit fullscreen mode
  1. Create a new directory called templates in the same directory as your app.py file and add a new file called index.html. Add the following code to the file:
   <!DOCTYPE html>
   <html>
     <head>
       <title>FastAPI Docker Tutorial</title>
     </head>
     <body>
       <h1>FastAPI Docker Tutorial</h1>
       <p>Welcome to the FastAPI Docker Tutorial!</p>
     </body>
   </html>
Enter fullscreen mode Exit fullscreen mode
  1. Install the dependencies for the project:
   poetry install
Enter fullscreen mode Exit fullscreen mode

This will install all the required dependencies for the project inside a virtual environment managed by Poetry.

  1. Create the Dockerfile

Now that we have the project set up with its dependencies, create a Dockerfile to containerize the application.

Create a new file called Dockerfile in the project directory with the following content:

   FROM python:3.11.2-buster
   ENV DEBIAN_FRONTEND='noninteractive'
   RUN apt-get update && apt install -y curl
   RUN curl -sSL https://install.python-poetry.org | python
   ENV PATH="${PATH}:/root/.local/bin"
   RUN poetry config virtualenvs.in-project true
   WORKDIR /app
   COPY . .
   RUN poetry install
   EXPOSE 8000
Enter fullscreen mode Exit fullscreen mode

This Dockerfile is based on the official Python 3.11.2 image and installs Poetry inside the container. It also sets the working directory to /app and copies the project files into the container. Lastly, it installs the project dependencies with Poetry and exposes port 8000, which is the port our FastAPI application will listen on.

  1. Create the docker-compose.yml file

In this step, we will create a docker-compose.yml file to define and run our container. docker-compose is a tool that allows us to define and run multi-container Docker applications. We will use it to define our tutorial service.

Create a new file called docker-compose.yml in the project directory with the following content:

   version: "3.8"
   services:
     tutorial:
       build:
         context: .
         dockerfile: Dockerfile
       ports:
         - "8000:8000"
       volumes:
         - .:/app
         - /app/.venv
       command:
         [
           "poetry",
           "run",
           "uvicorn",
           "--host",
           "0.0.0.0",
           "--port",
           "8000",
           "app.app:app",
           "--reload",
         ]
Enter fullscreen mode Exit fullscreen mode

This docker-compose.yml file defines a service called tutorial. It uses the build keyword to specify that the Dockerfile for this service is located in the current directory (.) and has a name Dockerfile. And it will be exposed on port 8000 of the host machine (ports: - "8000:8000").

Two volumes are mounted to the container, the current working directory (.) is mapped to /app directory in the container, and the .venv directory located in /app is mounted to an anonymous volume. This allows us to persist the virtual environment even after the container is deleted, but also allows us to access the project directory from within the container.

We are maintaining the local .venv because the local .venv is required for VSCode to provide syntax highlighting and code type checking based on the installed packages in the virtual environment. Additionally, if you have type annotations in your code, tools like mypy can use the virtual environment to perform static type checking. So maintaining the local .venv in sync with the container's virtual environment allows for better development experience and code quality.

The command section specifies the command to be run when the container starts. Here, we are using Poetry to run the uvicorn server, which is the ASGI server that we will use to serve our FastAPI app. We specify the host and port number for the server, as well as the module and app that should be run. The --reload option enables live reloading of the server, so that any changes made to the code will automatically restart the server.

  1. Start the container

With the Dockerfile and docker-compose.yml file in place, we can start our container using the following command:

   docker-compose up --build
Enter fullscreen mode Exit fullscreen mode

This command will build and start our tutorial service in a container. The --build option is used to build the container before starting it. Once the container is running, you should see output similar to the following:

   tutorial_1  | INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
   tutorial_1  | INFO:     Started reloader process [1] using watchgod
   tutorial_1  | INFO:     Started server process [3]
   tutorial_1  | INFO:     Waiting for application startup.
   tutorial_1  | INFO:     Application startup complete.
Enter fullscreen mode Exit fullscreen mode
  1. Test the application

With the container running, you can test the application by opening a web browser and navigating to http://localhost:8000. You should see the following output:

   FASTAPI Docker Tutorial

   Welcome to the FASTAPI Docker Tutorial!
Enter fullscreen mode Exit fullscreen mode

Congratulations! You have successfully containerized a Python FastAPI application development using Docker and docker-compose.

  1. Stop the container

To stop the container, use the following command in the same terminal session where you ran docker-compose up:

   docker-compose down
Enter fullscreen mode Exit fullscreen mode

This command will stop and remove the containers and networks created by docker-compose up.

Top comments (1)

Collapse
 
oakveda profile image
oakveda

adding --host solves issue at my end. Thanks.