Lorsqu'on travaille sur des projets AI, on a tendance à effectuer tout le développement initial sur nos laptops, puis à les transmettre au client. Il peut arriver que le client n'utilise pas le même système d'exploitation, ce qui veut dire que le code ne tournera probablement pas chez lui. C'est là que Docker entre en jeu !
Dans cet article, nous parcourrons les principales motivations derrière Docker et la manière dont il permet de construire des produits OS-agnostic qui accélèrent le développement et le déploiement.
Le sommaire est le suivant :
- Qu'est-ce que Docker ?
- Setup
- D'un projet AI à une image Docker
- Docker compose
- Bonnes pratiques
Qu'est-ce que Docker
Docker est un outil d'isolation de processus qui permet d'exécuter des applications dans un environnement confiné. Il est très souvent confondu avec les machines virtuelles qui « virtualisent » des ressources physiques, alors que Docker virtualise l'environnement d'exécution et est donc bien plus léger et rapide à démarrer et à exécuter.

Docker a principalement été inventé pour rendre les programmes OS-agnostic afin de faciliter leur développement et déploiement. Un Data Scientist, par exemple, peut écrire son programme sur macOS et l'exécuter via Docker sur l'infrastructure IT du client, quel que soit son OS (macOS, linux, Windows, …).

Docker introduit différentes notions :
• Dockerfile : un ensemble d'actions exécutées avec mise en cache dans l'ordre écrit
• Docker image : le système de fichiers et d'installation (issu du Dockerfile) dont vous aurez besoin pour exécuter votre programme, sans processus attaché
• Docker container : une instance de l'image hébergeant une copie de tous les fichiers et programmes nécessaires, avec un processus attaché et interactif à travers un terminal
• .dockerignore : un fichier listant tous les chemins des éléments que vous ne voulez pas héberger dans votre image Docker
• entrypoint.sh : un fichier cmd qui détermine les commandes à lancer lors de l'exécution du container
Docker fournit également Dockerhub, un service cloud capable d'héberger des images Docker partagées qui peuvent être push et pull par les équipes. Il héberge également des images officielles de langages, comme celle de Python.
Setup
Vous pouvez installer Docker depuis son site officiel en sélectionnant le système d'exploitation installé sur votre machine.
Une fois l'installation terminée, lancez Docker et exécutez la ligne de commande suivante pour vérifier que tout est OK :
docker run hello-world
D'un projet AI à une image Docker
Setup
La « dockerization » de votre projet AI intervient une fois les développements stabilisés, en supposant qu'ils soient écrits en Python.
Les fichiers Dockerfile, .dockerignore et entrypoint.sh sont placés à la racine du repository comme suit (voir mon précédent article sur l'organisation de projet) :

+On définit le fichier entrypoint.sh comme suit :
#!/bin/bash
set -xe
case $1 in
test)
python -m pytest -v --tb=line tests/unit_testing.py
action_1)
python app.py fonction_1;;
action_2)
python app.py fonction_2;;
esac+Pour créer le Dockerfile 🐳 :
- Choisissez la version de Python depuis Dockerhub, par exemple python:3.7-slim-buster avec l'OS debian :
FROM python:3.7-slim-buster- Installez les modules nécessaires via apt-get (liblept5 par exemple) :
RUN apt-get update -y\
&& apt-get install -y liblept5- Créez le dossier data/ qui sera le working directory :
RUN mkdir /data
WORKDIR /data- On copie tous les fichiers et dossiers du repository local dans le dossier /data de l'image Docker, à l'exception de ceux mentionnés dans .dockerignore :
ADD . /data- On installe Virtualenv, on crée project_vir_env, on l'active, puis on installe tous les requirements Python dedans :
RUN pip install virtualenv
RUN virtualenv project_vir_env
RUN . project_vir_env/bin/activate
RUN pip install -r packages.txt- On ajoute les droits d'exécution au fichier entrypoint et on le définit comme tel :
RUN chmod +x /data/entrypoint.sh
ENTRYPOINT ["data/entrypoint.sh"]Le Dockerfile est le suivant :
FROM python:3.7-slim-buster
RUN apt-get update -y \
&& apt-get install -y liblept5 \
RUN mkdir /data
WORKDIR /data
ADD . /data
RUN pip install virtualenv
RUN virtualenv project_vir_env
RUN . project_vir_env/bin/activate
RUN pip install -r packages.txt
RUN chmod +x /data/entrypoint.sh
ENTRYPOINT [ "/data/entrypoint.sh"]+On définit également .dockerignore :
project_vir_env/
notebooks/
.vscode/
**/__pycache__/
.DS_StoreLancement
Pour build l'image Docker, lancez d'abord le docker engine puis exécutez la ligne de commande suivante :
docker build -t nameofdockerimage .- -t : sert à nommer l'image
- . : indique l'emplacement du Dockerfile (dossier courant)
Vous pouvez exécuter un container en utilisant la ligne de commande suivante :
docker run -v $PWD:/data/ nameofdockerimage entrypointfunction- -v : sert à définir le volume et à lier votre dossier courant (repository du projet) au dossier data/ de votre container
- nameofdockerimage : identique à celui utilisé lors du build
- entrypointfunction : l'une des fonctions définies dans entrypoint.sh (test, action_1 ou action_2)
Vous pouvez consulter cette cheat sheet pour les commandes Docker possibles.
Docker compose
Lorsqu'on travaille sur un projet complexe, le produit final peut être composé de différents services, par exemple un frontend, une DS API et une base de données. Docker-compose est un orchestrateur Docker qui vous aide à « dockeriser » et à organiser tous vos services dans un format multi-containers.
À titre d'illustration, nous considérerons une application Python avec un frontend Streamlit qui appelle une API Flask pour calculer la somme de deux nombres. La structure est la suivante :

La structure du projet est la suivante :

Chaque service possède son propre Dockerfile :
- Dockerfile de la Data Science API :
FROM python:3.7-slim-buster
RUN mkdir /ds_api
WORKDIR /ds_api
ADD . /ds_api
RUN pip install virtualenv
RUN virtualenv api_vir_env
RUN . api_vir_env/bin/activate
RUN pip install -r requirements.txt
EXPOSE 8080
ENTRYPOINT ["python"]
CMD ["app.py"]
- Dockerfile du frontend :
FROM python:3.7-slim-buster
RUN mkdir /frontend_dir
WORKDIR /frontend_dir
ADD . /frontend_dir
RUN pip install virtualenv
RUN virtualenv front_vir_env
RUN . front_vir_env/bin/activate
RUN pip install -r requirements.txt
EXPOSE 8501
ENTRYPOINT ["streamlit", "run"]
CMD ["app.py"]La DS API est exposée sur le port 8080 tandis que le frontend est exposé sur le port 8501
Pour gérer les deux services en même temps, nous devrons créer un fichier docker-compose.yml 🐳 :
- On commence par définir la version de docker-compose
- Ensuite on définit les services de notre app qui seront lancés dans des containers différents. Au sein de chaque service on définit :
+ le container name
+ le hostname
+ le build (dossier et nom du Dockerfile du service)
+ les ports (d'exposition) : machine_port:container_port
+ restart (approche)
version: '3'
services:
datascience_api:
container_name: datascience_api
hostname: datascience_api
build:
context: ./datascience_api
dockerfile: Dockerfile
ports:
- 8080:8080
restart: unless-stopped
front:
container_name: frontend
hostname: frontend
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- 8501:8501
restart: unless-stopped
depends_on:
- datascience_apiNB1 : Le service frontend depends_on la datascience API, qui doit être exécutée en premier.
NB2 : Le frontend appelle la DS API en utilisant le nom de son container via le protocole http :
requests.post(“http://datascience_api:8080/add", params=query).json()Une fois le fichier docker-compose terminé, on exécute les lignes de commande suivantes à la racine du projet :
docker-compose build

docker-compose up
Les containers en cours d'exécution peuvent être visualisés avec la ligne de commande suivante :
docker-compose ps
Puisque les mêmes ports ont été utilisés à la fois pour le container et la machine locale, on peut ouvrir le frontend dans le navigateur de notre machine :

Vous pouvez retrouver tous les scripts dans le repository github.
Bonnes pratiques
- Figez les versions de toutes les images pullées (python, java) ; utiliser la latest peut rendre l'environnement de production instable
- Lancez un premier container avec un entrypoint de test pour vérifier que toutes les installations se sont déroulées correctement
- Mappez le container Docker avec un volume afin de pouvoir faire des développements en temps réel sans avoir à rebuild à chaque changement
- Dockerisez chaque service séparément pour faciliter le développement et le debugging
Conclusion
J'espère que vous avez apprécié la lecture de cet article et que vous avez acquis une première expérience pratique de Docker. Il vous permettra de déployer votre produit plus rapidement et plus efficacement, où que les développements aient été réalisés.
N'hésitez pas à consulter mes précédents articles du DS Starter pack :
- Must-have tools in Data Science projects
- All you need to know about Git, GitHub & GitLab
