CSC 8613 – Systèmes pour le machine learning

Portail informatique

CI1 : Conteneurs

Ce premier TP a pour objectifs de :
  • Comprendre pourquoi la conteneurisation est essentielle dans un système de Machine Learning.
  • Installer et vérifier le bon fonctionnement de Docker sur votre machine.
  • Construire une première image Docker contenant une mini-API FastAPI.
  • Exécuter un conteneur et exposer un service sur votre machine.
  • Démarrer un mini-système multi-conteneurs avec Docker Compose (API + base de données).
  • Apprendre à interagir avec des conteneurs (logs, exécution interactive, connexions).
  • Préparer l’environnement qui servira de base aux TP suivants.
Dans ce cours, tous les composants du système de Machine Learning (API, base de données, orchestrateur, Feature Store, monitoring, etc.) seront exécutés dans des conteneurs Docker. Il est donc essentiel de bien comprendre les bases avant de passer à l’ingestion de données et aux pipelines.

Installation de Docker et vérification de l’environnement

Dans cette première partie, vous allez installer Docker sur votre machine et vérifier que l’exécution de conteneurs fonctionne correctement.

Selon votre système d’exploitation, les instructions peuvent varier légèrement. Si vous travaillez sous Windows, l’installation de WSL2 est fortement recommandée pour simplifier l’utilisation de Docker.

Installez Docker Desktop (Windows / macOS) ou Docker Engine (Linux) en suivant la documentation officielle :
https://docs.docker.com/get-docker/
Si vous êtes sous Windows, assurez-vous que :
  • Le composant WSL2 est activé.
  • Votre distribution Linux par défaut est bien configurée.
  • Docker Desktop utilise le backend WSL2 (paramètres → General → Use the WSL 2 based engine).

Vérifiez votre installation en exécutant la commande suivante dans un terminal :
docker run hello-world
Notez dans votre rapport une capture d’écran montrant que le conteneur s’est exécuté correctement.

Listez maintenant les conteneurs présents sur votre machine (en cours d'exécution ou arrêtés) :
docker ps -a
Expliquez brièvement dans votre rapport ce que représente cette liste.
Si la commande docker run hello-world échoue, vérifiez :
  • Que Docker Desktop est lancé.
  • Que vous avez les permissions nécessaires (Linux : ajoutez votre utilisateur au groupe docker).
  • Qu’aucun proxy ou VPN ne bloque l’accès à Docker Hub.

Premiers pas avec Docker : images et conteneurs

Dans cet exercice, vous allez découvrir les commandes Docker fondamentales en manipulant des conteneurs simples.

Docker distingue les images (modèles figés contenant un environnement complet) et les conteneurs (instances actives d’une image). Vous manipulerez les deux tout au long du cours.

Expliquez en quelques phrases la différence entre une image Docker et un conteneur Docker. Cette réponse devra apparaître dans votre rapport final.

Exécutez un conteneur très léger basé sur l’image alpine et affichez un message dans la console :
docker run alpine echo "Bonjour depuis un conteneur Alpine"
Que se passe-t-il après l'exécution de cette commande ? Expliquez brièvement dans votre rapport.

Listez à nouveau les conteneurs présents sur votre machine :
docker ps -a
Vous devriez voir un conteneur alpine avec un statut Exited. Expliquez pourquoi dans votre rapport.

Lancez un conteneur interactif basé sur Alpine :
docker run -it alpine sh
À l’intérieur du conteneur, tapez les commandes suivantes :
ls uname -a exit
Indiquez dans votre rapport ce que vous observez.
Pour relancer un conteneur arrêté, utilisez :
docker start <container_id>
Pour entrer dans un conteneur en cours d’exécution :
docker exec -it <container_id> sh

Construire une première image Docker avec une mini-API FastAPI

Dans cet exercice, vous allez construire votre première image Docker à partir d’un petit service web écrit avec FastAPI. L’objectif est de comprendre la structure d’un Dockerfile et de créer une API simple exposant une route /health.

FastAPI est un framework Python moderne utilisé dans de nombreux systèmes de Machine Learning pour exposer des modèles en production.

Étape 1 — Compléter le fichier app.py

On vous fournit ci-dessous un squelette de fichier app.py avec quelques éléments manquants. Complétez les zones indiquées.

Complétez le code afin que l’API expose une route /health qui renvoie un JSON {"status": "ok"}.
# app.py # TODO: importer FastAPI from ________ import ________ # TODO: créer une instance FastAPI app = ________() # TODO: définir une route GET /health _____________________ def health(): return {"status": "ok"}
Pour lancer cette API en local (hors conteneur), vous pourriez utiliser : uvicorn app:app --reload Mais dans ce TP, nous exécuterons uniquement l’API dans un conteneur Docker.

Étape 2 — Compléter le Dockerfile

Voici un Dockerfile partiellement rempli. Complétez les instructions manquantes pour :

  • Définir une image de base adaptée ;
  • Créer un répertoire de travail ;
  • Copier votre fichier app.py ;
  • Installer les dépendances requises (fastapi, uvicorn) ;
  • Démarrer l’application au lancement du conteneur.

Complétez les lignes marquées # TODO.
# Dockerfile # TODO: choisir une image de base Python FROM ____________ # TODO: définir le répertoire de travail dans le conteneur WORKDIR ____________ # TODO: copier le fichier app.py COPY ____________ ____________ # Installer FastAPI et Uvicorn RUN pip install fastapi uvicorn # TODO: lancer le serveur au démarrage du conteneur ___________ ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
La commande CMD définit le processus principal du conteneur. Le port 8000 sera exposé vers votre machine lors de l'exécution.

Étape 3 — Construire l'image Docker

Construisez maintenant l’image Docker à partir du Dockerfile avec la commande suivante :
docker build -t simple-api .
Indiquez dans votre rapport que la construction s’est bien déroulée (capture d’écran recommandée).

Exécuter l’API FastAPI dans un conteneur Docker

Vous disposez maintenant d’une image Docker simple-api contenant une mini-API FastAPI. Dans cet exercice, vous allez exécuter cette image dans un conteneur et vérifier que l’API répond correctement sur votre machine.

Étape 1 — Lancer le conteneur

Lancez un conteneur à partir de l’image simple-api en exposant le port 8000 du conteneur sur le port 8000de votre machine. Utilisez la commande suivante :
docker run -p 8000:8000 simple-api
Expliquez dans votre rapport le rôle de l’option -p 8000:8000.
Si vous obtenez un message d’erreur du type address already in use, cela signifie qu’un autre processus utilise déjà le port 8000 sur votre machine. Vous pouvez soit arrêter ce processus, soit choisir un autre port, par exemple :
docker run -p 8080:8000 simple-api
Et accéder ensuite à l’API via http://localhost:8080.

Étape 2 — Tester l’endpoint /health

Dans un autre terminal (ou via votre navigateur), appelez l’endpoint /health de l’API :
curl http://localhost:8000/health
ou bien en ouvrant http://localhost:8000/health dans un navigateur. Vérifiez que la réponse JSON correspond à ce qui était attendu.
Ajoutez une capture d’écran de la réponse dans votre rapport.

Étape 3 — Observer les conteneurs en cours d’exécution

Dans un autre terminal, affichez la liste des conteneurs en cours d’exécution :
docker ps
Identifiez la ligne correspondant au conteneur simple-api et notez dans votre rapport :
  • le nom du conteneur,
  • l'image utilisée,
  • le port mappé.

Étape 4 — Arrêter le conteneur

Arrêtez le conteneur en cours d’exécution depuis un autre terminal à l’aide de la commande :
docker stop <nom_ou_id_du_conteneur>
Puis vérifiez qu’il n’apparaît plus dans docker ps, mais qu’il est toujours visible dans docker ps -a. Expliquez brièvement la différence entre ces deux commandes dans votre rapport.
Pour relancer un conteneur arrêté sans recréer un nouveau conteneur, vous pouvez utiliser :
docker start <nom_ou_id_du_conteneur>

Démarrer un mini-système multi-conteneurs avec Docker Compose

Dans cette partie, vous allez utiliser Docker Compose pour lancer plusieurs services en une seule commande : une API FastAPI (celle du TP) et une base de données PostgreSQL.

Docker Compose permet de décrire l’architecture d’une application (services, réseaux, volumes, variables d’environnement, etc.) dans un fichier unique docker-compose.yml. Cela vous permet de reproduire facilement un environnement complet, ce qui est un prérequis pour les systèmes de Machine Learning.

Étape 1 — Préparer la structure des fichiers

Organisez votre répertoire de travail de la façon suivante :
. ├── api/ │ ├── app.py # votre fichier FastAPI │ └── Dockerfile # votre Dockerfile pour l'API └── docker-compose.yml # à créer à la racine
Vérifiez que l'image simple-api peut toujours être construite depuis le répertoire api/ avec :
cd api docker build -t simple-api .

Étape 2 — Compléter le fichier docker-compose.yml

On vous propose ci-dessous un squelette de fichier docker-compose.yml incomplet. Votre objectif est de compléter les parties marquées # TODO pour :

  • Définir un service db basé sur l’image officielle postgres:16 ;
  • Définir un service api qui utilise le Dockerfile du dossier api/ ;
  • Configurer les variables d’environnement pour PostgreSQL (utilisateur, mot de passe, base, tous égaux à demo) ;
  • Exposer les ports nécessaires pour l’API et la base de données ;
  • Spécifier que l’API dépend de la base de données.

Complétez le fichier docker-compose.yml ci-dessous.
version: "3.9" services: db: image: postgres:16 environment: # TODO: définir l'utilisateur, le mot de passe et le nom de la base POSTGRES_USER: _______ POSTGRES_PASSWORD: _______ POSTGRES_DB: _______ ports: # TODO: exposer le port PostgreSQL vers l'hôte - "____:5432" api: # TODO: construire l'image à partir du Dockerfile dans ./api build: _______ ports: # TODO: exposer le port 8000 du conteneur vers l'hôte - "____:8000" depends_on: # TODO: indiquer que l'API dépend de la base de données - ____
  • Le service db sera accessible depuis d'autres conteneurs via le hostname db et le port 5432.
  • Le service api sera accessible depuis votre machine via http://localhost:8000.

Étape 3 — Démarrer la stack avec Docker Compose

À la racine de votre projet (là où se trouve docker-compose.yml), lancez les services en arrière-plan :
docker compose up -d
Puis affichez la liste des services gérés par Docker Compose :
docker compose ps
Vérifiez dans votre rapport que les services db et api sont bien démarrés (capture d’écran recommandée).

Étape 4 — Tester à nouveau l’endpoint /health

Vérifiez que l’endpoint /health de l’API est toujours accessible, cette fois-ci lorsque l’API est lancée via Docker Compose :
curl http://localhost:8000/health
ou via votre navigateur. Ajoutez une capture d’écran dans votre rapport.

Étape 5 — Arrêter proprement les services

Lorsque vous avez terminé, arrêtez et supprimez les conteneurs gérés par Docker Compose :
docker compose down
Expliquez dans votre rapport la différence entre :
  • Arrêter les services avec docker compose down ;
  • Arrêter un conteneur individuel avec docker stop <id>.
Par défaut, docker compose down ne supprime pas les volumes nommés (données persistantes). Pour tout nettoyer, vous pouvez utiliser :
docker compose down -v
(à utiliser avec précaution).

Interagir avec la base de données PostgreSQL dans un conteneur

Dans un système de Machine Learning en production, les données sont presque toujours stockées dans une base de données ou un data warehouse. Dans ce TP, nous utilisons PostgreSQL, exécuté lui aussi dans un conteneur Docker.

L’objectif ici n’est pas d’apprendre SQL en détail, mais de vérifier que vous savez vous connecter à une base PostgreSQL exécutée dans un conteneur Docker Compose.

Pré-requis

Assurez-vous que votre stack Docker Compose est bien démarrée :

docker compose up -d docker compose ps
Vous devez voir au moins deux services Up : db et api.

Étape 1 — Se connecter au conteneur PostgreSQL

Utilisez la commande suivante pour ouvrir un shell psql à l’intérieur du conteneur PostgreSQL :
docker compose exec db psql -U demo -d demo
Expliquez dans votre rapport le rôle de chaque option (exec, db, -U, -d).

Étape 2 — Exécuter quelques commandes SQL simples

Une fois connecté à psql, exécutez les commandes suivantes :
SELECT version();
Puis :
SELECT current_database();
Notez dans votre rapport les résultats obtenus, et ajoutez une capture d’écran de la session psql.
Pour quitter psql, utilisez :
\q

Étape 3 — Comprendre la connexion depuis d'autres services

Dans votre rapport, expliquez comment un autre service Docker (par exemple l’API) pourrait se connecter à la base de données PostgreSQL. Précisez :
  • le hostname à utiliser ;
  • le port ;
  • l’utilisateur et le mot de passe ;
  • le nom de la base.

Étape 4 — Nettoyer

Après vos tests, vous pouvez arrêter la stack :
docker compose down
Si vous souhaitez également supprimer les volumes associés (données persistantes), vous pouvez utiliser :
docker compose down -v
Expliquez dans votre rapport la conséquence de l’option -v.

Déboguer des conteneurs Docker : commandes essentielles et bonnes pratiques

Le débogage est une compétence essentielle lorsque vous travaillez avec des systèmes distribués ou multi-conteneurs. Dans cet exercice, vous allez découvrir quelques outils simples mais indispensables pour diagnostiquer des problèmes dans vos services Docker.

Vous utiliserez ces commandes à chaque TP du module, que ce soit pour comprendre un échec d’ingestion, une API qui ne démarre pas, ou un modèle qui ne se charge plus.

Étape 1 — Afficher les logs d’un service

Affichez en continu les logs du service api exécuté par Docker Compose :
docker compose logs -f api
Relevez dans votre rapport ce que vous observez lorsque :
  • l’API démarre correctement ;
  • l’API reçoit une requête /health.

Étape 2 — Entrer dans un conteneur en cours d’exécution

Utilisez la commande ci-dessous pour ouvrir un shell sh dans le conteneur de l’API :
docker compose exec api sh
À l’intérieur du conteneur :
ls python --version exit
Expliquez dans votre rapport ce que vous observez.
Cette technique est extrêmement utile pour vérifier la présence de fichiers, d’environnements ou de dépendances dans un conteneur.

Étape 3 — Redémarrer un service

Redémarrez seulement le service api à l’aide de la commande suivante :
docker compose restart api
Vérifiez qu’après redémarrage, l’API est toujours accessible sur /health. Expliquez dans votre rapport dans quelles situations un redémarrage est utile.

Étape 4 — Conteneur qui ne démarre pas : diagnostic

Simulez un problème en introduisant volontairement une erreur dans votre fichier app.py (par exemple renommer app en appi), puis reconstruisez l’image :
docker build -t simple-api .
Relancez Docker Compose :
docker compose up -d --build
Observez les logs :
docker compose logs -f api
Expliquez dans votre rapport comment vous avez identifié la cause de l’erreur.
Rappel : modifier du code dans un conteneur en cours d’exécution ne change rien. Vous devez reconstruire l’image avec docker build ou docker compose up --build.

Étape 5 — Supprimer des conteneurs et images

Supprimez tous les conteneurs arrêtés :
docker container prune
Supprimez toutes les images inutilisées :
docker image prune
Expliquez dans votre rapport pourquoi il est utile de nettoyer régulièrement son environnement Docker.

Questions de réflexion et consignes pour le rendu

Pour conclure ce premier TP, vous devez répondre à quelques questions de réflexion et préparer un rendu conforme aux consignes ci-dessous.

Questions de réflexion

Expliquez pourquoi un notebook Jupyter n’est généralement pas adapté pour déployer un modèle de Machine Learning en production. Votre réponse doit faire référence à au moins deux aspects vus durant ce TP (ex : reproductibilité, environnement, automatisation...).

Expliquez pourquoi Docker Compose est un outil essentiel lorsque l’on manipule plusieurs services (API, base de données...). Référencez au moins un avantage observé lors du TP.

Consignes pour le rendu

Le rendu doit contenir le code complet que vous avez produit, ainsi qu’un rapport écrit répondant aux questions ci-dessus et incluant les captures d’écran demandées dans les exercices.

Créez un dépôt Git (privé ou public) contenant :
  • le répertoire api/ avec votre fichier app.py et votre Dockerfile ;
  • votre fichier docker-compose.yml ;
  • un dossier reports/ contenant un fichier rapport.md rédigé en Markdown.
Dans le fichier rapport_tp1.md, incluez :
  • les réponses aux questions de réflexion ;
  • les captures d’écran demandées dans les exercices ;
  • toutes remarques personnelles sur les difficultés rencontrées.

Envoyez le lien vers votre dépôt Git à votre enseignant au plus tard une semaine après la séance de TP.