Skip to content

Commit

Permalink
-
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbrochart committed Aug 1, 2023
1 parent e9967af commit 534b5a1
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 3 deletions.
219 changes: 219 additions & 0 deletions docs/tutorials/jupyterhub_jupyverse_deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
In this tutorial, we will deploy Jupyverse through JupyterHub on a public [OVHcloud](https://www.ovhcloud.com) instance, and allow authentication using a [GitHub](https://github.com) account.

## OVH setup

### Create and connect to a public instance

Let's follow the guide on [Creating and connecting to your first Public Cloud instance](https://help.ovhcloud.com/csm/en-public-cloud-compute-getting-started?id=kb_article_view&sysparm_article=KB0051009). We first need to create SSH keys, so that we can connect to our instance using SSH. Enter in a terminal:

```console
$ ssh-keygen -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa):
```

You can hit _Enter_. You are then asked to enter a passphrase, we will need it later.

The public key can be accessed with:

```console
$ cat ~/.ssh/id_rsa.pub
```

Copy this public key into your clipboard.

In the OVHcloud Control Panel, click on "Instances" and then "Create an instance". Choose the "B2-7" model, which is a light and general use instance, and click "Next".

Select a region of you choice and click "Next".

Select the "Ubuntu 23.04" image and click "Add a key" under "SSH key". Give it a name an paste your public key, then click "Next". Your instance should already be configured, you can click "Next" again. In the network configuration, make sure "Public mode" is checked, and click "Next". Then select your preferred billing period and click "Create an instance".

Your instance should activate shortly. You can see it has a public IP, something like `1.2.3.4`. Let's connect to the instance using this IP address:

```console
$ ssh [email protected]
The authenticity of host '1.2.3.4 (1.2.3.4)' can't be established.
ED25519 key fingerprint is SHA256:Q1&tbgX3fp9+7J90zyK0ctuKe1aqPoEY76Qi58uoSnA.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?
```
Enter "yes", then enter your passphrase. You should now be connected to your instance.

### Set up the environment

Let's install [micromamba](https://mamba.readthedocs.io/en/latest/installation.html#micromamba) and configure it:

```console
$ sudo apt install bzip2
$ curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba
$ bin/micromamba shell init --shell bash --root-prefix=~/micromamba
$ exec bash
```

Now create a conda environment and install Python and Node.js:

```console
$ micromamba create -n jupyterhub
$ micromamba activate jupyterhub
$ micromamba install -c conda-forge python nodejs
```

And install JupyterHub and Jupyverse:

```console
$ pip install -e "git+https://github.com/jupyter-server/jupyverse.git@main#egg=jupyverse_api&subdirectory=jupyverse_api"
$ pip install -e "git+https://github.com/davidbrochart/jupyverse.git@auth_jupyterhub#egg=fps_auth_jupyterhub&subdirectory=plugins/auth_jupyterhub"
$ pip install https://github.com/davidbrochart/jupyterhub/archive/jupyverse.zip
$ pip install jupyverse[jupyterlab]
% npm install -g configurable-http-proxy
```

Let's create a JupyterHub configuration file under the `jupyterhub` directory:

```console
$ mkdir jupyterhub
$ cd jupyterhub
$ vim jupyterhub_config.py
```

With the following content:

```py
# jupyterhub_config.py file
c = get_config()

import os
pjoin = os.path.join

runtime_dir = os.path.join('/srv/jupyterhub')
ssl_dir = pjoin(runtime_dir, 'ssl')
if not os.path.exists(ssl_dir):
os.makedirs(ssl_dir)

# Allows multiple single-server per user
c.JupyterHub.allow_named_servers = True

# https on :443
c.JupyterHub.port = 443
c.JupyterHub.ssl_key = pjoin(ssl_dir, 'ssl.key')
c.JupyterHub.ssl_cert = pjoin(ssl_dir, 'ssl.cert')

# put the JupyterHub cookie secret and state db
# in /var/run/jupyterhub
c.JupyterHub.cookie_secret_file = pjoin(runtime_dir, 'cookie_secret')
c.JupyterHub.db_url = pjoin(runtime_dir, 'jupyterhub.sqlite')
# or `--db=/path/to/jupyterhub.sqlite` on the command-line

# use GitHub OAuthenticator for local users
c.JupyterHub.authenticator_class = 'oauthenticator.LocalGitHubOAuthenticator'
c.GitHubOAuthenticator.oauth_callback_url = os.environ['OAUTH_CALLBACK_URL']

# create system users that don't exist yet
c.LocalAuthenticator.create_system_users = True

# specify users and admin
c.Authenticator.allowed_users = {'rgbkrk', 'minrk', 'jhamrick'}
c.Authenticator.admin_users = {'jhamrick', 'rgbkrk'}

# uses the default spawner
# To use a different spawner, uncomment `spawner_class` and set to desired
# spawner (e.g. SudoSpawner). Follow instructions for desired spawner
# configuration.
# c.JupyterHub.spawner_class = 'sudospawner.SudoSpawner'

# start single-user notebook servers in ~/assignments,
# with ~/assignments/Welcome.ipynb as the default landing page
# this config could also be put in
# /etc/jupyter/jupyter_notebook_config.py
c.Spawner.notebook_dir = '~/assignments'
c.Spawner.args = ['--NotebookApp.default_url=/notebooks/Welcome.ipynb']
```

```console
export GITHUB_CLIENT_ID=github_id
export GITHUB_CLIENT_SECRET=github_secret
export OAUTH_CALLBACK_URL=https://example.com/hub/oauth_callback
export CONFIGPROXY_AUTH_TOKEN=super-secret
# append log output to log file /var/log/jupyterhub.log
jupyterhub -f /etc/jupyterhub/jupyterhub_config.py &>> /var/log/jupyterhub.log
```

### Set up HTTPS and NGINX

For this you will need a domain name, like [https://my.jupyverse.com](https://my.jupyverse.com), that must point to your instance through its IP address.

We'll use [NGINX and Let's Encrypt](https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/) to manage SSL/TLS certificates. Enter in a terminal:

```console
$ sudo apt install certbot nginx python3-certbot-nginx
```

Create a file at `/etc/nginx/conf.d/my.jupyverse.com.conf` (note that `my.jupyverse.com` is your domain name) with the following content:

```
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
server_name my.jupyverse.com;
}
```

Save the file, then run this command to verify the syntax of your configuration and restart NGINX:

```console
$ sudo nginx -t && sudo nginx -s reload
```

You may have to remove the _server_ section in `/etc/nginx/sites-enabled/default`, or simply remove this file. Now run the following command to generate certificates with the NGINX plug‑in:

```console
$ sudo certbot --nginx -d my.jupyverse.com
```

After answering a few questions, you should be all set. If you look at `/etc/nginx/conf.d/my.jupyverse.com.conf` again, you should see that it was modified. Add the following `location` sections at the bottom:

```
server {
root /var/www/html;
server_name my.jupyverse.com;
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/my.jupyverse.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/my.jupyverse.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
location / {
proxy_pass http://localhost:8000;
}
location ~ \/api\/kernels\/.+\/channels {
proxy_pass http://localhost:8000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location ~ \/terminals\/websocket\/.+ {
proxy_pass http://localhost:8000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location ~ \/api\/collaboration\/room\/.+ {
proxy_pass http://localhost:8000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
```

## Run the server

```console
export JUPYTERHUB_SINGLEUSER_APP=jupyverse
jupyterhub
```

Now open a browser window at [https://my.jupyverse.com](https://my.jupyverse.com), and click "Sign in with GitHub". Enter your credentials and click "Sign in". If you have two-factor authentication enabled on your GitHub account, you may have to approve the request by entering a code e.g. in your mobile phone GitHub application. You should be redirected back to Fief, where you are asked to provide an email to finalize the sign up. It should be pre-filled with your GitHub email. Just click "Finalize sign up".

After a while, JupyterLab should start. You should see your avatar in the top-right corner. Any other connected user should be visible in the "Collaboration" tab on the left, and if you work on the same notebook, you should see them collaborate live!
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
In this tutorial, we will deploy Jupyverse on a public [OVHcloud](https://www.ovhcloud.com) instance using [Fief](https://fief.dev), and allow authentication using a [GitHub](https://github.com) account.
In this tutorial, we will deploy Jupyverse as a standalone server on a public [OVHcloud](https://www.ovhcloud.com) instance using [Fief](https://fief.dev), and allow authentication using a [GitHub](https://github.com) account.

## OVH setup

Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ nav:
- usage/multi_user.md
- usage/microservices.md
- Turorials:
- tutorials/deployment.md
- tutorials/standalone_jupyverse_deployment.md
- Plugins:
- 'auth': plugins/auth.md
- 'contents': plugins/contents.md
Expand Down
2 changes: 2 additions & 0 deletions plugins/auth_jupyterhub/fps_auth_jupyterhub/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
__version__ = "0.2.0"

from .launch import launch
22 changes: 22 additions & 0 deletions plugins/auth_jupyterhub/fps_auth_jupyterhub/launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
from urllib.parse import urlparse


def launch():
from jupyverse_api.cli import main as jupyverse

service_url = os.environ.get("JUPYTERHUB_SERVICE_URL")
url = urlparse(service_url)
try:
return jupyverse.callback(
open_browser=True,
host=url.hostname,
port=url.port,
set_=[
f"frontend.base_url={url.path}",
f"app.mount_path={url.path}",
],
disable=[],
)
except Exception:
return
2 changes: 1 addition & 1 deletion plugins/auth_jupyterhub/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ requires-python = ">=3.8"
dependencies = [
"asphalt-sqlalchemy >=5.0.1,<6",
"httpx >=0.24.1,<1",
"jupyterhub >=4.0.1,<5",
"jupyterhub >=5,<6",
"jupyverse-api >=0.1.2,<1",
]

Expand Down

0 comments on commit 534b5a1

Please sign in to comment.