commit 65d94c5719d72322c0b3d4a5cd13605faaf8aaf3 Author: Eric Coissac Date: Tue Oct 14 17:40:41 2025 +0200 First version of obijupyterhub diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..411013a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM jupyter/base-notebook:latest + +USER root + +# Installation de R et des dépendances système +RUN apt-get update && apt-get install -y \ + r-base \ + r-base-dev \ + libcurl4-openssl-dev \ + libssl-dev \ + libxml2-dev \ + texlive-xetex \ + texlive-fonts-recommended \ + texlive-plain-generic \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Installation du kernel R pour Jupyter (en tant que root) +RUN R -e "install.packages('IRkernel', repos='http://cran.rstudio.com/')" && \ + R -e "IRkernel::installspec(user = FALSE)" + +# Installation de quelques packages R utiles pour les TP +RUN R -e "install.packages(c('tidyverse','vegan','ade4'), repos='http://cran.rstudio.com/')" + +# Installation du kernel bash (en tant que root aussi) +RUN pip install bash_kernel && \ + python -m bash_kernel.install --sys-prefix + +# Créer les répertoires nécessaires avec les bonnes permissions +RUN mkdir -p /home/${NB_USER}/.local/share/jupyter && \ + chown -R ${NB_UID}:${NB_GID} /home/${NB_USER} + +USER ${NB_UID} + +WORKDIR /home/${NB_USER} \ No newline at end of file diff --git a/Dockerfile.hub b/Dockerfile.hub new file mode 100644 index 0000000..245f9db --- /dev/null +++ b/Dockerfile.hub @@ -0,0 +1,16 @@ +FROM jupyterhub/jupyterhub:latest + +# Installation de DockerSpawner +RUN pip install dockerspawner + +# Copie de la configuration +COPY jupyterhub_config.py /srv/jupyterhub/jupyterhub_config.py + +# Port exposé +EXPOSE 8000 + +# Répertoire de travail +WORKDIR /srv/jupyterhub + +# Commande de démarrage +CMD ["jupyterhub", "-f", "/srv/jupyterhub/jupyterhub_config.py"] \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..8336e50 --- /dev/null +++ b/Readme.md @@ -0,0 +1,258 @@ +# Configuration JupyterHub avec OrbStack sur Mac (tout en Docker) + +## Prérequis +- OrbStack installé et démarré + +## Structure des fichiers + +Votre dossier `~/jupyterhub-tp` doit contenir : +``` +~/jupyterhub-tp/ +├── Dockerfile # Image pour les étudiants (déjà créée) +├── Dockerfile.hub # Image pour JupyterHub (nouvelle) +├── jupyterhub_config.py # Configuration +└── docker-compose.yml # Orchestration +``` + +## Étapes d'installation + +### 1. Créer la structure de dossiers + +```bash +mkdir -p ~/jupyterhub-tp +cd ~/jupyterhub-tp +``` + +### 2. Créer tous les fichiers nécessaires + +Créez les fichiers suivants avec le contenu des artifacts : +- `Dockerfile` (artifact "Dockerfile pour JupyterHub avec R et Bash") +- `Dockerfile.hub` (artifact "Dockerfile pour le container JupyterHub") +- `jupyterhub_config.py` (artifact "Configuration JupyterHub") +- `docker-compose.yml` (artifact "docker-compose.yml") + +### 3. Construire les images Docker + +```bash +# Image pour les étudiants +docker build -t jupyterhub-student:latest -f Dockerfile . + +# Image pour le hub JupyterHub +docker build -t jupyterhub-hub:latest -f Dockerfile.hub . +``` + +### 4. Démarrer JupyterHub avec Docker Compose + +```bash +docker-compose up -d +``` + +### 5. Accéder à JupyterHub + +Ouvrez votre navigateur et allez à : **http://localhost:8000** + +Vous pouvez vous connecter avec n'importe quel nom d'utilisateur. + +## Commandes utiles + +### Voir les logs de JupyterHub +```bash +docker-compose logs -f jupyterhub +``` + +### Voir tous les containers (hub + étudiants) +```bash +docker ps +``` + +### Arrêter JupyterHub +```bash +docker-compose down +``` + +### Redémarrer JupyterHub (après modification du config) +```bash +docker-compose restart jupyterhub +``` + +### Reconstruire après modification du Dockerfile +```bash +# Pour l'image étudiants +docker build -t jupyterhub-student:latest -f Dockerfile . +docker-compose restart jupyterhub + +# Pour l'image hub +docker-compose up -d --build +``` + +### Voir les logs d'un étudiant spécifique +```bash +docker logs jupyter-nom_utilisateur +``` + +### Nettoyer après le TP +```bash +# Arrêter et supprimer tous les containers +docker-compose down + +# Supprimer les containers étudiants +docker ps -a | grep jupyter- | awk '{print $1}' | xargs docker rm -f + +# Supprimer les volumes (ATTENTION : supprime les données étudiants) +docker volume ls | grep jupyterhub-user | awk '{print $2}' | xargs docker volume rm + +# Tout nettoyer (containers + volumes + réseau) +docker-compose down -v +docker ps -a | grep jupyter- | awk '{print $1}' | xargs docker rm -f +docker volume prune -f +``` + +## Gestion des données partagées + +### Structure des dossiers pour chaque étudiant + +Chaque étudiant verra ces dossiers dans son JupyterLab : +- **`work/`** : Son espace personnel (persistant, privé) +- **`shared/`** : Espace partagé entre tous les étudiants (lecture/écriture) +- **`course/`** : Fichiers du cours (lecture seule, vous déposez les fichiers) + +### Déposer des fichiers pour le cours + +Pour mettre des fichiers dans le dossier `course/` (accessible en lecture seule) : + +```bash +# Créer un dossier temporaire +mkdir -p ~/jupyterhub-tp/course-files + +# Copier vos fichiers dedans +cp mes_notebooks.ipynb ~/jupyterhub-tp/course-files/ +cp mes_donnees.csv ~/jupyterhub-tp/course-files/ + +# Copier dans le volume Docker +docker run --rm \ + -v jupyterhub-course:/target \ + -v ~/jupyterhub-tp/course-files:/source \ + alpine sh -c "cp -r /source/* /target/" +``` + +### Accéder aux fichiers partagés entre étudiants + +Les étudiants peuvent collaborer via le dossier `shared/` : + +```python +# Dans un notebook, pour lire un fichier partagé +import pandas as pd +df = pd.read_csv('/home/jovyan/shared/donnees_groupe.csv') + +# Pour écrire un fichier partagé +df.to_csv('/home/jovyan/shared/resultats_alice.csv') +``` + +### Récupérer les travaux des étudiants + +```bash +# Lister les volumes utilisateurs +docker volume ls | grep jupyterhub-user + +# Copier les fichiers d'un étudiant spécifique +docker run --rm \ + -v jupyterhub-user-alice:/source \ + -v ~/rendus:/target \ + alpine sh -c "cp -r /source/* /target/alice/" + +# Copier tous les travaux partagés +docker run --rm \ + -v jupyterhub-shared:/source \ + -v ~/rendus/shared:/target \ + alpine sh -c "cp -r /source/* /target/" +``` + +## Gestion des utilisateurs + +### Option 1 : Liste d'utilisateurs prédéfinis +Dans `jupyterhub_config.py`, décommentez et modifiez : +```python +c.Authenticator.allowed_users = {'etudiant1', 'etudiant2', 'etudiant3'} +``` + +### Option 2 : Autoriser tout le monde (pour tests) +Par défaut, la configuration autorise n'importe quel utilisateur : +```python +c.Authenticator.allow_all = True +``` + +⚠️ **Attention** : DummyAuthenticator est UNIQUEMENT pour les tests locaux ! + +## Vérification des kernels + +Une fois connecté, créez un nouveau notebook et vérifiez que vous avez accès à : +- **Python 3** (kernel par défaut) +- **R** (kernel R) +- **Bash** (kernel bash) + +## Personnalisation pour vos TP + +### Ajouter des packages R supplémentaires +Modifiez le `Dockerfile` (avant `USER ${NB_UID}`) : +```dockerfile +RUN R -e "install.packages(c('votre_package'), repos='http://cran.rstudio.com/')" +``` + +Puis reconstruisez : +```bash +docker build -t jupyterhub-student:latest -f Dockerfile . +docker-compose restart jupyterhub +``` + +### Ajouter des packages Python +Ajoutez dans le `Dockerfile` (avant `USER ${NB_UID}`) : +```dockerfile +RUN pip install numpy pandas matplotlib seaborn +``` + +### Distribuer des fichiers aux étudiants +Créez un dossier `files_tp/` et ajoutez dans le `Dockerfile` : +```dockerfile +COPY files_tp/ /home/${NB_USER}/tp/ +RUN chown -R ${NB_UID}:${NB_GID} /home/${NB_USER}/tp +``` + +### Changer le port (si 8000 est occupé) +Modifiez dans `docker-compose.yml` : +```yaml +ports: + - "8001:8000" # Accessible sur localhost:8001 +``` + +## Avantages de cette approche + +✅ **Tout en Docker** : Plus besoin d'installer Python/JupyterHub sur votre Mac +✅ **Portable** : Facile à déployer sur un autre Mac ou serveur +✅ **Isolé** : Pas de pollution de votre environnement système +✅ **Facile à nettoyer** : Un simple `docker-compose down` suffit +✅ **Reproductible** : Les étudiants auront exactement le même environnement + +## Dépannage + +**Erreur "Cannot connect to Docker daemon"** : +- Vérifiez qu'OrbStack est démarré +- Vérifiez que le socket existe : `ls -la /var/run/docker.sock` + +**Les containers étudiants ne démarrent pas** : +- Vérifiez les logs : `docker-compose logs jupyterhub` +- Vérifiez que l'image étudiants existe : `docker images | grep jupyterhub-student` + +**Port 8000 déjà utilisé** : +- Changez le port dans `docker-compose.yml` + +**Après modification du config, les changements ne sont pas pris en compte** : +```bash +docker-compose restart jupyterhub +``` + +**Je veux repartir de zéro** : +```bash +docker-compose down -v +docker rmi jupyterhub-hub jupyterhub-student +# Puis reconstruire tout +``` \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..faaedd7 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,34 @@ +services: + jupyterhub: + build: + context: . + dockerfile: Dockerfile.hub + container_name: jupyterhub + image: jupyterhub-hub:latest + ports: + - "8888:8000" + volumes: + # Accès au socket Docker pour spawner les containers étudiants + - /var/run/docker.sock:/var/run/docker.sock + # Persistance de la base de données JupyterHub + - jupyterhub-data:/srv/jupyterhub + # Montage du fichier de config en direct (pour modifications faciles) + - ./jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py:ro + networks: + - jupyterhub-network + restart: unless-stopped + environment: + # Mot de passe partagé pour tous les étudiants + JUPYTERHUB_PASSWORD: metabar2025 + # Variables d'environnement optionnelles + DOCKER_NOTEBOOK_DIR: /home/jovyan/work + +networks: + jupyterhub-network: + name: jupyterhub-network + driver: bridge + +volumes: + jupyterhub-data: + jupyterhub-shared: + jupyterhub-course: \ No newline at end of file diff --git a/jupyterhub_config.py b/jupyterhub_config.py new file mode 100644 index 0000000..d8fbc75 --- /dev/null +++ b/jupyterhub_config.py @@ -0,0 +1,91 @@ +import os + +# Configuration de base +c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner' + +# Activer les logs de debug +c.JupyterHub.log_level = 'DEBUG' +c.Spawner.debug = True + +# Image Docker à utiliser pour les containers étudiants +c.DockerSpawner.image = 'jupyterhub-student:latest' + +# Réseau Docker (créez-le avec: docker network create jupyterhub-network) +c.DockerSpawner.network_name = 'jupyterhub-network' + +# Connexion au socket Docker d'OrbStack depuis le container hub +c.DockerSpawner.client_kwargs = {'base_url': 'unix:///var/run/docker.sock'} + +# IMPORTANT : URL interne pour communiquer entre containers +# Le hub container communique avec les user containers via le réseau Docker +c.JupyterHub.hub_ip = '0.0.0.0' +c.JupyterHub.hub_connect_ip = 'jupyterhub' + +# Configuration réseau pour les containers étudiants +c.DockerSpawner.use_internal_ip = True +c.DockerSpawner.network_name = 'jupyterhub-network' +c.DockerSpawner.extra_host_config = {'network_mode': 'jupyterhub-network'} + +# Supprimer les containers après déconnexion (optionnel, mettre False pour garder les containers) +c.DockerSpawner.remove = True + +# Nommage des containers +c.DockerSpawner.name_template = "jupyter-{username}" + +# Montage de volumes pour persister les données des étudiants +# Définir la racine à /home/jovyan pour voir tous les dossiers +notebook_dir = '/home/jovyan' +c.DockerSpawner.notebook_dir = notebook_dir + +# Volume personnel pour chaque étudiant + volume partagé +c.DockerSpawner.volumes = { + # Volume personnel (persistant) - monté dans work/ + 'jupyterhub-user-{username}': '/home/jovyan/work', + # Volume partagé entre tous les étudiants + 'jupyterhub-shared': '/home/jovyan/shared', + # Volume partagé en lecture seule pour les fichiers du cours (optionnel) + 'jupyterhub-course': { + 'bind': '/home/jovyan/course', + 'mode': 'ro' # read-only + } +} + +# Configuration de la mémoire et CPU (ajustez selon vos besoins) +c.DockerSpawner.mem_limit = '2G' +c.DockerSpawner.cpu_limit = 1.0 + +# Configuration des utilisateurs - Mot de passe simple pour TP +from jupyterhub.auth import DummyAuthenticator + +class SimplePasswordAuthenticator(DummyAuthenticator): + """Authentificateur simple avec un mot de passe partagé pour tous""" + + def check_allowed(self, username, authentication=None): + """Vérifie si l'utilisateur est autorisé""" + if authentication is None: + return False + + # Récupérer le mot de passe depuis la variable d'environnement + expected_password = os.environ.get('JUPYTERHUB_PASSWORD', 'metabar2025') + provided_password = authentication.get('password', '') + + # Vérifier le mot de passe + return provided_password == expected_password + +c.JupyterHub.authenticator_class = SimplePasswordAuthenticator + +# Pour créer une liste d'utilisateurs autorisés, décommentez et modifiez: +# c.Authenticator.allowed_users = {'etudiant1', 'etudiant2', 'etudiant3'} + +# Ou autoriser n'importe quel utilisateur avec le bon mot de passe: +c.Authenticator.allow_all = True + +# Configuration admin +c.Authenticator.admin_users = {'admin'} + +# Port d'écoute +c.JupyterHub.bind_url = 'http://0.0.0.0:8000' + +# Timeout +c.Spawner.start_timeout = 300 +c.Spawner.http_timeout = 120 \ No newline at end of file diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..5f2fb65 --- /dev/null +++ b/setup.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -e + +# === Variables === +WORKDIR="$PWD" +NETWORK="jupyterhub-net" +HUB_IMAGE="jupyterhub-hub" +USER_IMAGE="jupyter-tp-singleuser" + +# === Préparation === +mkdir -p "$WORKDIR" +cd "$WORKDIR" + +echo "[1/5] Création du réseau Docker..." +docker network inspect $NETWORK >/dev/null 2>&1 || docker network create $NETWORK + +echo "[2/5] Construction des images..." +docker build -t $USER_IMAGE -f Dockerfile . +docker build -t $HUB_IMAGE -f Dockerfile.hub . + +echo "[3/5] Lancement de JupyterHub..." +docker compose up -d + +echo "[4/5] Hub accessible sur http://localhost:8888" +echo " Login avec n'importe quel nom et mot de passe : metabar2025" + +echo "[5/5] Pour voir les utilisateurs actifs :" +echo " docker ps | grep jupyterhub-user" + +echo "Terminé."