Merge pull request 'push-svpkkstsnzoy' (#1) from push-svpkkstsnzoy into push-qlwkxzrrwlkv
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
57
Dockerfile
57
Dockerfile
@@ -1,34 +1,49 @@
|
|||||||
FROM jupyter/base-notebook:latest
|
# ---------- Stage 1 : builder ----------
|
||||||
|
FROM jupyter/base-notebook:latest AS builder
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
# Installation de R et des dépendances système
|
# Install system dependencies for R, build tools and Go/Rust
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
r-base \
|
r-base r-base-dev \
|
||||||
r-base-dev \
|
libcurl4-openssl-dev libssl-dev libxml2-dev \
|
||||||
libcurl4-openssl-dev \
|
build-essential git curl \
|
||||||
libssl-dev \
|
|
||||||
libxml2-dev \
|
|
||||||
texlive-xetex \
|
|
||||||
texlive-fonts-recommended \
|
|
||||||
texlive-plain-generic \
|
|
||||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Installation du kernel R pour Jupyter (en tant que root)
|
# Install R kernel + useful packages
|
||||||
RUN R -e "install.packages('IRkernel', repos='http://cran.rstudio.com/')" && \
|
RUN R -e "install.packages('IRkernel', repos='http://cran.rstudio.com/')" && \
|
||||||
R -e "IRkernel::installspec(user = FALSE)"
|
R -e "IRkernel::installspec(user = FALSE)" && \
|
||||||
|
R -e "install.packages(c('tidyverse','vegan','ade4'), repos='http://cran.rstudio.com/')"
|
||||||
|
|
||||||
# Installation de quelques packages R utiles pour les TP
|
# Install bash kernel
|
||||||
RUN R -e "install.packages(c('tidyverse','vegan','ade4'), repos='http://cran.rstudio.com/')"
|
RUN pip install bash_kernel && python -m bash_kernel.install --sys-prefix
|
||||||
|
|
||||||
# Installation du kernel bash (en tant que root aussi)
|
# Install obitools4
|
||||||
RUN pip install bash_kernel && \
|
RUN curl -L https://raw.githubusercontent.com/metabarcoding/obitools4/master/install_obitools.sh | bash
|
||||||
python -m bash_kernel.install --sys-prefix
|
|
||||||
|
|
||||||
# Créer les répertoires nécessaires avec les bonnes permissions
|
# Install csvkit
|
||||||
|
RUN pip install csvkit
|
||||||
|
|
||||||
|
# Install csvlens via Rust
|
||||||
|
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y && \
|
||||||
|
. $HOME/.cargo/env && \
|
||||||
|
cargo install csvlens
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y ruby ruby-dev build-essential \
|
||||||
|
&& gem install youplot
|
||||||
|
|
||||||
|
# Copy csvlens to /usr/local/bin for final use
|
||||||
|
RUN cp $HOME/.cargo/bin/csvlens /usr/local/bin/
|
||||||
|
|
||||||
|
# Set permissions for Jupyter user
|
||||||
RUN mkdir -p /home/${NB_USER}/.local/share/jupyter && \
|
RUN mkdir -p /home/${NB_USER}/.local/share/jupyter && \
|
||||||
chown -R ${NB_UID}:${NB_GID} /home/${NB_USER}
|
chown -R ${NB_UID}:${NB_GID} /home/${NB_USER}
|
||||||
|
|
||||||
USER ${NB_UID}
|
# Switch back to Jupyter user
|
||||||
|
USER ${NB_UID}:${NB_GID}
|
||||||
|
WORKDIR /home/${NB_USER}/work
|
||||||
|
|
||||||
WORKDIR /home/${NB_USER}
|
# Environment variables
|
||||||
|
ENV PATH="/home/${NB_USER}/work/course/bin:${PATH}"
|
||||||
|
ENV R_LIBS_USER="/home/${NB_USER}/work/R_packages"
|
||||||
|
ENV R_LIBS_SITE="/home/${NB_USER}/work/course/R_packages:/usr/local/lib/R/site-library:/usr/lib/R/site-library"
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
FROM jupyterhub/jupyterhub:latest
|
FROM jupyterhub/jupyterhub:latest
|
||||||
|
|
||||||
# Installation de DockerSpawner
|
# Install DockerSpawner
|
||||||
RUN pip install dockerspawner
|
RUN pip install dockerspawner
|
||||||
|
|
||||||
# Copie de la configuration
|
# Copy configuration
|
||||||
COPY jupyterhub_config.py /srv/jupyterhub/jupyterhub_config.py
|
COPY jupyterhub_config.py /srv/jupyterhub/jupyterhub_config.py
|
||||||
|
|
||||||
# Port exposé
|
# Expose port
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
# Répertoire de travail
|
# Working directory
|
||||||
WORKDIR /srv/jupyterhub
|
WORKDIR /srv/jupyterhub
|
||||||
|
|
||||||
# Commande de démarrage
|
# Startup command
|
||||||
CMD ["jupyterhub", "-f", "/srv/jupyterhub/jupyterhub_config.py"]
|
CMD ["jupyterhub", "-f", "/srv/jupyterhub/jupyterhub_config.py"]
|
||||||
314
Readme.md
314
Readme.md
@@ -1,258 +1,350 @@
|
|||||||
# Configuration JupyterHub avec OrbStack sur Mac (tout en Docker)
|
# JupyterHub Configuration with OrbStack on Mac (all in Docker)
|
||||||
|
|
||||||
## Prérequis
|
## Prerequisites
|
||||||
- OrbStack installé et démarré
|
- OrbStack installed and running
|
||||||
|
|
||||||
## Structure des fichiers
|
## File Structure
|
||||||
|
|
||||||
Votre dossier `~/jupyterhub-tp` doit contenir :
|
Your `~/jupyterhub-tp` directory should contain:
|
||||||
```
|
```
|
||||||
~/jupyterhub-tp/
|
~/jupyterhub-tp/
|
||||||
├── Dockerfile # Image pour les étudiants (déjà créée)
|
├── Dockerfile # Image for students (already created)
|
||||||
├── Dockerfile.hub # Image pour JupyterHub (nouvelle)
|
├── Dockerfile.hub # Image for JupyterHub (new)
|
||||||
├── jupyterhub_config.py # Configuration
|
├── 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
|
```bash
|
||||||
mkdir -p ~/jupyterhub-tp
|
mkdir -p ~/jupyterhub-tp
|
||||||
cd ~/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 :
|
Create the following files with the content from artifacts:
|
||||||
- `Dockerfile` (artifact "Dockerfile pour JupyterHub avec R et Bash")
|
- `Dockerfile` (artifact "Dockerfile for JupyterHub with R and Bash")
|
||||||
- `Dockerfile.hub` (artifact "Dockerfile pour le container JupyterHub")
|
- `Dockerfile.hub` (artifact "Dockerfile for JupyterHub container")
|
||||||
- `jupyterhub_config.py` (artifact "Configuration JupyterHub")
|
- `jupyterhub_config.py` (artifact "JupyterHub Configuration")
|
||||||
- `docker-compose.yml` (artifact "docker-compose.yml")
|
- `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
|
```bash
|
||||||
# Image pour les étudiants
|
chmod +x start-jupyterhub.sh
|
||||||
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
|
### 4. Start JupyterHub
|
||||||
|
|
||||||
```bash
|
```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:8888**
|
||||||
|
|
||||||
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
|
```bash
|
||||||
docker-compose logs -f jupyterhub
|
docker-compose logs -f jupyterhub
|
||||||
```
|
```
|
||||||
|
|
||||||
### Voir tous les containers (hub + étudiants)
|
### View all containers (hub + students)
|
||||||
```bash
|
```bash
|
||||||
docker ps
|
docker ps
|
||||||
```
|
```
|
||||||
|
|
||||||
### Arrêter JupyterHub
|
### Stop JupyterHub
|
||||||
```bash
|
```bash
|
||||||
docker-compose down
|
docker-compose down
|
||||||
```
|
```
|
||||||
|
|
||||||
### Redémarrer JupyterHub (après modification du config)
|
### Restart JupyterHub (after config modification)
|
||||||
```bash
|
```bash
|
||||||
docker-compose restart jupyterhub
|
docker-compose restart jupyterhub
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reconstruire après modification du Dockerfile
|
### Rebuild after Dockerfile modification
|
||||||
```bash
|
```bash
|
||||||
# Pour l'image étudiants
|
# For student image
|
||||||
docker build -t jupyterhub-student:latest -f Dockerfile .
|
docker build -t jupyterhub-student:latest -f Dockerfile .
|
||||||
docker-compose restart jupyterhub
|
docker-compose restart jupyterhub
|
||||||
|
|
||||||
# Pour l'image hub
|
# For hub image
|
||||||
docker-compose up -d --build
|
docker-compose up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Voir les logs d'un étudiant spécifique
|
### View logs for a specific student
|
||||||
```bash
|
```bash
|
||||||
docker logs jupyter-nom_utilisateur
|
docker logs jupyter-username
|
||||||
```
|
```
|
||||||
|
|
||||||
### Nettoyer après le TP
|
### Clean up after lab
|
||||||
```bash
|
```bash
|
||||||
# Arrêter et supprimer tous les containers
|
# Stop and remove all containers
|
||||||
docker-compose down
|
docker-compose down
|
||||||
|
|
||||||
# Supprimer les containers étudiants
|
# Remove student containers
|
||||||
docker ps -a | grep jupyter- | awk '{print $1}' | xargs docker rm -f
|
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
|
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-compose down -v
|
||||||
docker ps -a | grep jupyter- | awk '{print $1}' | xargs docker rm -f
|
docker ps -a | grep jupyter- | awk '{print $1}' | xargs docker rm -f
|
||||||
docker volume prune -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 :
|
Each student will see this directory structure in their JupyterLab (everything under `work/` is persistent):
|
||||||
- **`work/`** : Son espace personnel (persistant, privé)
|
```
|
||||||
- **`shared/`** : Espace partagé entre tous les étudiants (lecture/écriture)
|
work/ # Personal workspace root (persistent)
|
||||||
- **`course/`** : Fichiers du cours (lecture seule, vous déposez les fichiers)
|
├── [student files] # Their own files and notebooks
|
||||||
|
├── R_packages/ # Personal R packages (writable by student)
|
||||||
|
├── shared/ # Shared workspace (read/write, shared with all)
|
||||||
|
└── course/ # Course files (read-only, managed by admin)
|
||||||
|
├── R_packages/ # Shared R packages (read-only, installed by prof)
|
||||||
|
├── bin/ # Shared executables (in PATH)
|
||||||
|
└── [course materials] # Your course files
|
||||||
|
```
|
||||||
|
|
||||||
### Déposer des fichiers pour le cours
|
**R Package Priority:**
|
||||||
|
1. R checks `work/R_packages/` first (personal, writable)
|
||||||
|
2. Then `work/course/R_packages/` (shared, read-only, installed by prof)
|
||||||
|
3. Then system libraries
|
||||||
|
|
||||||
Pour mettre des fichiers dans le dossier `course/` (accessible en lecture seule) :
|
**Important:** Everything is under `work/`, so all student files are automatically saved in their persistent volume.
|
||||||
|
|
||||||
|
### User Accounts
|
||||||
|
|
||||||
|
**Admin Account:**
|
||||||
|
- Username: `admin`
|
||||||
|
- Password: `admin2025` (change in docker-compose.yml: `JUPYTERHUB_ADMIN_PASSWORD`)
|
||||||
|
- Can write to `course/` directory
|
||||||
|
|
||||||
|
**Student Accounts:**
|
||||||
|
- Username: any name
|
||||||
|
- Password: `metabar2025` (change in docker-compose.yml: `JUPYTERHUB_PASSWORD`)
|
||||||
|
- Read-only access to `course/` directory
|
||||||
|
|
||||||
|
### Installing R Packages (Admin Only)
|
||||||
|
|
||||||
|
**From your Mac (recommended):**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Créer un dossier temporaire
|
chmod +x install-r-packages-admin.sh
|
||||||
|
|
||||||
|
# Install packages
|
||||||
|
./install-r-packages-admin.sh reshape2 plotly knitr
|
||||||
|
```
|
||||||
|
|
||||||
|
This script:
|
||||||
|
- Installs packages in the `course/R_packages/` directory
|
||||||
|
- All students can use them (read-only)
|
||||||
|
- No need to rebuild the image
|
||||||
|
|
||||||
|
**From admin notebook:**
|
||||||
|
|
||||||
|
Login as `admin` and create an R notebook:
|
||||||
|
|
||||||
|
```r
|
||||||
|
# Install packages in course/R_packages (admin only, available to all students)
|
||||||
|
course_lib <- "/home/jovyan/work/course/R_packages"
|
||||||
|
dir.create(course_lib, recursive = TRUE, showWarnings = FALSE)
|
||||||
|
|
||||||
|
install.packages(c('reshape2', 'plotly', 'knitr'),
|
||||||
|
lib = course_lib,
|
||||||
|
repos = 'http://cran.rstudio.com/')
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Admin account has write access to the course directory.
|
||||||
|
|
||||||
|
**Students can also install their own packages:**
|
||||||
|
|
||||||
|
Students can install packages in their personal `work/R_packages/`:
|
||||||
|
|
||||||
|
```r
|
||||||
|
# Install in personal library (each student has their own)
|
||||||
|
install.packages(c('mypackage')) # Will install in work/R_packages/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using R Packages (Students)
|
||||||
|
|
||||||
|
Students simply load packages normally:
|
||||||
|
```r
|
||||||
|
library(reshape2) # R checks: 1) work/R_packages/ 2) work/course/R_packages/ 3) system
|
||||||
|
library(plotly)
|
||||||
|
```
|
||||||
|
|
||||||
|
R automatically searches in this order:
|
||||||
|
1. Personal packages: `/home/jovyan/work/R_packages/` (R_LIBS_USER)
|
||||||
|
2. Prof packages: `/home/jovyan/work/course/R_packages/` (R_LIBS_SITE)
|
||||||
|
3. System packages
|
||||||
|
|
||||||
|
### List Available Packages
|
||||||
|
|
||||||
|
```r
|
||||||
|
# List all available packages (personal + course + system)
|
||||||
|
installed.packages()[,"Package"]
|
||||||
|
|
||||||
|
# Check personal packages
|
||||||
|
list.files("/home/jovyan/work/R_packages")
|
||||||
|
|
||||||
|
# Check course packages (installed by prof)
|
||||||
|
list.files("/home/jovyan/work/course/R_packages")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deposit Files for Course
|
||||||
|
|
||||||
|
To put files in the `course/` directory (accessible read-only):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create a temporary directory
|
||||||
mkdir -p ~/jupyterhub-tp/course-files
|
mkdir -p ~/jupyterhub-tp/course-files
|
||||||
|
|
||||||
# Copier vos fichiers dedans
|
# Copy your files into it
|
||||||
cp mes_notebooks.ipynb ~/jupyterhub-tp/course-files/
|
cp my_notebooks.ipynb ~/jupyterhub-tp/course-files/
|
||||||
cp mes_donnees.csv ~/jupyterhub-tp/course-files/
|
cp my_data.csv ~/jupyterhub-tp/course-files/
|
||||||
|
|
||||||
# Copier dans le volume Docker
|
# Copy into Docker volume
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v jupyterhub-course:/target \
|
-v jupyterhub-course:/target \
|
||||||
-v ~/jupyterhub-tp/course-files:/source \
|
-v ~/jupyterhub-tp/course-files:/source \
|
||||||
alpine sh -c "cp -r /source/* /target/"
|
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
|
```python
|
||||||
# Dans un notebook, pour lire un fichier partagé
|
# In a notebook, to read a shared file
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
df = pd.read_csv('/home/jovyan/shared/donnees_groupe.csv')
|
df = pd.read_csv('/home/jovyan/work/shared/group_data.csv')
|
||||||
|
|
||||||
# Pour écrire un fichier partagé
|
# To write a shared file
|
||||||
df.to_csv('/home/jovyan/shared/resultats_alice.csv')
|
df.to_csv('/home/jovyan/work/shared/alice_results.csv')
|
||||||
```
|
```
|
||||||
|
|
||||||
### Récupérer les travaux des étudiants
|
### Retrieve Student Work
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Lister les volumes utilisateurs
|
# List user volumes
|
||||||
docker volume ls | grep jupyterhub-user
|
docker volume ls | grep jupyterhub-user
|
||||||
|
|
||||||
# Copier les fichiers d'un étudiant spécifique
|
# Copy files from a specific student
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v jupyterhub-user-alice:/source \
|
-v jupyterhub-user-alice:/source \
|
||||||
-v ~/rendus:/target \
|
-v ~/submissions:/target \
|
||||||
alpine sh -c "cp -r /source/* /target/alice/"
|
alpine sh -c "cp -r /source/* /target/alice/"
|
||||||
|
|
||||||
# Copier tous les travaux partagés
|
# Copy all shared work
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v jupyterhub-shared:/source \
|
-v jupyterhub-shared:/source \
|
||||||
-v ~/rendus/shared:/target \
|
-v ~/submissions/shared:/target \
|
||||||
alpine sh -c "cp -r /source/* /target/"
|
alpine sh -c "cp -r /source/* /target/"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Gestion des utilisateurs
|
## User Management
|
||||||
|
|
||||||
### Option 1 : Liste d'utilisateurs prédéfinis
|
### Option 1: Predefined User List
|
||||||
Dans `jupyterhub_config.py`, décommentez et modifiez :
|
In `jupyterhub_config.py`, uncomment and modify:
|
||||||
```python
|
```python
|
||||||
c.Authenticator.allowed_users = {'etudiant1', 'etudiant2', 'etudiant3'}
|
c.Authenticator.allowed_users = {'student1', 'student2', 'student3'}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Option 2 : Autoriser tout le monde (pour tests)
|
### Option 2: Allow Everyone (for testing)
|
||||||
Par défaut, la configuration autorise n'importe quel utilisateur :
|
By default, the configuration allows any user:
|
||||||
```python
|
```python
|
||||||
c.Authenticator.allow_all = True
|
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 à :
|
Once logged in, create a new notebook and verify you have access to:
|
||||||
- **Python 3** (kernel par défaut)
|
- **Python 3** (default kernel)
|
||||||
- **R** (kernel R)
|
- **R** (R kernel)
|
||||||
- **Bash** (kernel bash)
|
- **Bash** (bash kernel)
|
||||||
|
|
||||||
## Personnalisation pour vos TP
|
## Customization for Your Labs
|
||||||
|
|
||||||
### Ajouter des packages R supplémentaires
|
### Add Additional R Packages
|
||||||
Modifiez le `Dockerfile` (avant `USER ${NB_UID}`) :
|
Modify the `Dockerfile` (before `USER ${NB_UID}`):
|
||||||
```dockerfile
|
```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
|
```bash
|
||||||
docker build -t jupyterhub-student:latest -f Dockerfile .
|
docker build -t jupyterhub-student:latest -f Dockerfile .
|
||||||
docker-compose restart jupyterhub
|
docker-compose restart jupyterhub
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ajouter des packages Python
|
### Add Python Packages
|
||||||
Ajoutez dans le `Dockerfile` (avant `USER ${NB_UID}`) :
|
Add to the `Dockerfile` (before `USER ${NB_UID}`):
|
||||||
```dockerfile
|
```dockerfile
|
||||||
RUN pip install numpy pandas matplotlib seaborn
|
RUN pip install numpy pandas matplotlib seaborn
|
||||||
```
|
```
|
||||||
|
|
||||||
### Distribuer des fichiers aux étudiants
|
### Distribute Files to Students
|
||||||
Créez un dossier `files_tp/` et ajoutez dans le `Dockerfile` :
|
Create a `files_lab/` directory and add to the `Dockerfile`:
|
||||||
```dockerfile
|
```dockerfile
|
||||||
COPY files_tp/ /home/${NB_USER}/tp/
|
COPY files_lab/ /home/${NB_USER}/lab/
|
||||||
RUN chown -R ${NB_UID}:${NB_GID} /home/${NB_USER}/tp
|
RUN chown -R ${NB_UID}:${NB_GID} /home/${NB_USER}/lab
|
||||||
```
|
```
|
||||||
|
|
||||||
### Changer le port (si 8000 est occupé)
|
### Change Port (if 8000 is occupied)
|
||||||
Modifiez dans `docker-compose.yml` :
|
Modify in `docker-compose.yml`:
|
||||||
```yaml
|
```yaml
|
||||||
ports:
|
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
|
✅ **Everything in Docker**: No need to install Python/JupyterHub on your Mac
|
||||||
✅ **Portable** : Facile à déployer sur un autre Mac ou serveur
|
✅ **Portable**: Easy to deploy on another Mac or server
|
||||||
✅ **Isolé** : Pas de pollution de votre environnement système
|
✅ **Isolated**: No pollution of your system environment
|
||||||
✅ **Facile à nettoyer** : Un simple `docker-compose down` suffit
|
✅ **Easy to Clean**: A simple `docker-compose down` is enough
|
||||||
✅ **Reproductible** : Les étudiants auront exactement le même environnement
|
✅ **Reproducible**: Students will have exactly the same environment
|
||||||
|
|
||||||
## Dépannage
|
## Troubleshooting
|
||||||
|
|
||||||
**Erreur "Cannot connect to Docker daemon"** :
|
**Error "Cannot connect to Docker daemon"**:
|
||||||
- Vérifiez qu'OrbStack est démarré
|
- Check that OrbStack is running
|
||||||
- Vérifiez que le socket existe : `ls -la /var/run/docker.sock`
|
- Verify the socket exists: `ls -la /var/run/docker.sock`
|
||||||
|
|
||||||
**Les containers étudiants ne démarrent pas** :
|
**Student containers don't start**:
|
||||||
- Vérifiez les logs : `docker-compose logs jupyterhub`
|
- Check logs: `docker-compose logs jupyterhub`
|
||||||
- Vérifiez que l'image étudiants existe : `docker images | grep jupyterhub-student`
|
- Verify student image exists: `docker images | grep jupyterhub-student`
|
||||||
|
|
||||||
**Port 8000 déjà utilisé** :
|
**Port 8000 already in use**:
|
||||||
- Changez le port dans `docker-compose.yml`
|
- 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
|
```bash
|
||||||
docker-compose restart jupyterhub
|
docker-compose restart jupyterhub
|
||||||
```
|
```
|
||||||
|
|
||||||
**Je veux repartir de zéro** :
|
**I want to start from scratch**:
|
||||||
```bash
|
```bash
|
||||||
docker-compose down -v
|
docker-compose down -v
|
||||||
docker rmi jupyterhub-hub jupyterhub-student
|
docker rmi jupyterhub-hub jupyterhub-student
|
||||||
# Puis reconstruire tout
|
# Then rebuild everything
|
||||||
```
|
./start-jupyterhub.sh
|
||||||
|
```
|
||||||
|
|||||||
@@ -8,19 +8,21 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8888:8000"
|
- "8888:8000"
|
||||||
volumes:
|
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
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
# Persistance de la base de données JupyterHub
|
# JupyterHub database persistence
|
||||||
- jupyterhub-data:/srv/jupyterhub
|
- 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
|
- ./jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py:ro
|
||||||
networks:
|
networks:
|
||||||
- jupyterhub-network
|
- jupyterhub-network
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
# Mot de passe partagé pour tous les étudiants
|
# Shared password for all students
|
||||||
JUPYTERHUB_PASSWORD: metabar2025
|
JUPYTERHUB_PASSWORD: metabar2025
|
||||||
# Variables d'environnement optionnelles
|
# Admin password (for installing R packages)
|
||||||
|
JUPYTERHUB_ADMIN_PASSWORD: admin2025
|
||||||
|
# Optional environment variables
|
||||||
DOCKER_NOTEBOOK_DIR: /home/jovyan/work
|
DOCKER_NOTEBOOK_DIR: /home/jovyan/work
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
@@ -31,4 +33,5 @@ networks:
|
|||||||
volumes:
|
volumes:
|
||||||
jupyterhub-data:
|
jupyterhub-data:
|
||||||
jupyterhub-shared:
|
jupyterhub-shared:
|
||||||
jupyterhub-course:
|
jupyterhub-course:
|
||||||
|
|
||||||
74
install_packages.sh
Normal file
74
install_packages.sh
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to install R packages in the course directory (admin only)
|
||||||
|
# Usage: ./install-r-packages.sh package1 package2 package3
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
echo -e "${BLUE}📦 R Package Installer (Admin)${NC}"
|
||||||
|
echo "================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if packages are provided
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo -e "${RED}❌ Error: No packages specified${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Usage:"
|
||||||
|
echo " ./install-r-packages.sh package1 package2 package3"
|
||||||
|
echo ""
|
||||||
|
echo "Example:"
|
||||||
|
echo " ./install-r-packages.sh reshape2 plotly knitr"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build package list
|
||||||
|
PACKAGES=$(IFS=,; echo "$*")
|
||||||
|
R_PACKAGES=$(echo "$PACKAGES" | sed "s/,/', '/g")
|
||||||
|
|
||||||
|
echo -e "${YELLOW}📋 Packages to install: ${R_PACKAGES}${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Create a temporary directory for installation
|
||||||
|
TEMP_DIR=$(mktemp -d)
|
||||||
|
echo -e "${BLUE}📁 Creating temporary directory: ${TEMP_DIR}${NC}"
|
||||||
|
|
||||||
|
# Install packages in temporary directory
|
||||||
|
echo -e "${BLUE}🔨 Installing R packages...${NC}"
|
||||||
|
docker run --rm \
|
||||||
|
--user root \
|
||||||
|
-v "${TEMP_DIR}:/temp_packages" \
|
||||||
|
jupyterhub-student:latest \
|
||||||
|
R -e "install.packages(c('${R_PACKAGES}'), lib='/temp_packages', repos='http://cran.rstudio.com/')" || {
|
||||||
|
echo -e "${RED}❌ Failed to install packages${NC}"
|
||||||
|
rm -rf "${TEMP_DIR}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copy to course R packages directory
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}💾 Copying to course/R_packages...${NC}"
|
||||||
|
docker run --rm \
|
||||||
|
-v jupyterhub-course:/target \
|
||||||
|
-v "${TEMP_DIR}:/source" \
|
||||||
|
alpine sh -c "mkdir -p /target/R_packages && cp -r /source/* /target/R_packages/"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -rf "${TEMP_DIR}"
|
||||||
|
|
||||||
|
# List installed packages
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✅ Installation complete!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}📦 Installed packages in work/course/R_packages:${NC}"
|
||||||
|
docker run --rm \
|
||||||
|
-v jupyterhub-course:/course \
|
||||||
|
alpine ls -1 /course/R_packages/
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}ℹ️ Students need to restart their R kernels to use new packages.${NC}"
|
||||||
@@ -1,5 +1,100 @@
|
|||||||
import os
|
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 work/ - everything is persistent
|
||||||
|
notebook_dir = '/home/jovyan/work'
|
||||||
|
c.DockerSpawner.notebook_dir = notebook_dir
|
||||||
|
|
||||||
|
# Personal volume for each student + shared volumes under work/
|
||||||
|
c.DockerSpawner.volumes = {
|
||||||
|
# Personal volume (persistent) - root directory
|
||||||
|
'jupyterhub-user-{username}': '/home/jovyan/work',
|
||||||
|
# Shared volume between all students - under work/
|
||||||
|
'jupyterhub-shared': '/home/jovyan/work/shared',
|
||||||
|
# Shared read-only volume for course files - under work/
|
||||||
|
'jupyterhub-course': {
|
||||||
|
'bind': '/home/jovyan/work/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
|
||||||
|
|
||||||
|
provided_password = authentication.get('password', '')
|
||||||
|
|
||||||
|
# Admin user with special password
|
||||||
|
if username == 'admin':
|
||||||
|
admin_password = os.environ.get('JUPYTERHUB_ADMIN_PASSWORD', 'admin2025')
|
||||||
|
return provided_password == admin_password
|
||||||
|
|
||||||
|
# Regular students with shared password
|
||||||
|
student_password = os.environ.get('JUPYTERHUB_PASSWORD', 'metabar2025')
|
||||||
|
return provided_password == student_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
|
# Configuration de base
|
||||||
c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'
|
c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'
|
||||||
|
|
||||||
@@ -34,7 +129,7 @@ c.DockerSpawner.name_template = "jupyter-{username}"
|
|||||||
|
|
||||||
# Montage de volumes pour persister les données des étudiants
|
# Montage de volumes pour persister les données des étudiants
|
||||||
# Définir la racine à /home/jovyan pour voir tous les dossiers
|
# Définir la racine à /home/jovyan pour voir tous les dossiers
|
||||||
notebook_dir = '/home/jovyan'
|
notebook_dir = '/home/jovyan/work'
|
||||||
c.DockerSpawner.notebook_dir = notebook_dir
|
c.DockerSpawner.notebook_dir = notebook_dir
|
||||||
|
|
||||||
# Volume personnel pour chaque étudiant + volume partagé
|
# Volume personnel pour chaque étudiant + volume partagé
|
||||||
@@ -42,10 +137,10 @@ c.DockerSpawner.volumes = {
|
|||||||
# Volume personnel (persistant) - monté dans work/
|
# Volume personnel (persistant) - monté dans work/
|
||||||
'jupyterhub-user-{username}': '/home/jovyan/work',
|
'jupyterhub-user-{username}': '/home/jovyan/work',
|
||||||
# Volume partagé entre tous les étudiants
|
# Volume partagé entre tous les étudiants
|
||||||
'jupyterhub-shared': '/home/jovyan/shared',
|
'jupyterhub-shared': '/home/jovyan/work/shared',
|
||||||
# Volume partagé en lecture seule pour les fichiers du cours (optionnel)
|
# Volume partagé en lecture seule pour les fichiers du cours (optionnel)
|
||||||
'jupyterhub-course': {
|
'jupyterhub-course': {
|
||||||
'bind': '/home/jovyan/course',
|
'bind': '/home/jovyan/work/course',
|
||||||
'mode': 'ro' # read-only
|
'mode': 'ro' # read-only
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,26 +149,6 @@ c.DockerSpawner.volumes = {
|
|||||||
c.DockerSpawner.mem_limit = '2G'
|
c.DockerSpawner.mem_limit = '2G'
|
||||||
c.DockerSpawner.cpu_limit = 1.0
|
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:
|
# Pour créer une liste d'utilisateurs autorisés, décommentez et modifiez:
|
||||||
# c.Authenticator.allowed_users = {'etudiant1', 'etudiant2', 'etudiant3'}
|
# c.Authenticator.allowed_users = {'etudiant1', 'etudiant2', 'etudiant3'}
|
||||||
|
|
||||||
|
|||||||
104
setup.sh
Normal file → Executable file
104
setup.sh
Normal file → Executable file
@@ -1,30 +1,90 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
set -e
|
|
||||||
|
|
||||||
# === Variables ===
|
# JupyterHub startup script for labs
|
||||||
WORKDIR="$PWD"
|
# Usage: ./start-jupyterhub.sh
|
||||||
NETWORK="jupyterhub-net"
|
|
||||||
HUB_IMAGE="jupyterhub-hub"
|
|
||||||
USER_IMAGE="jupyter-tp-singleuser"
|
|
||||||
|
|
||||||
# === Préparation ===
|
set -e # Stop on error
|
||||||
mkdir -p "$WORKDIR"
|
|
||||||
cd "$WORKDIR"
|
|
||||||
|
|
||||||
echo "[1/5] Création du réseau Docker..."
|
echo "🚀 Starting JupyterHub for Lab"
|
||||||
docker network inspect $NETWORK >/dev/null 2>&1 || docker network create $NETWORK
|
echo "=============================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
echo "[2/5] Construction des images..."
|
# Colors for display
|
||||||
docker build -t $USER_IMAGE -f Dockerfile .
|
GREEN='\033[0;32m'
|
||||||
docker build -t $HUB_IMAGE -f Dockerfile.hub .
|
BLUE='\033[0;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
echo "[3/5] Lancement de JupyterHub..."
|
# Check we're in the right directory
|
||||||
docker compose up -d
|
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"
|
# Stop existing containers
|
||||||
echo " Login avec n'importe quel nom et mot de passe : metabar2025"
|
echo -e "${BLUE}📦 Stopping existing containers...${NC}"
|
||||||
|
docker-compose down 2>/dev/null || true
|
||||||
|
|
||||||
echo "[5/5] Pour voir les utilisateurs actifs :"
|
# Remove old student containers
|
||||||
echo " docker ps | grep jupyterhub-user"
|
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:8888${NC}"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
echo "📝 Password: metabar2025"
|
||||||
|
echo "👥 Students can connect with any username"
|
||||||
|
echo ""
|
||||||
|
echo "🔑 Admin account:"
|
||||||
|
echo " Username: admin"
|
||||||
|
echo " Password: admin2025"
|
||||||
|
echo ""
|
||||||
|
echo "📂 Each student will have access to:"
|
||||||
|
echo " - work/ : personal workspace (everything saved)"
|
||||||
|
echo " - work/R_packages/ : personal R packages (writable)"
|
||||||
|
echo " - work/shared/ : shared workspace"
|
||||||
|
echo " - work/course/ : course files (read-only)"
|
||||||
|
echo " - work/course/R_packages/ : shared R packages by prof (read-only)"
|
||||||
|
echo " - work/course/bin/ : shared executables (in PATH)"
|
||||||
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user