Automating FastAPI Project Build with GitHub Actions and push to DockerHub
FastAPI has quickly become a popular choice for building web APIs due to its speed, simplicity, and auto-generated interactive documentation. When it comes to deploying your FastAPI project, integrating continuous integration and continuous deployment (CI/CD) practices can streamline the process and ensure smooth deployments. In this article, we’ll explore how to build a FastAPI project using GitHub Actions and push it to Docker Hub, taking advantage of automated testing and image building.
Prerequisites
Before we dive into the deployment process, let’s briefly touch on the technologies we’ll be using:
FastAPI: A modern, fast web framework for building APIs with Python. Its automatic OpenAPI and JSON Schema documentation generation makes API development a breeze.
GitHub Actions: A powerful CI/CD tool that allows you to automate tasks directly from your GitHub repository. We’ll leverage it to run tests and build Docker images automatically.
Docker Hub: A cloud-based registry service that allows you to store and manage your Docker images.
Sample FastAPI Project
To demonstrate the deployment process, we’ll use a sample FastAPI project called ob-sample-fast-api. This project is a simple FastAPI application with an /info
endpoint that provides basic information about the application.
Project Structure
The project repository has the following structure:
ob-sample-fast-api/
├── app/
│ ├── main.py
│ └── tests/
│ └── test_main.py
├── Dockerfile
├── requirements.txt
└── README.md
app/main.py
: The main FastAPI application file containing the/info
endpoint.tests/test_main.py
: Test file for the FastAPI application.Dockerfile
: Dockerfile for containerizing the FastAPI application.requirements.txt
: Contains all the python required packagesREADME.md
: Project documentation and instructions.
FastAPI Application
Let’s take a closer look at the main FastAPI application code. In app/main.py
, we have the following code snippet
from fastapi import FastAPI
app = FastAPI()
@app.get("/info")
def info():
return {"name": "ob-sample-fast-api", "version": "1.0.0"}
This code sets up a FastAPI application with a single route, /info
, which returns a JSON response containing project name and current version
Testing
FastAPI encourages writing tests for your application. The tests/test_main.py
file contains a simple test case for the /info
endpoint:
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_info():
response = client.get("/info")
assert response.status_code == 200
assert response.json() == {"name": "ob-sample-fast-api", "version": "1.0.0"}
This test uses FastAPI’s TestClient
to simulate requests and assert the expected responses.
Dockerfile
The Dockerfile
in the project directory is used to define how the FastAPI application should be packaged into a Docker image. Here's a simplified version of the Dockerfile:
# Use an official Python runtime as a parent image
FROM python:3.9-slim
# Set the working directory in the container
WORKDIR /app
# Copy the requirements file into the container at /app
COPY requirements.txt /app/
# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Copy the current directory contents into the container at /app
COPY . /app/
# Make port 80 available to the world outside this container
EXPOSE 80
# Define environment variable
ENV NAME ob-sample-fast-api-docker
# Set the maintainer label
LABEL maintainer="itskmyoo <itskmyoo@gmail.com>"
# Run main.py when the container launches
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]DockerfileCopy code
Here’s an explanation of each section of the Dockerfile:
# Use an official Python runtime as a parent image
FROM python:3.9-slim
This line specifies the base image for your Docker image. In this case, you’re using the official Python 3.9 slim image as the parent image.
# Set the working directory in the container
WORKDIR /app
This line sets the working directory inside the container to /app
. Subsequent commands will be executed in this directory.
# Copy the requirements file into the container at /app
COPY requirements.txt /app/
Here, you’re copying the requirements.txt
file from your local directory to the /app/
directory inside the container.
# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
This command installs the Python packages specified in requirements.txt
using the pip
package manager. The --no-cache-dir
flag prevents caching of the downloaded files, which can help reduce the image size.
# Copy the current directory contents into the container at /app
COPY . /app/
This line copies the entire content of your local directory into the /app/
directory inside the container. This includes your FastAPI application code, main.py
, and other files.
# Make port 80 available to the world outside this container
EXPOSE 80
Here, you’re exposing port 80 from the container. This allows communication to the application running inside the container over port 80.
# Set the maintainer label
LABEL maintainer="itskmyoo <itskmyoo@gmail.com>"
This label sets the maintainer information for the image. It’s good practice to include this information for reference.
# Run main.py when the container launches
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
Finally, this command specifies the default command to run when the container is launched. It uses uvicorn
(ASGI server) to run the main
FastAPI application (main:app
) on host 0.0.0.0
and port 80
.
You can use this Dockerfile to build a Docker image of your FastAPI application, which can then be deployed and run in a Docker container.
With this sample FastAPI project, we have a basic understanding of the application’s structure, code, and testing.
Setting Up GitHub Actions
- Create a Workflow: In your GitHub repository, navigate to the “Actions” tab and click on “New workflow.” Choose the “Set up a workflow yourself” option.
- Define the Workflow:
name: FastAPI Docker Build and Push
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
run: echo ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
- name: Build and push Docker image
run: |
docker buildx create --use
docker buildx build \
--file Dockerfile \
--tag <docker-hub-user-name>/<docker-hub-respository-name>:latest \
--push .
env:
DOCKER_CLI_EXPERIMENTAL: enabled
DOCKER_BUILDKIT: 1
Replace docker-hub-user-name and docker-hub-respository-name with your actual Docker Hub username and repository name.
Docker Hub Access Token
To securely authenticate with Docker Hub, create a Docker access token with read and write access in your Docker Hub account settings. Add this token as a secret named DOCKERHUB_ACCESS_TOKEN in your GitHub repository.
To do so you need to go settings > secrets and variables > actions and add repsitory secrets.
With your GitHub Actions workflow and Docker Hub access token set up, every time you push changes to the main
branch, GitHub Actions will automatically build your FastAPI project into a Docker image and push it to Docker Hub.
You can alter this behaviour about when to build your docker image, you can change these lines of code
on:
push:
branches:
- main
Which can be either when you create a release or any other custom logic.
Here is a screenshot after a successful build in Github Actions
And once done, you can go to you dockerhub account and see the latest tagged image.
Conclusion
By integrating GitHub Actions and Docker Hub, you’ve automated the build process for your FastAPI project. Now you can focus on developing your APIs while enjoying the benefits of continuous integration and deployment.
You can include test cases to block the build or run custom business logic to check the code quality using Github available resources.
Happy coding and deploying!