First version of obijupyterhub
This commit is contained in:
34
Dockerfile
Normal file
34
Dockerfile
Normal file
@@ -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}
|
||||||
16
Dockerfile.hub
Normal file
16
Dockerfile.hub
Normal file
@@ -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"]
|
||||||
258
Readme.md
Normal file
258
Readme.md
Normal file
@@ -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
|
||||||
|
```
|
||||||
34
docker-compose.yaml
Normal file
34
docker-compose.yaml
Normal file
@@ -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:
|
||||||
91
jupyterhub_config.py
Normal file
91
jupyterhub_config.py
Normal file
@@ -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
|
||||||
30
setup.sh
Normal file
30
setup.sh
Normal file
@@ -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é."
|
||||||
Reference in New Issue
Block a user