Deployer FastAPI sur Fly.io : Tutoriel approfondi pour les développeurs intermédiaires
Pourquoi Deployer FastAPI sur Fly.io ?
Contexte réel : pourquoi un dev a besoin de ca au quotidien
Deployer une application FastAPI sur Fly.io est essentiel pour rendre votre application accessible à travers le monde, sans avoir à gérer un serveur physique ou virtuel. Fly.io offre des instances très performantes et faibles en coût, permettant aux développeurs d'expérimenter avec leur code sans inconvénients financiers. Cela est particulièrement utile pour les startups et les petits projets qui nécessitent une solution de déploiement rapide et flexible.
Un cas d'usage concret en 2-3 phrases
Imaginez que vous développez une application de gestion de tâches simple, et que vous souhaitez la rendre accessible à vos collègues et clients. Deployer cette application sur Fly.io vous permettrait d'avoir un environnement de production dédié avec des performances optimales sans avoir à gérer les infrastructures physiques.
Prerequis
Liste à puces des connaissances nécessaires
- Connaissance approfondie de FastAPI
- Compréhension des concepts de base d'API REST
- Familiarité avec le terminal et la ligne de commande
- Compétences en gestion de paquets (pip)
Outils à installer (versions)
- Python 3.9 ou ultérieur
- Pip (Python Package Installer)
- Fly.io CLI (Command Line Interface)
Concepts fondamentaux
1. FastAPI
FastAPI est un framework web moderne pour construire API RESTful et GraphQL en utilisant Python 3.7+ avec des annotations de type.
Schema mental :
## Importer FastAPI et déclarer une application
from fastapi import FastAPI
app = FastAPI()
## Définir une route simple
@app.get("/")
def read_root():
return {"message": "Bienvenue sur mon API"}
2. Fly.io
Fly.io est une plateforme de déploiement d'applications serverless et microservices. Il permet de déployer des applications en un temps record, avec des instances auto-échelonnées.
Schema mental :
## Installer l'interface de ligne de commande Fly.io
brew install flyctl
## Connexion à votre compte Fly.io
flyctl auth login
3. Docker
FastAPI s'exécute généralement dans un environnement Docker pour une meilleure portabilité et gestion des dépendances.
Schema mental :
## Définir l'image de base
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
## Copier le fichier main.py dans le conteneur
COPY main.py /app/main.py
## Exposer le port 8000
EXPOSE 8000
## Lancer l'application FastAPI avec Uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Mise en pratique : projet fil rouge
Construis UN mini-projet complet et réaliste (ex: un gestionnaire de tâches)
Nous allons créer une API simple pour gérer des tâches. La structure du projet sera la suivante :
main.py: Le fichier principal de l'application FastAPI.Dockerfile: Le fichier Docker pour construire l'image de notre application.
Étape 1 : Créer le fichier main.py
## main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
## Modèle Pydantic pour les tâches
class Task(BaseModel):
id: int
title: str
description: str
## Base de données in-memory pour stocker les tâches
tasks_db = []
@app.post("/tasks/", response_model=Task)
def create_task(task: Task):
tasks_db.append(task)
return task
@app.get("/tasks/{task_id}", response_model=Task)
def read_task(task_id: int):
for task in tasks_db:
if task.id == task_id:
return task
raise HTTPException(status_code=404, detail="Tâche non trouvée")
@app.put("/tasks/{task_id}", response_model=Task)
def update_task(task_id: int, updated_task: Task):
for i, task in enumerate(tasks_db):
if task.id == task_id:
tasks_db[i] = updated_task
return updated_task
raise HTTPException(status_code=404, detail="Tâche non trouvée")
@app.delete("/tasks/{task_id}")
def delete_task(task_id: int):
for i, task in enumerate(tasks_db):
if task.id == task_id:
del tasks_db[i]
return {"message": "Tâche supprimée"}
raise HTTPException(status_code=404, detail="Tâche non trouvée")
Étape 2 : Créer le fichier Dockerfile
## Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
COPY main.py /app/main.py
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Étape 3 : Construire et exécuter l'application localement
## Build the Docker image
docker build -t my-fastapi-app .
## Run the Docker container
docker run -d -p 8000:8000 my-fastapi-app
Étape 4 : Tester les routes avec curl
## Create a new task
curl -X POST "http://localhost:8000/tasks/" -H "Content-Type: application/json" -d '{"id": 1, "title": "Faire du code", "description": "Terminer ce tutoriel"}'
## Get a specific task
curl "http://localhost:8000/tasks/1"
## Update a task
curl -X PUT "http://localhost:8000/tasks/1" -H "Content-Type: application/json" -d '{"id": 1, "title": "Faire du code", "description": "Terminer ce tutoriel avec succès"}'
## Delete a task
curl -X DELETE "http://localhost:8000/tasks/1"
Erreurs fréquentes et debugging
1. Erreur : ModuleNotFoundError: No module named 'uvicorn'
Code incorrect :
pip install uvicorn
Code correct :
pip install fastapi uvicorn
2. Erreur : HTTPException: HTTP status code 404 not found for path '/tasks/1'
Code incorrect :
@app.get("/tasks/{task_id}", response_model=Task)
def read_task(task_id: int):
task = next((t for t in tasks_db if t.id == task_id), None)
if task is None:
raise HTTPException(status_code=404, detail="Tâche non trouvée")
return task
Code correct :
@app.get("/tasks/{task_id}", response_model=Task)
def read_task(task_id: int):
for task in tasks_db:
if task.id == task_id:
return task
raise HTTPException(status_code=404, detail="Tâche non trouvée")
3. Erreur : RuntimeError: Event loop is closed
Code incorrect :
import asyncio
async def main():
await asyncio.sleep(1)
if __name__ == "__main__":
asyncio.run(main())
Code correct :
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "Bienvenue sur mon API"}
Pour aller plus loin
1. Ajouter des tests unitaires
Utilisez pytest pour ajouter des tests unitaires à votre application.
pip install pytest
Créez un fichier test_main.py :
## test_main.py
from main import app, tasks_db
def test_create_task():
response = app.post("/tasks/", json={"id": 1, "title": "Faire du code", "description": "Terminer ce tutoriel"})
assert response.status_code == 200
assert response.json() == {"id": 1, "title": "Faire du code", "description": "Terminer ce tutoriel"}
def test_read_task():
response = app.post("/tasks/", json={"id": 2, "title": "Tester FastAPI", "description": "Écrire des tests"})
response = app.get("/tasks/2")
assert response.status_code == 200
assert response.json() == {"id": 2, "title": "Tester FastAPI", "description": "Écrire des tests"}
2. Utiliser Fly.io pour le déploiement
Suivez les étapes ci-dessous pour déployer votre application sur Fly.io :
## Créer un fichier app.json pour spécifier la configuration de l'application
{
"name": "my-fastapi-app",
"kill_signal": "SIGINT",
"kill_timeout": 15,
"services": [
{
"http_checks": [{
"interval": "10s"
}],
"internal_port": 8000,
"log_destination": "stderr",
"name": "my-fastapi-app",
"release_command": "",
"runtime": "docker",
"source_path": ".",
"type": "python",
"disk": 512
}
],
"ssh_ports": [],
"storage_mb": 1024,
"env_vars": {}
}
## Déployer l'application sur Fly.io
flyctl launch --name my-fastapi-app
3. Ajouter des logs
Utilisez flyctl logs pour surveiller les journaux de votre application en temps réel.
flyctl logs -a my-fastapi-app
Défi pratique
Défini un projet simple : une API de blog
Créez une API de blog avec FastAPI, permettant d'ajouter des articles, de les récupérer et de les supprimer. Utilisez Docker pour construire l'image de votre application et déployez-la sur Fly.io.
Ce tutoriel couvre les aspects fondamentaux du déploiement de FastAPI sur Fly.io en fournissant un projet complet et des erreurs courantes à résoudre. N'oubliez pas de tester chaque étape pour vous assurer que tout fonctionne correctement.