├── .env
├── .gitignore
├── LICENSE
├── README.md
├── binder
├── docker-compose.yml
├── jupyterhub
├── Dockerfile
└── jupyterhub_config.py
├── jupyterlab
├── Dockerfile
└── conda-activate.sh
└── reverse-proxy
└── traefik.toml
/.env:
--------------------------------------------------------------------------------
1 | # A name for this Docker Compose application, it can be whatever you like
2 | COMPOSE_PROJECT_NAME=jupyterhub
3 |
4 | # The server where this JupyterHub server is hosted
5 | HOST=jupyter.ens.uvsq.fr
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .*
2 | *~
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018-2019 Luca De Feo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JupyterHub deployment in use at Université de Versailles
2 |
3 | This is a [JupyterHub](https://jupyter.org/hub) deployment based on
4 | Docker currently in use at [Université de
5 | Versailles](https://jupyter.ens.uvsq.fr/).
6 |
7 | ## Features
8 |
9 | - Containerized single user Jupyter servers, using
10 | [DockerSpawner](https://github.com/jupyterhub/dockerspawner);
11 | - Central authentication to the University CAS server;
12 | - User data persistence;
13 | - HTTPS proxy.
14 |
15 | ## Learn more
16 |
17 | This deployment is described in depth in [this blog
18 | post](https://opendreamkit.org/2018/10/17/jupyterhub-docker/).
19 |
20 | ### Adapt to your needs
21 |
22 | This deployment is ready to clone and roll on your own server. Read
23 | the [blog
24 | post](https://opendreamkit.org/2018/10/17/jupyterhub-docker/) first,
25 | to be sure you understand the configuration.
26 |
27 | Then, if you like, clone this repository and apply (at least) the
28 | following changes:
29 |
30 | - In [`.env`](.env), set the variable `HOST` to the name of the server you
31 | intend to host your deployment on.
32 | - In [`reverse-proxy/traefik.toml`](reverse-proxy/traefik.toml), edit
33 | the paths in `certFile` and `keyFile` and point them to your own TLS
34 | certificates. Possibly edit the `volumes` section in the
35 | `reverse-proyx` service in
36 | [`docker-compose.yml`](docker-compose.yml).
37 | - In
38 | [`jupyterhub/jupyterhub_config.py`](jupyterhub/jupyterhub_config.py),
39 | edit the *"Authenticator"* section according to your institution
40 | authentication server. If in doubt, [read
41 | here](https://jupyterhub.readthedocs.io/en/stable/getting-started/authenticators-users-basics.html).
42 |
43 | Other changes you may like to make:
44 |
45 | - Edit [`jupyterlab/Dockerfile`](jupyterlab/Dockerfile) to include the
46 | software you like. Do not forget to change
47 | [`jupyterhub/jupyterhub_config.py`](jupyterhub/jupyterhub_config.py)
48 | accordingly, in particular the *"user data persistence"* section.
49 |
50 | ### Run!
51 |
52 | Once you are ready, build and launch the application with
53 |
54 | ```
55 | docker-compose build
56 | docker-compose up -d
57 | ```
58 |
59 | Read the [Docker Compose manual](https://docs.docker.com/compose/) to
60 | learn how to manage your application.
61 |
62 | ## Acknowledgements
63 |
64 |
Work partially funded by the EU H2020 project
65 | [OpenDreamKit](https://opendreamkit.org/).
66 |
--------------------------------------------------------------------------------
/binder:
--------------------------------------------------------------------------------
1 | jupyterlab/
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | jupyterhub:
5 | build: jupyterhub
6 | image: jupyterhub_img
7 | container_name: jupyterhub
8 | volumes:
9 | - /var/run/docker.sock:/var/run/docker.sock
10 | - jupyterhub_data:/srv/jupyterhub
11 | environment:
12 | - DOCKER_JUPYTER_CONTAINER=jupyterlab_img
13 | - DOCKER_NETWORK_NAME=${COMPOSE_PROJECT_NAME}_default
14 | - HUB_IP=jupyterhub
15 | - HOST
16 | labels:
17 | - "traefik.enable=true"
18 | - "traefik.frontend.rule=Host:${HOST}"
19 | restart: on-failure
20 |
21 | jupyterlab:
22 | build: jupyterlab
23 | image: jupyterlab_img
24 | container_name: jupyterlab-throaway
25 | network_mode: none
26 | command: echo
27 |
28 | reverse-proxy:
29 | image: traefik
30 | container_name: reverse-proxy
31 | ports:
32 | - "80:80"
33 | - "443:443"
34 | - "8080:8080"
35 | volumes:
36 | - ./reverse-proxy/traefik.toml:/etc/traefik/traefik.toml
37 | - /etc/certs:/etc/certs
38 | - /var/run/docker.sock:/var/run/docker.sock
39 | restart: on-failure
40 |
41 | volumes:
42 | jupyterhub_data:
43 |
--------------------------------------------------------------------------------
/jupyterhub/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM jupyterhub/jupyterhub:0.9.3
2 |
3 | COPY jupyterhub_config.py .
4 |
5 | RUN wget https://raw.githubusercontent.com/jupyterhub/jupyterhub/0.9.3/examples/cull-idle/cull_idle_servers.py
6 |
7 | RUN pip install \
8 | dockerspawner==0.10.0 \
9 | jhub_cas_authenticator==1.0.0
10 |
--------------------------------------------------------------------------------
/jupyterhub/jupyterhub_config.py:
--------------------------------------------------------------------------------
1 | # JupyterHub configuration
2 | #
3 | ## If you update this file, do not forget to delete the `jupyterhub_data` volume before restarting the jupyterhub service:
4 | ##
5 | ## docker volume rm jupyterhub_jupyterhub_data
6 | ##
7 | ## or, if you changed the COMPOSE_PROJECT_NAME to :
8 | ##
9 | ## docker volume rm _jupyterhub_data
10 | ##
11 |
12 | import os
13 |
14 | ## Generic
15 | c.JupyterHub.admin_access = True
16 | c.Spawner.default_url = '/lab'
17 |
18 | ## Authenticator
19 | from jhub_cas_authenticator.cas_auth import CASAuthenticator
20 | c.JupyterHub.authenticator_class = CASAuthenticator
21 |
22 | # The CAS URLs to redirect (un)authenticated users to.
23 | c.CASAuthenticator.cas_login_url = 'https://cas.uvsq.fr/login'
24 | c.CASLocalAuthenticator.cas_logout_url = 'https://cas.uvsq/logout'
25 |
26 | # The CAS endpoint for validating service tickets.
27 | c.CASAuthenticator.cas_service_validate_url = 'https://cas.uvsq.fr/serviceValidate'
28 |
29 | # The service URL the CAS server will redirect the browser back to on successful authentication.
30 | c.CASAuthenticator.cas_service_url = 'https://%s/hub/login' % os.environ['HOST']
31 |
32 | c.Authenticator.admin_users = { 'lucadefe' }
33 |
34 |
35 | ## Docker spawner
36 | c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'
37 | c.DockerSpawner.image = os.environ['DOCKER_JUPYTER_CONTAINER']
38 | c.DockerSpawner.network_name = os.environ['DOCKER_NETWORK_NAME']
39 | # See https://github.com/jupyterhub/dockerspawner/blob/master/examples/oauth/jupyterhub_config.py
40 | c.JupyterHub.hub_ip = os.environ['HUB_IP']
41 |
42 | # user data persistence
43 | # see https://github.com/jupyterhub/dockerspawner#data-persistence-and-dockerspawner
44 | notebook_dir = os.environ.get('DOCKER_NOTEBOOK_DIR') or '/home/jovyan'
45 | c.DockerSpawner.notebook_dir = notebook_dir
46 | c.DockerSpawner.volumes = { 'jupyterhub-user-{username}': notebook_dir }
47 |
48 | # Other stuff
49 | c.Spawner.cpu_limit = 1
50 | c.Spawner.mem_limit = '10G'
51 |
52 |
53 | ## Services
54 | c.JupyterHub.services = [
55 | {
56 | 'name': 'cull_idle',
57 | 'admin': True,
58 | 'command': 'python /srv/jupyterhub/cull_idle_servers.py --timeout=3600'.split(),
59 | },
60 | ]
61 |
--------------------------------------------------------------------------------
/jupyterlab/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM jupyter/scipy-notebook:137a295ff71b
2 |
3 | LABEL maintainer="Luca De Feo "
4 |
5 | USER root
6 |
7 | # APT packages
8 | RUN apt-get update && \
9 | apt-get install -y --no-install-recommends \
10 | fonts-dejavu \
11 | tzdata \
12 | gfortran \
13 | gcc \
14 | scilab \
15 | pari-gp \
16 | libpari-dev \
17 | sagemath \
18 | sagemath-jupyter \
19 | libgmp-dev \
20 | && apt-get clean && \
21 | rm -rf /var/lib/apt/lists/*
22 |
23 | USER $NB_UID
24 |
25 | # Conda packages
26 | # Sage conflicts with the latest jupyterhub, thus we must relax the pinning
27 | RUN conda install --quiet --yes \
28 | 'r-base=3.4.1' \
29 | 'r-irkernel=0.8*' \
30 | 'r-plyr=1.8*' \
31 | 'r-devtools=1.13*' \
32 | 'r-tidyverse=1.1*' \
33 | 'r-shiny=1.0*' \
34 | 'r-rmarkdown=1.8*' \
35 | 'r-forecast=8.2*' \
36 | 'r-rsqlite=2.0*' \
37 | 'r-reshape2=1.4*' \
38 | 'r-nycflights13=0.2*' \
39 | 'r-caret=6.0*' \
40 | 'r-rcurl=1.95*' \
41 | 'r-crayon=1.3*' \
42 | 'r-randomforest=4.6*' \
43 | 'r-htmltools=0.3*' \
44 | 'r-sparklyr=0.7*' \
45 | 'r-htmlwidgets=1.0*' \
46 | 'r-hexbin=1.27*' \
47 | 'jupyterhub' \
48 | # 'sage=8.*' \
49 | 'julia=1.0*' && \
50 | conda clean -tipsy && \
51 | fix-permissions $CONDA_DIR
52 |
53 | ENV CPATH=$CONDA_DIR/include
54 |
55 | RUN pip install \
56 | pari_jupyter \
57 | # PySingular jupyter_kernel_singular \
58 | scilab-kernel && \
59 | fix-permissions $CONDA_DIR
60 |
61 | # Fix SageMath kernel
62 | USER root
63 | RUN sed -i 's/"\/usr\/bin\/sage"/"env", "PATH=\/usr\/local\/sbin:\/usr\/local\/bin:\/usr\/sbin:\/usr\/bin:\/sbin:\/bin", "\/usr\/bin\/sage"/' /usr/share/jupyter/kernels/sagemath/kernel.json
64 | USER $NB_UID
65 |
66 | # Add conda env hook
67 | COPY ./conda-activate.sh /usr/local/bin/before-notebook.d/
68 |
--------------------------------------------------------------------------------
/jupyterlab/conda-activate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source /opt/conda/bin/activate base
4 |
--------------------------------------------------------------------------------
/reverse-proxy/traefik.toml:
--------------------------------------------------------------------------------
1 | debug = true
2 |
3 | logLevel = "ERROR"
4 | defaultEntryPoints = ["https","http"]
5 |
6 | [entryPoints]
7 | [entryPoints.http]
8 | address = ":80"
9 | [entryPoints.http.redirect]
10 | entryPoint = "https"
11 | [entryPoints.https]
12 | address = ":443"
13 | [entryPoints.https.tls]
14 | [[entryPoints.https.tls.certificates]]
15 | certFile = "/etc/certs/jupyter_ens_uvsq_fr.crt"
16 | keyFile = "/etc/certs/jupyter.ens.uvsq.fr.key"
17 |
18 | [docker]
19 | domain = "docker.local"
20 | watch = true
21 |
22 | [api]
23 | [api.statistics]
24 | recentErrors = 10
25 |
--------------------------------------------------------------------------------