Skip to content

Développement local avec kind

Ce guide permet de faire tourner FastAPI + PostgreSQL localement sur un cluster kind, sans dépendre d'AWS.

Depuis #161 : le backend vit sous backend/

Le workload FastAPI a été déplacé sous backend/, symétrique de frontend/ (ADR 030). Trois gestes du quotidien changent :

  • Les tests se lancent depuis backend/ : cd backend && pytest. Les tests importent from app.main import app, donc lancés depuis la racine ils ne résolvent plus rien.
  • Le build d'image prend backend/ comme contexte : docker build -t fastapi:local backend/.
  • .env et .env.docker vivent sous backend/. app/config.py déclare env_file=".env", résolu par rapport au répertoire courant. Ces deux fichiers étant gitignorés, un git pull ne les déplace pas : le faire à la main, sinon Settings() échoue au démarrage sur des variables manquantes.
mv .env .env.docker backend/   # une seule fois, après avoir récupéré la branche

Prérequis

  • Docker Desktop (ou Docker dans WSL)
  • kind v0.25+
  • kubectl v1.31+

Vérification rapide :

docker version
kind version
kubectl version --client

Démarrage rapide

1. Créer le cluster

kind create cluster --name fastapi-local
kubectl cluster-info --context kind-fastapi-local

2. Builder et charger l'image

cd ~/Project-DEVOPS-FASTAPI-2026

# Build
docker build -t fastapi:local backend/

# Charger dans kind (contourne le registry)
kind load docker-image fastapi:local --name fastapi-local

3. Déployer

kubectl apply -k k8s/overlays/local-kind/ --context kind-fastapi-local

4. Vérifier que les pods sont Running

kubectl get pods -n fastapi --context kind-fastapi-local

Attendre que les deux pods fastapi-* et postgres-* soient 1/1 Running.

5. Tester

# Port-forward en arrière-plan
kubectl port-forward -n fastapi svc/fastapi 8080:80 --context kind-fastapi-local &

# Test basique
curl http://localhost:8080/

# Swagger UI (ouvrir dans un navigateur)
# http://localhost:8080/docs

# Test CORS
curl -H "Origin: http://localhost:3000" -v http://localhost:8080/ 2>&1 | grep -i access-control

Cycle de développement

Après chaque modification du code :

docker build -t fastapi:local backend/
kind load docker-image fastapi:local --name fastapi-local
kubectl rollout restart deployment/fastapi -n fastapi --context kind-fastapi-local
kubectl rollout status deployment/fastapi -n fastapi --context kind-fastapi-local

Ce que fait l'overlay local-kind

Paramètre Base (EKS) Local (kind)
DB_HOSTNAME endpoint RDS AWS postgres (service k8s)
DB_NAME fastapi_db fastapi
PostgreSQL RDS managé Pod avec emptyDir
Image ${ECR_IMAGE} (CI) fastapi:local
imagePullPolicy Always Never
Replicas 2 1
Secrets External Secrets Operator Secret k8s local

Debugging

Symptôme Cause probable Fix
ImagePullBackOff sur fastapi:local Image pas chargée dans kind kind load docker-image fastapi:local --name fastapi-local
Init:Error sur migrations PostgreSQL pas encore ready Attendre, le pod va retry
Init:CrashLoopBackOff Erreur Alembic ou config manquante kubectl logs -n fastapi <pod> -c migrations
Pydantic ValidationError Secret incomplet Vérifier DB_PASSWORD + SECRET_KEY dans secret.yaml
Pod en Pending Resource limits trop hauts Réduire les limits dans le base
readOnlyRootFilesystem bloque Lib qui écrit hors /tmp Ajouter un emptyDir sur le chemin concerné
Données postgres perdues emptyDir est éphémère Normal — supprimer le pod postgres pour reset
# Logs migrations
kubectl logs -n fastapi <pod-fastapi> -c migrations --context kind-fastapi-local

# Logs app
kubectl logs -n fastapi <pod-fastapi> -c fastapi --context kind-fastapi-local

# Reset base de données (supprime toutes les données)
kubectl delete pod -n fastapi -l app=postgres --context kind-fastapi-local

Teardown

# Supprimer uniquement les ressources (garde le cluster)
kubectl delete -k k8s/overlays/local-kind/ --context kind-fastapi-local

# Supprimer le cluster entier
kind delete cluster --name fastapi-local

Test cross-origin front/back via docker-compose (#110)

Le docker-compose-dev.yaml inclut le service frontend : il reproduit en local la topologie cross-origin de la prod (app.api.). La SPA est servie sur localhost:3000, l'API sur localhost:8080 → origines distinctes → le navigateur applique CORS exactement comme en prod. On valide donc le vrai fetch /posts/public + les en-têtes CORS avant de pousser.

docker compose -f docker-compose-dev.yaml up --build frontend api db
# Front : http://localhost:3000  (fetch -> http://localhost:8080/posts/public)
# L'API autorise http://localhost:3000 via le defaut ALLOWED_ORIGINS de backend/app/config.py.

L'URL de l'API est lue au runtime dans /config/config.js (#156), plus figée au build. En local, le compose monte frontend/local-config/ sur /usr/share/nginx/html/config : c'est l'équivalent exact du ConfigMap monté par overlay dans le cluster, donc le mécanisme testé en local est celui qui tourne en prod. Pour viser une autre API, éditer frontend/local-config/config.js et redémarrer le conteneur — pas de rebuild. Une base vide → liste vide ; créer un post via POST /posts/ (authentifié) pour peupler /posts/public.