Dans le monde du développement Python, la gestion de plusieurs projets locaux peut rapidement devenir complexe. Les défis liés à la gestion des versions, des dépendances et des configurations peuvent rendre le bootstrap d'un projet chronophage, surtout lorsque plusieurs projets coexistent dans le même environnement. Traditionnellement, l'installation des dépendances directement dans l'environnement local pouvait mener à des conflits de configuration ou de dépendances, devenant un obstacle lors du partage de projets avec des coéquipiers.
Isolation des Environnements de Développement avec Docker
Une solution efficace pour résoudre ces problèmes consiste à créer des environnements de développement isolés pour chaque projet. Cette isolation peut être facilement réalisée en utilisant des conteneurs et Docker Compose pour les gérer. Cette série d'articles explore cette approche, en commençant par les bonnes pratiques pour containeriser un service ou un outil Python.
Configuration des Outils Requis
Avant de plonger dans le processus de containerisation, assurez-vous d'avoir les outils nécessaires installés localement :
- Windows ou macOS : Installez Docker Desktop.
- Linux : Installez Docker, puis Docker Compose.
Containerisation d'un Service Python
Voyons comment containeriser un service Python simple, tel qu'une application Flask, permettant de le faire fonctionner de manière autonome sans configuration supplémentaire.
# server.py
from flask import Flask
server = Flask(__name__)
@server.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
server.run(host='0.0.0.0')
Pour exécuter ce programme, assurez-vous d'avoir les dépendances requises installées en utilisant un fichier requirements.txt
. Par exemple :
# requirements.txt
Flask==1.1.1
Organisez votre structure de projet comme suit :
app
├─── requirements.txt
└─── src
└─── server.py
Dockerfile : La Clé de la Containerisation
Le moyen de faire fonctionner notre code Python dans un conteneur consiste à le packager en tant qu'image Docker, puis à exécuter un conteneur basé sur cette image. Un exemple de Dockerfile pour notre service Python "Hello World" est le suivant :
# Définir l'image de base (système d'exploitation hôte)
FROM python:3.8
# Définir le répertoire de travail dans le conteneur
WORKDIR /code
# Copier le fichier de dépendances dans le répertoire de travail
COPY requirements.txt .
# Installer les dépendances
RUN pip install -r requirements.txt
# Copier le contenu du répertoire local src dans le répertoire de travail
COPY src/ .
# Commande à exécuter au démarrage du conteneur
CMD [ "python", "./server.py" ]
Chaque instruction du Dockerfile génère une couche d'image, empilée sur les précédentes. Il en résulte une image Docker, une pile de couches en lecture seule. L'exécution de la commande docker build
génère cette image, comme illustré ci-dessous :
$ docker build -t monimage .
Bonnes Pratiques de Développement pour les Dockerfiles
Concentrons-nous maintenant sur les bonnes pratiques pour accélérer le cycle de développement.
Choix de l'Image de Base
Le choix de l'image de base est crucial, impactant la taille de l'image finale. Privilégions les images officielles, fréquemment mises à jour et moins sujettes aux problèmes de sécurité. Pour les applications Python, l'image officielle Docker Python slim (par exemple python:3.8-slim
) fonctionne bien dans la plupart des cas.
Ordre des Instructions pour Exploiter le Cache de Construction
Pour exploiter efficacement le mécanisme de cache du constructeur, placez les instructions pour les couches qui changent fréquemment après celles qui changent moins. Par exemple, installez d'abord les dépendances, puis copiez le code source.
Construction à Plusieurs Étapes
Bien que moins utile pendant le développement, les constructions à plusieurs étapes permettent d'obtenir une image finale plus légère. Cela consiste à éliminer les fichiers et paquets logiciels inutiles. Un exemple rapide :
# Première étape
FROM python:3.8 AS builder
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# Deuxième étape
FROM python:3.8-slim
WORKDIR /code
COPY --from=builder /root/.local /root/.local
COPY ./src .
ENV PATH=/root/.local:$PATH
CMD [ "python", "./server.py" ]
Exécuter le Conteneur
Après avoir écrit le Dockerfile et construit l'image, exécutez le conteneur avec votre service Python.
$ docker run -d -p 5000:5000 monimage
Votre serveur "Hello World" est maintenant containerisé, accessible via le port mappé à localhost.
$ curl http://localhost:5000
"Hello World!"
Conclusion
Ce post a montré comment containeriser un service Python pour une expérience de développement optimale. La containerisation offre des résultats déterministes facilement reproductibles sur d'autres plates-formes, évite les conflits de dépendances et permet de maintenir un environnement de développement standard propre. Un environnement de développement containerisé est facile à gérer et à partager avec d'autres développeurs.
Dans le prochain article de cette série, nous explorerons la mise en place d'un projet multi-services basé sur des conteneurs, où le composant Python est connecté à d'autres services externes, et comment gérer le cycle de vie de tous ces composants de projet avec Docker Compose.