---
title: 'JupyterHub with GitLab integration'
disqus: hackmd
---
JupyterHub with GitLab integration
==================================
## Table of Contents
[TOC]
## Overview

## JupyterHub Overview
JupyterHub is the best way to serve Jupyter notebooks for multiple users.
JupyterHub performs the following functions:
- The Hub launches a proxy
- The proxy forwards all requests to the Hub by default
- The Hub handles user login and spawns single-user servers on demand
- The Hub configures the proxy to forward URL prefixes to the single-user notebook servers

Source: https://jupyterhub.readthedocs.io/en/stable/
## Deploy JupyterHub on Kubernetes with ZTJH (Zero To JupyterHub)
### Initial setup on an existing Kubernetes cluster
First create a new token:
```
$ openssl rand -hex 32
fd3057a1d7ff7b5e5a77cfb1f370ec4d3d54575f7ef0d382b0477a6707c54adf
```
Add the JupyterHub helm repo:
```
helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/
helm repo update
```
More details in the documentation: https://zero-to-jupyterhub.readthedocs.io/en/latest/index.html
### values.yaml
Use the following values to deploy JupyterHub on Kubernetes.
Replace `example.com` with the real domain name.
```yaml
hub:
extraEnv:
JUPYTER_ENABLE_LAB: 1
GITLAB_HOST: https://gitlab.example.com
# disable TLS verification for self-signed certificates
OAUTH_TLS_VERIFY: 0
extraConfig:
gitlab: |
c.KubeSpawner.cmd = ['jupyter-labhub']
c.GitLabOAuthenticator.scope = ['read_user api read_repository write_repository']
async def pre_spawn_hook(spawner):
auth_state = await spawner.user.get_auth_state()
if not auth_state:
spawner.log.warning("No auth state for %s", spawner.user)
return
spawner.environment['GITLAB_ACCESS_TOKEN'] = auth_state['access_token']
spawner.environment['GITLAB_USER_LOGIN'] = auth_state['gitlab_user']['username']
spawner.environment['GITLAB_USER_ID'] = str(auth_state['gitlab_user']['id'])
spawner.environment['GITLAB_USER_EMAIL'] = auth_state['gitlab_user']['email']
spawner.environment['GITLAB_USER_NAME'] = auth_state['gitlab_user']['name']
c.KubeSpawner.pre_spawn_hook = pre_spawn_hook
auth:
type: gitlab
gitlab:
clientId: <client_id>
clientSecret: <client_secret>
callbackUrl: https://hub.example.com/hub/oauth_callback
state:
enabled: true
# generated
cryptoKey: ec2f09ac9a264823117b0131be51af250c2a5d33f16e1b3c08720cdc8f6a0ee9
singleuser:
defaultUrl: "/lab"
image:
name: quantstack/jupyter-singleuser-custom
tag: stable
extraEnv:
GITLAB_HOST: gitlab.example.com # without "https://"
lifecycleHooks:
postStart:
exec:
command:
- "sh"
- "-c"
- >
git clone https://gitlab.com/gitlab-org/nurtch-demo.git TEST_REPO || true;
echo "https://oauth2:${GITLAB_ACCESS_TOKEN}@${GITLAB_HOST}" > ~/.git-credentials;
git config --global http.sslVerify false;
git config --global credential.helper store;
git config --global user.email "${GITLAB_USER_EMAIL}";
git config --global user.name "${GITLAB_USER_NAME}";
jupyter serverextension enable --py jupyterlab_git
ingress:
enabled: true
hosts:
- jupyterhub.example.com
tls:
- hosts:
- jupyterhub.example.com
secretName: tls-secret
proxy:
secretToken: "184ae13d4e33c3c77aa66e1c27389d06a78dce9965a5d704d72488f8f2709799"
https:
hosts:
- hub.example.com
letsencrypt:
contactEmail: hub@example.com
service:
type: ClusterIP
```
To deploy a release for the chart:
```
helm upgrade --install jupyterhub jupyterhub/jupyterhub \
--namespace jupyterhub \
--version=0.9-4300ff5 \
--values values.yaml \
--timeout=10000000000 \
--debug --dry-run
```
## Create a GitLab application for authentication
To use the GitLab OAuth2 authentication with JupyterHub, go **Profile Settings > Applications** and select **New Application**.

More info at: https://docs.gitlab.com/ee/integration/oauth_provider.html
### Updating the JupyterHub deployment
- As a `callbackUrl`, use the JupyterHub endpoint.
- Update `auth.gitlab.clientId` and `auth.gitlab.clientSecret` in `values.yaml`.
- Update the deployment by running the `helm upgrade` command above.
### Using `git`
As a result of the GitLab integration, users are able to use their name and email address when committing and pushing to a repository.
The default Docker image ([quantstack/jupyter-singleuser-custom:stable](https://cloud.docker.com/u/quantstack/repository/docker/quantstack/jupyter-singleuser-custom)) adds the `jupyterlab-git` extension to enable UI:

It is also possible to use `git` from the command line:

## Using a custom single-user image
Custom single-user images can be built using a tool from the Jupyter Project called [repo2docker](https://repo2docker.readthedocs.io/en/latest/).
`repo2docker` creates a Docker image from a repository, and automatically installs the dependencies depending on the content of files such as `requirements.txt` and `environment.yml`.
An example of such image is available at: https://github.com/jtpio/jupyter-singleuser
## Next steps
- Create a base Docker image including all relevant `conda` and `pip` packages, as well as JupyterLab extensions.
- Create a CI pipeline to automatically build and push new Docker images to the internal registry when new dependencies have been added.
- Add profiles to choose a server on startup:

- Add GPU support
- Document best practices for working with Jupyter notebooks
- Explore the use of voilĂ for sharing web applications and dashboards between the research team and management.
- Synchronization of the user settings to GitLab (keep the `~/.jupyter` folder in sync).