Introduction
In this blog post, we will walk through the process of deploying a FastAPI application on an AWS EC2 instance with Nginx as a reverse proxy, while leveraging GitHub Actions for Continuous Integration and Continuous Deployment (CI/CD). This will ensure that every change pushed to the main
branch is automatically deployed to the server.
By the end of this tutorial, you will have a fully functional, continuously deployed FastAPI application running on AWS.
Project Overview
We will be working with a FastAPI Book API and following these key steps:
- Implement the missing
GET /api/v1/books/{book_id}
endpoint. - Test the application locally.
- Dockerize the application.
- Deploy it on an AWS EC2 instance.
- Set up Nginx as a reverse proxy.
- Configure GitHub Actions to automate deployment.
Step 1: Implement the Missing Endpoint
The application already provides endpoints for creating, updating, and deleting books, but we need to add an endpoint to retrieve a book by its ID.
Modify api/routes/books.py
by adding the missing endpoint:
from typing import OrderedDict
from typing import Dict
from fastapi import APIRouter, status, HTTPException
from fastapi.responses import JSONResponse
from api.db.schemas import Book, Genre, InMemoryDB
router = APIRouter()
db = InMemoryDB()
db.books = {
1: Book(
id=1,
title="The Hobbit",
author="J.R.R. Tolkien",
publication_year=1937,
genre=Genre.SCI_FI,
),
2: Book(
id=2,
title="The Lord of the Rings",
author="J.R.R. Tolkien",
publication_year=1954,
genre=Genre.FANTASY,
),
3: Book(
id=3,
title="The Return of the King",
author="J.R.R. Tolkien",
publication_year=1955,
genre=Genre.FANTASY,
),
}
@router.post("/", status_code=status.HTTP_201_CREATED)
async def create_book(book: Book):
db.add_book(book)
return JSONResponse(
status_code=status.HTTP_201_CREATED, content=book.model_dump()
)
@router.get("/", response_model=OrderedDict[int, Book], status_code=status.HTTP_200_OK)
async def get_books() -> OrderedDict[int, Book]:
return db.get_books()
@router.put("/{book_id}", response_model=Book, status_code=status.HTTP_200_OK)
async def update_book(book_id: int, book: Book) -> Book:
return JSONResponse(
status_code=status.HTTP_200_OK,
content=db.update_book(book_id, book).model_dump(),
)
@router.delete("/{book_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_book(book_id: int) -> None:
db.delete_book(book_id)
return JSONResponse(status_code=status.HTTP_204_NO_CONTENT, content=None)
# Added the missing api endpoint
@router.get("/{book_id}", response_model=Book, status_code=status.HTTP_200_OK)
async def get_book(book_id: int):
book=db.books.get(book_id)
if not book:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Book not found")
return book
This ensures that a book is retrieved by its ID or returns a 404 Not Found
error if it doesn't exist.
Step 2: Test the Application Locally
Before deploying, test the API locally using pytest:
pytest
To run the FastAPI application locally:
uvicorn api.main:app --host 0.0.0.0 --port 8084 --reload
Test the new endpoint with:
curl -X 'GET' 'http://127.0.0.1:8084/api/v1/books/1' -H 'accept: application/json'
Step 3: Dockerizing the Application
Create a Dockerfile
in the root directory:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8084"]
Build and run the Docker container:
docker build -t fastapi-app .
docker run -d -p 8084:8084 --name fastapi-container fastapi-app
Step 4: Deploy to AWS EC2
1. Launch an EC2 Instance
- Choose Ubuntu 22.04.
- Configure security groups to allow SSH (22), HTTP (80), and Custom TCP (8084).
2. SSH into the Instance
ssh -i your-key.pem ubuntu@your-ec2-public-ip
3. Install Docker and Git
sudo apt update && sudo apt install -y docker.io git
4. Clone the Repository and Run the Application
git clone https://github.com/yourusername/fastapi-book-project.git
cd fastapi-book-project
docker build -t fastapi-app .
docker run -d -p 8084:8084 --name fastapi-container fastapi-app
Test the application from your browser: http://your-ec2-public-ip:8084/api/v1/books/1
Step 5: Set Up Nginx as a Reverse Proxy
Install Nginx:
sudo apt install -y nginx
Modify the Nginx configuration file /etc/nginx/sites-available/default
:
server {
listen 80;
server_name your-ec2-public-ip;
location / {
proxy_pass http://127.0.0.1:8084/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Restart Nginx:
sudo systemctl restart nginx
Now, your FastAPI application is accessible at http://your-ec2-public-ip/
.
Step 6: Automate Deployment with GitHub Actions
Create .github/workflows/deploy.yml
:
name: Deploy FastAPI to EC2
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Deploy to EC2
uses: appleboy/ssh-action@v0.1.6
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd ~/fastapi-book-project
git pull origin main
docker build -t fastapi-app .
docker stop fastapi-container || true
docker rm fastapi-container || true
docker run -d -p 8084:8084 --name fastapi-container fastapi-app
sudo systemctl restart nginx
Set Up GitHub Secrets
In your GitHub repository:
- Navigate to Settings > Secrets > Actions.
- Add EC2_HOST (EC2 public IP) and EC2_SSH_KEY (your private key).
Trigger Deployment
Run:
git add .
git commit -m "Test auto-deploy"
git push origin main
On a successful push to main
, GitHub Actions will automatically deploy the app!
Challenges and Resolutions
1. Nginx Not Proxying Requests
Check Nginx logs:
sudo journalctl -u nginx --no-pager | tail -n 20
2. Docker Container Fails to Start
Verify logs:
docker logs fastapi-container
3. GitHub Actions SSH Key Issues
Ensure EC2_SSH_KEY
secret is correctly set in GitHub.
Further Enhancements
- Use AWS ECS instead of EC2 for better scalability.
- Set up HTTPS with Let's Encrypt.
- Store secrets securely using AWS Secrets Manager.
With this setup, you have a robust CI/CD pipeline that ensures seamless deployment of your FastAPI application. 🚀
Top comments (0)