FastAPI dans des conteneurs - Docker¶
đ Translation by AI and humans
This translation was made by AI guided by humans. đ€
It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. đ€
You can improve this translation by helping us guide the AI LLM better.
Lors du déploiement d'applications FastAPI, une approche courante consiste à construire une image de conteneur Linux. C'est généralement fait avec Docker. Vous pouvez ensuite déployer cette image de conteneur de plusieurs façons possibles.
L'utilisation de conteneurs Linux présente plusieurs avantages, notamment la sécurité, la réplicabilité, la simplicité, entre autres.
Astuce
Vous ĂȘtes pressĂ© et vous connaissez dĂ©jĂ tout ça ? Allez directement au Dockerfile ci-dessous đ.
Aperçu du Dockerfile đ
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
# Si vous exécutez derriÚre un proxy comme Nginx ou Traefik, ajoutez --proxy-headers
# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"]
Qu'est-ce qu'un conteneur¶
Les conteneurs (principalement les conteneurs Linux) sont un moyen trĂšs lĂ©ger d'empaqueter des applications, y compris toutes leurs dĂ©pendances et les fichiers nĂ©cessaires, tout en les isolant des autres conteneurs (autres applications ou composants) dans le mĂȘme systĂšme.
Les conteneurs Linux s'exĂ©cutent en utilisant le mĂȘme noyau Linux que l'hĂŽte (machine, machine virtuelle, serveur cloud, etc.). Cela signifie simplement qu'ils sont trĂšs lĂ©gers (comparĂ©s Ă des machines virtuelles complĂštes Ă©mulant un systĂšme d'exploitation entier).
Ainsi, les conteneurs consomment peu de ressources, une quantité comparable à l'exécution directe des processus (alors qu'une machine virtuelle consommerait beaucoup plus).
Les conteneurs ont également leurs propres processus d'exécution isolés (généralement un seul processus), leur systÚme de fichiers et leur réseau, ce qui simplifie le déploiement, la sécurité, le développement, etc.
Qu'est-ce qu'une image de conteneur¶
Un conteneur s'exécute à partir d'une image de conteneur.
Une image de conteneur est une version statique de tous les fichiers, des variables d'environnement et de la commande/le programme par dĂ©faut devant ĂȘtre prĂ©sents dans un conteneur. Ici, statique signifie que l'image du conteneur ne s'exĂ©cute pas, elle n'est pas en cours d'exĂ©cution, ce ne sont que les fichiers et mĂ©tadonnĂ©es empaquetĂ©s.
Par opposition à une « image de conteneur » qui correspond aux contenus statiques stockés, un « conteneur » fait normalement référence à l'instance en cours d'exécution, la chose qui est exécutée.
Lorsque le conteneur est démarré et en cours d'exécution (démarré à partir d'une image de conteneur), il peut créer ou modifier des fichiers, des variables d'environnement, etc. Ces changements n'existeront que dans ce conteneur, mais ne persisteront pas dans l'image de conteneur sous-jacente (ils ne seront pas enregistrés sur le disque).
Une image de conteneur est comparable au programme et Ă ses contenus, par exemple python et un fichier main.py.
Et le conteneur lui-mĂȘme (par opposition Ă l'image de conteneur) est l'instance en cours d'exĂ©cution rĂ©elle de l'image, comparable Ă un processus. En fait, un conteneur ne fonctionne que lorsqu'il a un processus en cours d'exĂ©cution (et normalement, il s'agit d'un seul processus). Le conteneur s'arrĂȘte lorsqu'aucun processus n'y est en cours d'exĂ©cution.
Images de conteneur¶
Docker a été l'un des principaux outils pour créer et gérer des images de conteneur et des conteneurs.
Et il existe un Docker Hub public avec des images de conteneur officielles pré-construites pour de nombreux outils, environnements, bases de données et applications.
Par exemple, il existe une image Python officielle.
Et il existe beaucoup d'autres images pour différentes choses comme des bases de données, par exemple :
- PostgreSQL
- MySQL
- MongoDB
- Redis, etc.
En utilisant une image de conteneur pré-construite, il est trÚs facile de combiner et d'utiliser différents outils. Par exemple, pour essayer une nouvelle base de données. Dans la plupart des cas, vous pouvez utiliser les images officielles et simplement les configurer avec des variables d'environnement.
Ainsi, dans de nombreux cas, vous pouvez apprendre les conteneurs et Docker et réutiliser ces connaissances avec de nombreux outils et composants différents.
Vous exécuteriez donc plusieurs conteneurs avec des éléments différents, comme une base de données, une application Python, un serveur web avec une application frontend React, et les connecter entre eux via leur réseau interne.
Tous les systÚmes de gestion de conteneurs (comme Docker ou Kubernetes) disposent de ces fonctionnalités réseau intégrées.
Conteneurs et processus¶
Une image de conteneur inclut normalement dans ses métadonnées le programme/la commande par défaut à exécuter lorsque le conteneur est démarré et les paramÚtres à transmettre à ce programme. TrÚs similaire à ce que vous utiliseriez en ligne de commande.
Lorsqu'un conteneur est démarré, il exécutera cette commande/ce programme (bien que vous puissiez la/le remplacer et faire exécuter une autre commande/un autre programme).
Un conteneur fonctionne tant que le processus principal (commande ou programme) est en cours d'exécution.
Un conteneur a normalement un seul processus, mais il est aussi possible de dĂ©marrer des sous-processus Ă partir du processus principal, et ainsi vous aurez plusieurs processus dans le mĂȘme conteneur.
Mais il n'est pas possible d'avoir un conteneur en cours d'exĂ©cution sans au moins un processus en cours. Si le processus principal s'arrĂȘte, le conteneur s'arrĂȘte.
Construire une image Docker pour FastAPI¶
TrĂšs bien, construisons quelque chose maintenant ! đ
Je vais vous montrer comment construire une image Docker pour FastAPI à partir de zéro, basée sur l'image officielle Python.
C'est ce que vous voudrez faire dans la plupart des cas, par exemple :
- Utiliser Kubernetes ou des outils similaires
- Exécuter sur un Raspberry Pi
- Utiliser un service cloud qui exécuterait une image de conteneur pour vous, etc.
DĂ©pendances des paquets¶
Vous aurez normalement les dépendances des paquets de votre application dans un fichier.
Cela dépendra principalement de l'outil que vous utilisez pour installer ces dépendances.
La maniĂšre la plus courante consiste Ă avoir un fichier requirements.txt avec les noms des paquets et leurs versions, un par ligne.
Vous utiliserez bien sĂ»r les mĂȘmes idĂ©es que vous avez lues dans Ă propos des versions de FastAPI pour dĂ©finir les plages de versions.
Par exemple, votre requirements.txt pourrait ressembler Ă :
fastapi[standard]>=0.113.0,<0.114.0
pydantic>=2.7.0,<3.0.0
Et vous installerez normalement ces dépendances de paquets avec pip, par exemple :
$ pip install -r requirements.txt
---> 100%
Successfully installed fastapi pydantic
Info
Il existe d'autres formats et outils pour définir et installer des dépendances de paquets.
CrĂ©er le code FastAPI¶
- Créez un répertoire
appet entrez dedans. - Créez un fichier vide
__init__.py. - Créez un fichier
main.pyavec :
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
Dockerfile¶
Maintenant, dans le mĂȘme rĂ©pertoire de projet, crĂ©ez un fichier Dockerfile avec :
# (1)!
FROM python:3.9
# (2)!
WORKDIR /code
# (3)!
COPY ./requirements.txt /code/requirements.txt
# (4)!
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (5)!
COPY ./app /code/app
# (6)!
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
-
Démarrer à partir de l'image de base Python officielle.
-
Définir le répertoire de travail courant sur
/code.C'est lĂ que nous placerons le fichier
requirements.txtet le répertoireapp. -
Copier le fichier des dépendances vers le répertoire
/code.Copier uniquement le fichier des dépendances en premier, pas le reste du code.
Comme ce fichier ne change pas souvent, Docker le détectera et utilisera le cache pour cette étape, ce qui activera le cache pour l'étape suivante aussi.
-
Installer les dépendances listées dans le fichier des dépendances.
L'option
--no-cache-dirindique Ăpipde ne pas enregistrer localement les paquets tĂ©lĂ©chargĂ©s, car cela ne sert que sipipdevait ĂȘtre relancĂ© pour installer les mĂȘmes paquets, mais ce n'est pas le cas lorsque l'on travaille avec des conteneurs.Remarque
Le
--no-cache-dirconcerne uniquementpip, cela n'a rien Ă voir avec Docker ou les conteneurs.L'option
--upgradeindique Ăpipde mettre Ă niveau les paquets s'ils sont dĂ©jĂ installĂ©s.Comme l'Ă©tape prĂ©cĂ©dente de copie du fichier peut ĂȘtre dĂ©tectĂ©e par le cache Docker, cette Ă©tape utilisera Ă©galement le cache Docker lorsqu'il est disponible.
L'utilisation du cache à cette étape vous fera gagner beaucoup de temps lors de la reconstruction de l'image encore et encore pendant le développement, au lieu de télécharger et installer toutes les dépendances à chaque fois.
-
Copier le répertoire
./appdans le répertoire/code.Comme cela contient tout le code qui est ce qui change le plus fréquemment, le cache Docker ne sera pas facilement utilisé pour cette étape ou pour les étapes suivantes.
Il est donc important de placer cela vers la fin du
Dockerfile, pour optimiser les temps de construction de l'image de conteneur. -
Définir la commande pour utiliser
fastapi run, qui utilise Uvicorn sous le capot.CMDprend une liste de chaĂźnes, chacune de ces chaĂźnes correspond Ă ce que vous taperiez en ligne de commande sĂ©parĂ© par des espaces.Cette commande sera exĂ©cutĂ©e Ă partir du rĂ©pertoire de travail courant, le mĂȘme rĂ©pertoire
/codeque vous avez défini plus haut avecWORKDIR /code.
Astuce
Passez en revue ce que fait chaque ligne en cliquant sur chaque bulle numĂ©rotĂ©e dans le code. đ
Alertes
Vous devez vous assurer d'utiliser toujours la forme exec de l'instruction CMD, comme expliqué ci-dessous.
Utiliser CMD - Forme Exec¶
L'instruction Docker CMD peut ĂȘtre Ă©crite sous deux formes :
â Forme Exec :
# â
Ă faire
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
âïž Forme Shell :
# âïž Ă ne pas faire
CMD fastapi run app/main.py --port 80
Assurez-vous d'utiliser toujours la forme exec pour garantir que FastAPI peut s'arrĂȘter proprement et que les Ă©vĂ©nements de cycle de vie sont dĂ©clenchĂ©s.
Vous pouvez en lire davantage dans la documentation Docker sur les formes shell et exec.
Cela peut ĂȘtre trĂšs visible lors de l'utilisation de docker compose. Voir cette section de la FAQ Docker Compose pour plus de dĂ©tails techniques : Pourquoi mes services mettent-ils 10 secondes Ă se recrĂ©er ou Ă s'arrĂȘter ?.
Structure du rĂ©pertoire¶
Vous devriez maintenant avoir une structure de répertoire comme :
.
âââ app
â  âââ __init__.py
â âââ main.py
âââ Dockerfile
âââ requirements.txt
DerriĂšre un proxy de terminaison TLS¶
Si vous exĂ©cutez votre conteneur derriĂšre un proxy de terminaison TLS (load balancer) comme Nginx ou Traefik, ajoutez l'option --proxy-headers, cela indiquera Ă Uvicorn (via la CLI FastAPI) de faire confiance aux en-tĂȘtes envoyĂ©s par ce proxy lui indiquant que l'application s'exĂ©cute derriĂšre HTTPS, etc.
CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"]
Cache Docker¶
Il y a une astuce importante dans ce Dockerfile, nous copions d'abord le fichier des dépendances seul, pas le reste du code. Laissez-moi vous expliquer pourquoi.
COPY ./requirements.txt /code/requirements.txt
Docker et d'autres outils construisent ces images de conteneur de maniÚre incrémentale, en ajoutant une couche au-dessus de l'autre, en commençant par le haut du Dockerfile et en ajoutant tous les fichiers créés par chacune des instructions du Dockerfile.
Docker et des outils similaires utilisent Ă©galement un cache interne lors de la construction de l'image : si un fichier n'a pas changĂ© depuis la derniĂšre construction de l'image de conteneur, alors il va rĂ©utiliser la mĂȘme couche créée la derniĂšre fois, au lieu de recopier le fichier et crĂ©er une nouvelle couche Ă partir de zĂ©ro.
Ăviter simplement la copie des fichiers n'amĂ©liore pas nĂ©cessairement les choses de maniĂšre significative, mais comme il a utilisĂ© le cache pour cette Ă©tape, il peut utiliser le cache pour l'Ă©tape suivante. Par exemple, il peut utiliser le cache pour l'instruction qui installe les dĂ©pendances avec :
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
Le fichier des dépendances ne changera pas fréquemment. Ainsi, en copiant uniquement ce fichier, Docker pourra utiliser le cache pour cette étape.
Et ensuite, Docker pourra utiliser le cache pour l'Ă©tape suivante qui tĂ©lĂ©charge et installe ces dĂ©pendances. Et c'est lĂ que nous gagnons beaucoup de temps. âš ... et Ă©vitons l'ennui en attendant. đȘđ
Télécharger et installer les dépendances de paquets peut prendre des minutes, mais utiliser le cache ne prendra que quelques secondes au plus.
Et comme vous reconstruirez l'image de conteneur encore et encore pendant le développement pour vérifier que vos modifications de code fonctionnent, cela vous fera gagner beaucoup de temps cumulé.
Ensuite, vers la fin du Dockerfile, nous copions tout le code. Comme c'est ce qui change le plus fréquemment, nous le plaçons vers la fin, car presque toujours, tout ce qui suit cette étape ne pourra pas utiliser le cache.
COPY ./app /code/app
Construire l'image Docker¶
Maintenant que tous les fichiers sont en place, construisons l'image de conteneur.
- Allez dans le rĂ©pertoire du projet (lĂ oĂč se trouve votre
Dockerfile, contenant votre répertoireapp). - Construisez votre image FastAPI :
$ docker build -t myimage .
---> 100%
Astuce
Remarquez le . à la fin, équivalent à ./, il indique à Docker le répertoire à utiliser pour construire l'image de conteneur.
Dans ce cas, c'est le mĂȘme rĂ©pertoire courant (.).
DĂ©marrer le conteneur Docker¶
- Exécutez un conteneur basé sur votre image :
$ docker run -d --name mycontainer -p 80:80 myimage
VĂ©rifier¶
Vous devriez pouvoir le vérifier via l'URL de votre conteneur Docker, par exemple : http://192.168.99.100/items/5?q=somequery ou http://127.0.0.1/items/5?q=somequery (ou équivalent, en utilisant votre hÎte Docker).
Vous verrez quelque chose comme :
{"item_id": 5, "q": "somequery"}
Documentation interactive de l'API¶
Vous pouvez maintenant aller sur http://192.168.99.100/docs ou http://127.0.0.1/docs (ou équivalent, en utilisant votre hÎte Docker).
Vous verrez la documentation interactive automatique de l'API (fournie par Swagger UI) :

Documentation alternative de l'API¶
Et vous pouvez aussi aller sur http://192.168.99.100/redoc ou http://127.0.0.1/redoc (ou équivalent, en utilisant votre hÎte Docker).
Vous verrez la documentation automatique alternative (fournie par ReDoc) :

Construire une image Docker avec un FastAPI mono-fichier¶
Si votre FastAPI est un seul fichier, par exemple main.py sans répertoire ./app, votre structure de fichiers pourrait ressembler à ceci :
.
âââ Dockerfile
âââ main.py
âââ requirements.txt
Vous n'auriez alors qu'Ă changer les chemins correspondants pour copier le fichier dans le Dockerfile :
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# (1)!
COPY ./main.py /code/
# (2)!
CMD ["fastapi", "run", "main.py", "--port", "80"]
-
Copier le fichier
main.pydirectement dans le répertoire/code(sans répertoire./app). -
Utiliser
fastapi runpour servir votre application dans le fichier uniquemain.py.
Lorsque vous passez le fichier Ă fastapi run, il dĂ©tectera automatiquement qu'il s'agit d'un fichier unique et non d'un package et saura comment l'importer et servir votre application FastAPI. đ
Concepts de dĂ©ploiement¶
Parlons Ă nouveau de certains des mĂȘmes Concepts de dĂ©ploiement en termes de conteneurs.
Les conteneurs sont principalement un outil pour simplifier le processus de construction et de déploiement d'une application, mais ils n'imposent pas une approche particuliÚre pour gérer ces concepts de déploiement, et il existe plusieurs stratégies possibles.
La bonne nouvelle, c'est qu'avec chaque stratĂ©gie diffĂ©rente, il existe un moyen de couvrir tous les concepts de dĂ©ploiement. đ
Passons en revue ces concepts de déploiement en termes de conteneurs :
- HTTPS
- Exécution au démarrage
- Redémarrages
- Réplication (le nombre de processus en cours d'exécution)
- Mémoire
- Ătapes prĂ©alables au dĂ©marrage
HTTPS¶
Si l'on se concentre uniquement sur l'image de conteneur pour une application FastAPI (et plus tard sur le conteneur en cours d'exécution), HTTPS serait normalement géré à l'extérieur par un autre outil.
Cela pourrait ĂȘtre un autre conteneur, par exemple avec Traefik, gĂ©rant HTTPS et l'acquisition automatique des certificats.
Astuce
Traefik s'intĂšgre avec Docker, Kubernetes, et d'autres, donc il est trĂšs facile de configurer HTTPS pour vos conteneurs avec lui.
Alternativement, HTTPS pourrait ĂȘtre gĂ©rĂ© par un fournisseur cloud comme l'un de leurs services (tout en exĂ©cutant l'application dans un conteneur).
ExĂ©cution au dĂ©marrage et redĂ©marrages¶
Il y a normalement un autre outil chargé de démarrer et exécuter votre conteneur.
Cela pourrait ĂȘtre Docker directement, Docker Compose, Kubernetes, un service cloud, etc.
Dans la plupart (ou toutes) des situations, il existe une option simple pour activer l'exécution du conteneur au démarrage et activer les redémarrages en cas d'échec. Par exemple, dans Docker, c'est l'option de ligne de commande --restart.
Sans utiliser de conteneurs, faire en sorte que les applications s'exĂ©cutent au dĂ©marrage et avec redĂ©marrages peut ĂȘtre fastidieux et difficile. Mais en travaillant avec des conteneurs, dans la plupart des cas, cette fonctionnalitĂ© est incluse par dĂ©faut. âš
RĂ©plication - Nombre de processus¶
Si vous avez un cluster de machines avec Kubernetes, Docker Swarm Mode, Nomad, ou un autre systÚme complexe similaire pour gérer des conteneurs distribués sur plusieurs machines, alors vous voudrez probablement gérer la réplication au niveau du cluster plutÎt que d'utiliser un gestionnaire de processus (comme Uvicorn avec workers) dans chaque conteneur.
L'un de ces systĂšmes de gestion de conteneurs distribuĂ©s comme Kubernetes dispose normalement d'une maniĂšre intĂ©grĂ©e de gĂ©rer la rĂ©plication des conteneurs tout en supportant l'Ă©quilibrage de charge des requĂȘtes entrantes. Le tout au niveau du cluster.
Dans ces cas, vous voudrez probablement construire une image Docker à partir de zéro comme expliqué ci-dessus, en installant vos dépendances et en exécutant un seul processus Uvicorn au lieu d'utiliser plusieurs workers Uvicorn.
Ăquilibreur de charge¶
Lors de l'utilisation de conteneurs, vous aurez normalement un composant Ă l'Ă©coute sur le port principal. Cela pourrait ĂȘtre un autre conteneur qui est Ă©galement un proxy de terminaison TLS pour gĂ©rer HTTPS ou un outil similaire.
Comme ce composant prend la charge des requĂȘtes et la distribue entre les workers de façon (espĂ©rons-le) Ă©quilibrĂ©e, on l'appelle Ă©galement communĂ©ment un Ă©quilibreur de charge.
Astuce
Le mĂȘme composant de proxy de terminaison TLS utilisĂ© pour HTTPS sera probablement aussi un Ă©quilibreur de charge.
Et en travaillant avec des conteneurs, le mĂȘme systĂšme que vous utilisez pour les dĂ©marrer et les gĂ©rer dispose dĂ©jĂ d'outils internes pour transmettre la communication rĂ©seau (par ex. les requĂȘtes HTTP) depuis cet Ă©quilibreur de charge (qui peut aussi ĂȘtre un proxy de terminaison TLS) vers le ou les conteneurs avec votre application.
Un Ă©quilibreur de charge - Plusieurs conteneurs worker¶
Lorsque vous travaillez avec Kubernetes ou des systĂšmes de gestion de conteneurs distribuĂ©s similaires, l'utilisation de leurs mĂ©canismes rĂ©seau internes permet au seul Ă©quilibreur de charge Ă l'Ă©coute sur le port principal de transmettre la communication (les requĂȘtes) vers potentiellement plusieurs conteneurs exĂ©cutant votre application.
Chacun de ces conteneurs exĂ©cutant votre application aura normalement un seul processus (par ex. un processus Uvicorn exĂ©cutant votre application FastAPI). Ils seront tous des conteneurs identiques, exĂ©cutant la mĂȘme chose, mais chacun avec son propre processus, sa mĂ©moire, etc. De cette façon, vous profiterez de la parallĂ©lisation sur diffĂ©rents cĆurs du CPU, voire sur diffĂ©rentes machines.
Et le systĂšme de conteneurs distribuĂ©s avec l'Ă©quilibreur de charge distribuera les requĂȘtes Ă chacun des conteneurs exĂ©cutant votre application Ă tour de rĂŽle. Ainsi, chaque requĂȘte pourrait ĂȘtre traitĂ©e par l'un des multiples conteneurs rĂ©pliquĂ©s exĂ©cutant votre application.
Et normalement cet Ă©quilibreur de charge pourra gĂ©rer des requĂȘtes qui vont vers d'autres applications dans votre cluster (par ex. vers un autre domaine, ou sous un autre prĂ©fixe de chemin d'URL), et transmettra cette communication aux bons conteneurs pour cette autre application s'exĂ©cutant dans votre cluster.
Un processus par conteneur¶
Dans ce type de scénario, vous voudrez probablement avoir un seul processus (Uvicorn) par conteneur, puisque vous gérez déjà la réplication au niveau du cluster.
Donc, dans ce cas, vous ne voudrez pas avoir plusieurs workers dans le conteneur, par exemple avec l'option de ligne de commande --workers. Vous voudrez avoir un seul processus Uvicorn par conteneur (mais probablement plusieurs conteneurs).
Avoir un autre gestionnaire de processus à l'intérieur du conteneur (comme ce serait le cas avec plusieurs workers) n'ajouterait que de la complexité inutile que vous gérez trÚs probablement déjà avec votre systÚme de cluster.
Conteneurs avec plusieurs processus et cas particuliers¶
Bien sĂ»r, il existe des cas particuliers oĂč vous pourriez vouloir avoir un conteneur avec plusieurs processus worker Uvicorn Ă l'intĂ©rieur.
Dans ces cas, vous pouvez utiliser l'option de ligne de commande --workers pour définir le nombre de workers que vous souhaitez exécuter :
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
# (1)!
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
- Ici, nous utilisons l'option de ligne de commande
--workerspour définir le nombre de workers à 4.
Voici quelques exemples oĂč cela pourrait avoir du sens :
Une application simple¶
Vous pourriez vouloir un gestionnaire de processus dans le conteneur si votre application est suffisamment simple pour s'exécuter sur un seul serveur, pas un cluster.
Docker Compose¶
Vous pourriez déployer sur un seul serveur (pas un cluster) avec Docker Compose, donc vous n'auriez pas un moyen simple de gérer la réplication des conteneurs (avec Docker Compose) tout en préservant le réseau partagé et l'équilibrage de charge.
Vous pourriez alors vouloir avoir un seul conteneur avec un gestionnaire de processus qui démarre plusieurs processus worker à l'intérieur.
L'idée principale est que rien de tout cela ne sont des rÚgles gravées dans la pierre que vous devez suivre aveuglément. Vous pouvez utiliser ces idées pour évaluer votre propre cas d'usage et décider de la meilleure approche pour votre systÚme, en vérifiant comment gérer les concepts suivants :
- Sécurité - HTTPS
- Exécution au démarrage
- Redémarrages
- Réplication (le nombre de processus en cours d'exécution)
- Mémoire
- Ătapes prĂ©alables au dĂ©marrage
MĂ©moire¶
Si vous exécutez un seul processus par conteneur, vous aurez une quantité de mémoire consommée plus ou moins bien définie, stable et limitée par chacun de ces conteneurs (plus d'un s'ils sont répliqués).
Vous pouvez alors dĂ©finir ces mĂȘmes limites et exigences de mĂ©moire dans vos configurations pour votre systĂšme de gestion de conteneurs (par exemple dans Kubernetes). De cette façon, il pourra rĂ©pliquer les conteneurs sur les machines disponibles en tenant compte de la quantitĂ© de mĂ©moire dont ils ont besoin et de la quantitĂ© disponible sur les machines du cluster.
Si votre application est simple, cela ne sera probablement pas un problĂšme, et vous n'aurez peut-ĂȘtre pas besoin de spĂ©cifier des limites de mĂ©moire strictes. Mais si vous utilisez beaucoup de mĂ©moire (par exemple avec des modĂšles de machine learning), vous devez vĂ©rifier combien de mĂ©moire vous consommez et ajuster le nombre de conteneurs qui s'exĂ©cutent sur chaque machine (et peut-ĂȘtre ajouter plus de machines Ă votre cluster).
Si vous exécutez plusieurs processus par conteneur, vous devez vous assurer que le nombre de processus démarrés ne consomme pas plus de mémoire que ce qui est disponible.
Ătapes prĂ©alables au dĂ©marrage et conteneurs¶
Si vous utilisez des conteneurs (par ex. Docker, Kubernetes), alors il existe deux approches principales que vous pouvez utiliser.
Plusieurs conteneurs¶
Si vous avez plusieurs conteneurs, probablement chacun exécutant un seul processus (par exemple, dans un cluster Kubernetes), alors vous voudrez probablement avoir un conteneur séparé effectuant le travail des étapes préalables dans un seul conteneur, exécutant un seul processus, avant d'exécuter les conteneurs worker répliqués.
Info
Si vous utilisez Kubernetes, ce sera probablement un Init Container.
Si, dans votre cas d'usage, il n'y a pas de problĂšme Ă exĂ©cuter ces Ă©tapes prĂ©alables plusieurs fois en parallĂšle (par exemple si vous n'exĂ©cutez pas de migrations de base de donnĂ©es, mais vĂ©rifiez simplement si la base de donnĂ©es est prĂȘte), alors vous pourriez aussi simplement les mettre dans chaque conteneur juste avant de dĂ©marrer le processus principal.
Un seul conteneur¶
Si vous avez une configuration simple, avec un seul conteneur qui dĂ©marre ensuite plusieurs processus worker (ou un seul processus aussi), vous pouvez alors exĂ©cuter ces Ă©tapes prĂ©alables dans le mĂȘme conteneur, juste avant de dĂ©marrer le processus avec l'application.
Image Docker de base¶
Il existait une image Docker officielle FastAPI : tiangolo/uvicorn-gunicorn-fastapi. Mais elle est dĂ©sormais dĂ©prĂ©ciĂ©e. âïž
Vous ne devriez probablement pas utiliser cette image Docker de base (ni aucune autre similaire).
Si vous utilisez Kubernetes (ou autres) et que vous définissez déjà la réplication au niveau du cluster, avec plusieurs conteneurs. Dans ces cas, il est préférable de construire une image à partir de zéro comme décrit ci-dessus : Construire une image Docker pour FastAPI.
Et si vous devez avoir plusieurs workers, vous pouvez simplement utiliser l'option de ligne de commande --workers.
Détails techniques
L'image Docker a Ă©tĂ© créée Ă une Ă©poque oĂč Uvicorn ne supportait pas la gestion et le redĂ©marrage des workers morts, il fallait donc utiliser Gunicorn avec Uvicorn, ce qui ajoutait pas mal de complexitĂ©, uniquement pour que Gunicorn gĂšre et redĂ©marre les processus worker Uvicorn.
Mais maintenant qu'Uvicorn (et la commande fastapi) supporte l'usage de --workers, il n'y a plus de raison d'utiliser une image Docker de base au lieu de construire la vĂŽtre (c'est Ă peu prĂšs la mĂȘme quantitĂ© de code đ
).
DĂ©ployer l'image de conteneur¶
AprÚs avoir une image de conteneur (Docker), il existe plusieurs façons de la déployer.
Par exemple :
- Avec Docker Compose sur un seul serveur
- Avec un cluster Kubernetes
- Avec un cluster Docker Swarm Mode
- Avec un autre outil comme Nomad
- Avec un service cloud qui prend votre image de conteneur et la déploie
Image Docker avec uv¶
Si vous utilisez uv pour installer et gérer votre projet, vous pouvez suivre leur guide Docker pour uv.
RĂ©capitulatif¶
Avec les systÚmes de conteneurs (par ex. avec Docker et Kubernetes), il devient assez simple de gérer tous les concepts de déploiement :
- HTTPS
- Exécution au démarrage
- Redémarrages
- Réplication (le nombre de processus en cours d'exécution)
- Mémoire
- Ătapes prĂ©alables au dĂ©marrage
Dans la plupart des cas, vous ne voudrez probablement pas utiliser d'image de base, et au contraire construire une image de conteneur à partir de zéro basée sur l'image Docker Python officielle.
En prenant soin de l'ordre des instructions dans le Dockerfile et du cache Docker, vous pouvez minimiser les temps de construction, maximiser votre productivitĂ© (et Ă©viter l'ennui). đ