Prépare la dockerization : Dockerfile, docker-compose, .dockerignore

Image Python 3.14-slim (même version qu'en dev local), utilisateur
non-root, healthcheck sans dépendance externe, un seul worker Uvicorn
(SQLite gère mal les écritures concurrentes multi-process). data/ monté
en volume dans docker-compose.yml pour garder la base SQLite visible et
sauvegardable directement dans le dossier du projet, comme en dev local.

Non testé avec un vrai build : Docker indisponible dans cet environnement
de dev, à vérifier avant un premier déploiement.
This commit is contained in:
maxsoch 2026-07-14 19:44:35 +02:00
parent 11da706573
commit 5792b453e7
5 changed files with 105 additions and 0 deletions

12
.dockerignore Normal file
View file

@ -0,0 +1,12 @@
.git
.gitignore
.venv
__pycache__
*.pyc
.pytest_cache
.ruff_cache
.claude
data/*.db
.env
tests
*.md

View file

@ -51,6 +51,12 @@ ruff check . # linter
Les tests utilisent une base SQLite **en mémoire** (voir `tests/conftest.py`), Les tests utilisent une base SQLite **en mémoire** (voir `tests/conftest.py`),
jamais le fichier `data/stock.db` du développement. jamais le fichier `data/stock.db` du développement.
`docker compose up --build` construit et lance l'app (Dockerfile, Python
3.14-slim, `data/` monté en volume depuis l'hôte, 1 seul worker Uvicorn
volontairement — SQLite gère mal les écritures concurrentes multi-process).
Pas testé avec un vrai build dans cette session (Docker indisponible dans
l'environnement de dev) — à vérifier avant un premier déploiement.
## Convention : commentaires détaillés ## Convention : commentaires détaillés
Contrairement à la préférence par défaut (commentaires minimaux), ce projet Contrairement à la préférence par défaut (commentaires minimaux), ce projet

45
Dockerfile Normal file
View file

@ -0,0 +1,45 @@
# Image officielle Python, même version majeure qu'en dev local (3.14).
# "slim" = Debian minimal : largement suffisant ici, aucune dépendance du
# projet ne nécessite de compilation (pas besoin d'une image complète).
FROM python:3.14-slim
# Pas de bytecode .pyc écrit sur disque (inutile dans un conteneur
# éphémère) et sortie non bufferisée (les logs uvicorn apparaissent tout
# de suite dans `docker logs`, pas seulement à la fermeture du process).
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# Copié avant le reste du code : cette couche ne se reconstruit que si
# requirements.txt change, ce qui accélère largement les rebuilds pendant
# le développement (le code applicatif change bien plus souvent).
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ app/
COPY scripts/ scripts/
# data/ (base SQLite) est destiné à être monté en volume (voir
# docker-compose.yml) pour que les données survivent aux
# rebuilds/redémarrages du conteneur.
RUN mkdir -p data
# Utilisateur non-root par sécurité. Si le volume data/ monté depuis
# l'hôte pose un problème de permissions (l'utilisateur de l'hôte n'a pas
# forcément l'UID 1000), ajustez les droits du dossier hôte avec
# `chown -R 1000:1000 data/`, ou retirez ces deux lignes pour tourner en root.
RUN useradd --create-home --uid 1000 appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
# Vérifie que le serveur répond, sans dépendre d'un outil externe
# (curl/wget, absents de l'image "slim").
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/materiels')" || exit 1
# Un seul worker Uvicorn : SQLite gère mal les écritures concurrentes
# depuis plusieurs process. Largement suffisant pour l'usage interne visé.
# Pas de --reload : réservé au dev local hors conteneur (voir README).
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

View file

@ -31,6 +31,26 @@ uvicorn app.main:app --reload
L'app est accessible sur http://127.0.0.1:8000. La base SQLite est créée L'app est accessible sur http://127.0.0.1:8000. La base SQLite est créée
automatiquement au premier démarrage dans `data/stock.db`. automatiquement au premier démarrage dans `data/stock.db`.
## Lancer avec Docker
```bash
cp .env.example .env # requis, voir Configuration des emails ci-dessous
docker compose up --build
```
L'app est accessible sur http://localhost:8000. `data/` est monté depuis
l'hôte : la base SQLite (`data/stock.db`) reste au même endroit qu'en dev
local sans Docker, visible et sauvegardable directement dans ce dossier.
Un seul worker Uvicorn dans le conteneur (voir commentaire dans le
`Dockerfile`) : SQLite gère mal les écritures concurrentes depuis
plusieurs process, et un seul worker est largement suffisant pour l'usage
interne visé.
Pour du développement actif, la boucle venv + `--reload` ci-dessus reste
plus rapide (pas de rebuild d'image à chaque changement) ; Docker sert
surtout à tester ou reproduire l'environnement de déploiement.
## Lancer les tests ## Lancer les tests
```bash ```bash
@ -107,4 +127,6 @@ scripts/
envoyer_resume_stock.py déclenche manuellement l'email récapitulatif envoyer_resume_stock.py déclenche manuellement l'email récapitulatif
tests/ tests pytest (client de test FastAPI + base SQLite en mémoire) tests/ tests pytest (client de test FastAPI + base SQLite en mémoire)
data/ base SQLite locale (ignorée par git) data/ base SQLite locale (ignorée par git)
Dockerfile image de l'app (Python 3.14-slim, utilisateur non-root, 1 worker Uvicorn)
docker-compose.yml lancement local avec data/ monté en volume
``` ```

20
docker-compose.yml Normal file
View file

@ -0,0 +1,20 @@
# Lancement : docker compose up --build
# L'app est accessible sur http://localhost:8000
#
# Prérequis : cp .env.example .env (et le remplir si un vrai relais SMTP
# est disponible ; sinon les emails restent simulés dans les logs, comme
# en dev local — voir `docker compose logs -f`).
#
# data/ est monté depuis l'hôte : la base SQLite (data/stock.db) reste
# visible et sauvegardable directement dans ce dossier, exactement comme
# en dev local sans Docker.
services:
stock:
build: .
ports:
- "8000:8000"
volumes:
- ./data:/app/data
env_file:
- .env
restart: unless-stopped