diff --git a/Dockerfile b/Dockerfile index 411013a..039bd7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,30 +2,27 @@ FROM jupyter/base-notebook:latest USER root -# Installation de R et des dépendances système +# Install R and system dependencies 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) +# Install R kernel for Jupyter (as 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/')" +# Install some useful R packages for labs +RUN R -e "install.packages(c('ggplot2', 'dplyr', 'tidyr', 'readr'), repos='http://cran.rstudio.com/')" -# Installation du kernel bash (en tant que root aussi) +# Install bash kernel (as root also) RUN pip install bash_kernel && \ python -m bash_kernel.install --sys-prefix -# Créer les répertoires nécessaires avec les bonnes permissions +# Create necessary directories with proper permissions RUN mkdir -p /home/${NB_USER}/.local/share/jupyter && \ chown -R ${NB_UID}:${NB_GID} /home/${NB_USER} diff --git a/Dockerfile.hub b/Dockerfile.hub index 245f9db..319fca6 100644 --- a/Dockerfile.hub +++ b/Dockerfile.hub @@ -1,16 +1,16 @@ FROM jupyterhub/jupyterhub:latest -# Installation de DockerSpawner +# Install DockerSpawner RUN pip install dockerspawner -# Copie de la configuration +# Copy configuration COPY jupyterhub_config.py /srv/jupyterhub/jupyterhub_config.py -# Port exposé +# Expose port EXPOSE 8000 -# Répertoire de travail +# Working directory WORKDIR /srv/jupyterhub -# Commande de démarrage +# Startup command CMD ["jupyterhub", "-f", "/srv/jupyterhub/jupyterhub_config.py"] \ No newline at end of file diff --git a/Readme.md b/Readme.md index 8336e50..0250b0f 100644 --- a/Readme.md +++ b/Readme.md @@ -1,258 +1,257 @@ -# Configuration JupyterHub avec OrbStack sur Mac (tout en Docker) +# JupyterHub Configuration with OrbStack on Mac (all in Docker) -## Prérequis -- OrbStack installé et démarré +## Prerequisites +- OrbStack installed and running -## Structure des fichiers +## File Structure -Votre dossier `~/jupyterhub-tp` doit contenir : +Your `~/jupyterhub-tp` directory should contain: ``` ~/jupyterhub-tp/ -├── Dockerfile # Image pour les étudiants (déjà créée) -├── Dockerfile.hub # Image pour JupyterHub (nouvelle) +├── Dockerfile # Image for students (already created) +├── Dockerfile.hub # Image for JupyterHub (new) ├── jupyterhub_config.py # Configuration -└── docker-compose.yml # Orchestration +├── docker-compose.yml # Orchestration +└── start-jupyterhub.sh # Startup script ``` -## Étapes d'installation +## Installation Steps -### 1. Créer la structure de dossiers +### 1. Create Directory Structure ```bash mkdir -p ~/jupyterhub-tp cd ~/jupyterhub-tp ``` -### 2. Créer tous les fichiers nécessaires +### 2. Create All Necessary Files -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") +Create the following files with the content from artifacts: +- `Dockerfile` (artifact "Dockerfile for JupyterHub with R and Bash") +- `Dockerfile.hub` (artifact "Dockerfile for JupyterHub container") +- `jupyterhub_config.py` (artifact "JupyterHub Configuration") - `docker-compose.yml` (artifact "docker-compose.yml") +- `start-jupyterhub.sh` (artifact "start-jupyterhub.sh") -### 3. Construire les images Docker +### 3. Make Startup Script Executable ```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 . +chmod +x start-jupyterhub.sh ``` -### 4. Démarrer JupyterHub avec Docker Compose +### 4. Start JupyterHub ```bash -docker-compose up -d +./start-jupyterhub.sh ``` -### 5. Accéder à JupyterHub +### 5. Access JupyterHub -Ouvrez votre navigateur et allez à : **http://localhost:8000** +Open your browser and go to: **http://localhost:8000** -Vous pouvez vous connecter avec n'importe quel nom d'utilisateur. +You can log in with any username and password: `metabar2025` -## Commandes utiles +## Useful Commands -### Voir les logs de JupyterHub +### View JupyterHub logs ```bash docker-compose logs -f jupyterhub ``` -### Voir tous les containers (hub + étudiants) +### View all containers (hub + students) ```bash docker ps ``` -### Arrêter JupyterHub +### Stop JupyterHub ```bash docker-compose down ``` -### Redémarrer JupyterHub (après modification du config) +### Restart JupyterHub (after config modification) ```bash docker-compose restart jupyterhub ``` -### Reconstruire après modification du Dockerfile +### Rebuild after Dockerfile modification ```bash -# Pour l'image étudiants +# For student image docker build -t jupyterhub-student:latest -f Dockerfile . docker-compose restart jupyterhub -# Pour l'image hub +# For hub image docker-compose up -d --build ``` -### Voir les logs d'un étudiant spécifique +### View logs for a specific student ```bash -docker logs jupyter-nom_utilisateur +docker logs jupyter-username ``` -### Nettoyer après le TP +### Clean up after lab ```bash -# Arrêter et supprimer tous les containers +# Stop and remove all containers docker-compose down -# Supprimer les containers étudiants +# Remove student containers docker ps -a | grep jupyter- | awk '{print $1}' | xargs docker rm -f -# Supprimer les volumes (ATTENTION : supprime les données étudiants) +# Remove volumes (WARNING: deletes student data) docker volume ls | grep jupyterhub-user | awk '{print $2}' | xargs docker volume rm -# Tout nettoyer (containers + volumes + réseau) +# Clean everything (containers + volumes + network) 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 +## Managing Shared Data -### Structure des dossiers pour chaque étudiant +### Directory Structure for Each Student -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) +Each student will see these directories in their JupyterLab: +- **`work/`** : Personal workspace (persistent, private) +- **`shared/`** : Shared workspace between all students (read/write) +- **`course/`** : Course files (read-only, you deposit files) -### Déposer des fichiers pour le cours +### Deposit Files for Course -Pour mettre des fichiers dans le dossier `course/` (accessible en lecture seule) : +To put files in the `course/` directory (accessible read-only): ```bash -# Créer un dossier temporaire +# Create a temporary directory 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/ +# Copy your files into it +cp my_notebooks.ipynb ~/jupyterhub-tp/course-files/ +cp my_data.csv ~/jupyterhub-tp/course-files/ -# Copier dans le volume Docker +# Copy into Docker volume 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 +### Access Shared Files Between Students -Les étudiants peuvent collaborer via le dossier `shared/` : +Students can collaborate via the `shared/` directory: ```python -# Dans un notebook, pour lire un fichier partagé +# In a notebook, to read a shared file import pandas as pd -df = pd.read_csv('/home/jovyan/shared/donnees_groupe.csv') +df = pd.read_csv('/home/jovyan/shared/group_data.csv') -# Pour écrire un fichier partagé -df.to_csv('/home/jovyan/shared/resultats_alice.csv') +# To write a shared file +df.to_csv('/home/jovyan/shared/alice_results.csv') ``` -### Récupérer les travaux des étudiants +### Retrieve Student Work ```bash -# Lister les volumes utilisateurs +# List user volumes docker volume ls | grep jupyterhub-user -# Copier les fichiers d'un étudiant spécifique +# Copy files from a specific student docker run --rm \ -v jupyterhub-user-alice:/source \ - -v ~/rendus:/target \ + -v ~/submissions:/target \ alpine sh -c "cp -r /source/* /target/alice/" -# Copier tous les travaux partagés +# Copy all shared work docker run --rm \ -v jupyterhub-shared:/source \ - -v ~/rendus/shared:/target \ + -v ~/submissions/shared:/target \ alpine sh -c "cp -r /source/* /target/" ``` -## Gestion des utilisateurs +## User Management -### Option 1 : Liste d'utilisateurs prédéfinis -Dans `jupyterhub_config.py`, décommentez et modifiez : +### Option 1: Predefined User List +In `jupyterhub_config.py`, uncomment and modify: ```python -c.Authenticator.allowed_users = {'etudiant1', 'etudiant2', 'etudiant3'} +c.Authenticator.allowed_users = {'student1', 'student2', 'student3'} ``` -### Option 2 : Autoriser tout le monde (pour tests) -Par défaut, la configuration autorise n'importe quel utilisateur : +### Option 2: Allow Everyone (for testing) +By default, the configuration allows any user: ```python c.Authenticator.allow_all = True ``` -⚠️ **Attention** : DummyAuthenticator est UNIQUEMENT pour les tests locaux ! +⚠️ **Warning**: DummyAuthenticator is ONLY for local testing! -## Vérification des kernels +## Kernel Verification -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) +Once logged in, create a new notebook and verify you have access to: +- **Python 3** (default kernel) +- **R** (R kernel) +- **Bash** (bash kernel) -## Personnalisation pour vos TP +## Customization for Your Labs -### Ajouter des packages R supplémentaires -Modifiez le `Dockerfile` (avant `USER ${NB_UID}`) : +### Add Additional R Packages +Modify the `Dockerfile` (before `USER ${NB_UID}`): ```dockerfile -RUN R -e "install.packages(c('votre_package'), repos='http://cran.rstudio.com/')" +RUN R -e "install.packages(c('your_package'), repos='http://cran.rstudio.com/')" ``` -Puis reconstruisez : +Then rebuild: ```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}`) : +### Add Python Packages +Add to the `Dockerfile` (before `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` : +### Distribute Files to Students +Create a `files_lab/` directory and add to the `Dockerfile`: ```dockerfile -COPY files_tp/ /home/${NB_USER}/tp/ -RUN chown -R ${NB_UID}:${NB_GID} /home/${NB_USER}/tp +COPY files_lab/ /home/${NB_USER}/lab/ +RUN chown -R ${NB_UID}:${NB_GID} /home/${NB_USER}/lab ``` -### Changer le port (si 8000 est occupé) -Modifiez dans `docker-compose.yml` : +### Change Port (if 8000 is occupied) +Modify in `docker-compose.yml`: ```yaml ports: - - "8001:8000" # Accessible sur localhost:8001 + - "8001:8000" # Accessible on localhost:8001 ``` -## Avantages de cette approche +## Advantages of This Approach -✅ **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 +✅ **Everything in Docker**: No need to install Python/JupyterHub on your Mac +✅ **Portable**: Easy to deploy on another Mac or server +✅ **Isolated**: No pollution of your system environment +✅ **Easy to Clean**: A simple `docker-compose down` is enough +✅ **Reproducible**: Students will have exactly the same environment -## Dépannage +## Troubleshooting -**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` +**Error "Cannot connect to Docker daemon"**: +- Check that OrbStack is running +- Verify the socket exists: `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` +**Student containers don't start**: +- Check logs: `docker-compose logs jupyterhub` +- Verify student image exists: `docker images | grep jupyterhub-student` -**Port 8000 déjà utilisé** : -- Changez le port dans `docker-compose.yml` +**Port 8000 already in use**: +- Change port in `docker-compose.yml` -**Après modification du config, les changements ne sont pas pris en compte** : +**After config modification, changes are not applied**: ```bash docker-compose restart jupyterhub ``` -**Je veux repartir de zéro** : +**I want to start from scratch**: ```bash docker-compose down -v docker rmi jupyterhub-hub jupyterhub-student -# Puis reconstruire tout -``` \ No newline at end of file +# Then rebuild everything +./start-jupyterhub.sh +``` diff --git a/docker-compose.yaml b/docker-compose.yaml index faaedd7..8ff08d1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,21 +6,21 @@ services: container_name: jupyterhub image: jupyterhub-hub:latest ports: - - "8888:8000" + - "8000:8000" volumes: - # Accès au socket Docker pour spawner les containers étudiants + # Access to Docker socket to spawn student containers - /var/run/docker.sock:/var/run/docker.sock - # Persistance de la base de données JupyterHub + # JupyterHub database persistence - jupyterhub-data:/srv/jupyterhub - # Montage du fichier de config en direct (pour modifications faciles) + # Mount config file directly (for easy modifications) - ./jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py:ro networks: - jupyterhub-network restart: unless-stopped environment: - # Mot de passe partagé pour tous les étudiants + # Shared password for all students JUPYTERHUB_PASSWORD: metabar2025 - # Variables d'environnement optionnelles + # Optional environment variables DOCKER_NOTEBOOK_DIR: /home/jovyan/work networks: @@ -31,4 +31,5 @@ networks: volumes: jupyterhub-data: jupyterhub-shared: - jupyterhub-course: \ No newline at end of file + jupyterhub-course: + \ No newline at end of file diff --git a/jupyterhub_config.py b/jupyterhub_config.py index d8fbc75..4bf7668 100644 --- a/jupyterhub_config.py +++ b/jupyterhub_config.py @@ -1,5 +1,96 @@ import os +# Base configuration +c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner' + +# Enable debug logs +c.JupyterHub.log_level = 'DEBUG' +c.Spawner.debug = True + +# Docker image to use for student containers +c.DockerSpawner.image = 'jupyterhub-student:latest' + +# Docker network (create with: docker network create jupyterhub-network) +c.DockerSpawner.network_name = 'jupyterhub-network' + +# Connection to OrbStack Docker socket from the hub container +c.DockerSpawner.client_kwargs = {'base_url': 'unix:///var/run/docker.sock'} + +# IMPORTANT: Internal URL for communication between containers +# The hub container communicates with user containers via Docker network +c.JupyterHub.hub_ip = '0.0.0.0' +c.JupyterHub.hub_connect_ip = 'jupyterhub' + +# Network configuration for student containers +c.DockerSpawner.use_internal_ip = True +c.DockerSpawner.network_name = 'jupyterhub-network' +c.DockerSpawner.extra_host_config = {'network_mode': 'jupyterhub-network'} + +# Remove containers after disconnection (optional, set to False to keep containers) +c.DockerSpawner.remove = True + +# Container naming +c.DockerSpawner.name_template = "jupyter-{username}" + +# Volume mounting to persist student data +# Set root to /home/jovyan to see all directories +notebook_dir = '/home/jovyan' +c.DockerSpawner.notebook_dir = notebook_dir + +# Personal volume for each student + shared volume +c.DockerSpawner.volumes = { + # Personal volume (persistent) - mounted in work/ + 'jupyterhub-user-{username}': '/home/jovyan/work', + # Shared volume between all students + 'jupyterhub-shared': '/home/jovyan/shared', + # Shared read-only volume for course files (optional) + 'jupyterhub-course': { + 'bind': '/home/jovyan/course', + 'mode': 'ro' # read-only + } +} + +# Memory and CPU configuration (adjust according to your needs) +c.DockerSpawner.mem_limit = '2G' +c.DockerSpawner.cpu_limit = 1.0 + +# User configuration - Simple password authentication for lab +from jupyterhub.auth import DummyAuthenticator + +class SimplePasswordAuthenticator(DummyAuthenticator): + """Simple authenticator with a shared password for everyone""" + + def check_allowed(self, username, authentication=None): + """Check if user is allowed""" + if authentication is None: + return False + + # Get password from environment variable + expected_password = os.environ.get('JUPYTERHUB_PASSWORD', 'metabar2025') + provided_password = authentication.get('password', '') + + # Check password + return provided_password == expected_password + +c.JupyterHub.authenticator_class = SimplePasswordAuthenticator + +# To create a list of allowed users, uncomment and modify: +# c.Authenticator.allowed_users = {'student1', 'student2', 'student3'} + +# Or allow any user with the correct password: +c.Authenticator.allow_all = True + +# Admin configuration +c.Authenticator.admin_users = {'admin'} + +# Listening port +c.JupyterHub.bind_url = 'http://0.0.0.0:8000' + +# Timeout +c.Spawner.start_timeout = 300 +c.Spawner.http_timeout = 120 +import os + # Configuration de base c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner' @@ -88,4 +179,4 @@ 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 +c.Spawner.http_timeout = 120 diff --git a/setup.sh b/setup.sh index 5f2fb65..9d95ed5 100644 --- a/setup.sh +++ b/setup.sh @@ -1,30 +1,83 @@ -#!/usr/bin/env bash -set -e +#!/bin/bash -# === Variables === -WORKDIR="$PWD" -NETWORK="jupyterhub-net" -HUB_IMAGE="jupyterhub-hub" -USER_IMAGE="jupyter-tp-singleuser" +# JupyterHub startup script for labs +# Usage: ./start-jupyterhub.sh -# === Préparation === -mkdir -p "$WORKDIR" -cd "$WORKDIR" +set -e # Stop on error -echo "[1/5] Création du réseau Docker..." -docker network inspect $NETWORK >/dev/null 2>&1 || docker network create $NETWORK +echo "🚀 Starting JupyterHub for Lab" +echo "==============================" +echo "" -echo "[2/5] Construction des images..." -docker build -t $USER_IMAGE -f Dockerfile . -docker build -t $HUB_IMAGE -f Dockerfile.hub . +# Colors for display +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color -echo "[3/5] Lancement de JupyterHub..." -docker compose up -d +# Check we're in the right directory +if [ ! -f "Dockerfile" ] || [ ! -f "docker-compose.yml" ]; then + echo "❌ Error: Run this script from the jupyterhub-tp/ directory" + exit 1 +fi -echo "[4/5] Hub accessible sur http://localhost:8888" -echo " Login avec n'importe quel nom et mot de passe : metabar2025" +# Stop existing containers +echo -e "${BLUE}📦 Stopping existing containers...${NC}" +docker-compose down 2>/dev/null || true -echo "[5/5] Pour voir les utilisateurs actifs :" -echo " docker ps | grep jupyterhub-user" +# Remove old student containers +echo -e "${BLUE}🧹 Cleaning up student containers...${NC}" +docker ps -aq --filter name=jupyter- | xargs -r docker rm -f 2>/dev/null || true -echo "Terminé." +# Build student image +echo "" +echo -e "${BLUE}🔨 Building student image...${NC}" +docker build -t jupyterhub-student:latest -f Dockerfile . + +# Build hub image +echo "" +echo -e "${BLUE}🔨 Building JupyterHub image...${NC}" +docker build -t jupyterhub-hub:latest -f Dockerfile.hub . + +# Create volumes if they don't exist +echo "" +echo -e "${BLUE}💾 Creating shared volumes...${NC}" +docker volume create jupyterhub-shared 2>/dev/null || echo " Volume jupyterhub-shared already exists" +docker volume create jupyterhub-course 2>/dev/null || echo " Volume jupyterhub-course already exists" + +# Start the stack +echo "" +echo -e "${BLUE}🚀 Starting JupyterHub...${NC}" +docker-compose up -d + +# Wait for service to be ready +echo "" +echo -e "${YELLOW}⏳ Waiting for JupyterHub to start...${NC}" +sleep 3 + +# Check that container is running +if docker ps | grep -q jupyterhub; then + echo "" + echo -e "${GREEN}✅ JupyterHub is running!${NC}" + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo -e "${GREEN}🌐 JupyterHub available at: http://localhost:8000${NC}" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "📝 Password: metabar2025" + echo "👥 Students can connect with any username" + echo "" + echo "📂 Each student will have access to:" + echo " - work/ : personal workspace" + echo " - shared/ : shared workspace" + echo " - course/ : course files (read-only)" + echo "" + echo "🔍 To view logs: docker-compose logs -f jupyterhub" + echo "🛑 To stop: docker-compose down" + echo "" +else + echo "" + echo -e "${YELLOW}⚠️ JupyterHub container doesn't seem to be starting${NC}" + echo "Check logs with: docker-compose logs jupyterhub" + exit 1 +fi