--- title: 'JupyterHub with GitLab integration' disqus: hackmd --- JupyterHub with GitLab integration ================================== ## Table of Contents [TOC] ## Overview ![](https://i.imgur.com/97qSYAt.png) ## 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 ![](https://i.imgur.com/96FxlKq.png) 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**. ![](https://i.imgur.com/eGjZaKv.png) 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: ![](https://i.imgur.com/3tJUWeB.png) It is also possible to use `git` from the command line: ![](https://i.imgur.com/LHSRBXK.png) ## 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: ![](https://i.imgur.com/Np0DxCP.png) - 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).