Skip to content

Commit

Permalink
Merge pull request 2i2c-org#4205 from GeorgianaElena/document-binderh…
Browse files Browse the repository at this point in the history
…ub-demo-ui

Add runbook on binderhub-ui hub
  • Loading branch information
GeorgianaElena authored Jun 13, 2024
2 parents 7a044fc + ba4d8cf commit c8ce880
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 11 deletions.
197 changes: 197 additions & 0 deletions docs/howto/features/binderhub-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# Make binderhub-ui hub

We can support users who want to build, push and launch user images, from open source GitHub repositories, from an UI similar with [mybinder.org](https://mybinder.org).

We call this a `binderhub-ui` style hub and the primary features offered would be:

- **User authentication**
- **NO Persistent storage**
- **BinderHub style UI**

## Important architectural decisions made

1. **Two separate domains, one for binderhub UI and one for JupyterHub**
Keeping them separate will help with having clean and correct sharing URLs without having them be based off the `hub/services/:name` path.

1. **A logged out homepage, and a logged-in homepage**
See https://github.com/2i2c-org/infrastructure/issues/4168 for context on this decision

## Initial hub setup

Follow the steps in the [Initial Hub setup](hub-deployment-guide:runbooks:phase3.1) guide to get started on a very basic hub setup or copy-paste the configuration of a similar hub, then follow the steps below.

## Setup jupyterhub.custom config

Under `jupyterhub.custom` there are a few configuration options that need to be set in the following way:

1. disable the `jupyterhub-configurator`
1. enable the `binderhubUI`, which will in turn enable the hub to use [BinderSpawnerMixin](https://github.com/jupyterhub/binderhub/blob/bd297b2c3f713cf46b0b22cfabc86d8140bbed41/helm-chart/binderhub/values.yaml#L115-L207) that allows converting JupyterHub container spawners into BinderHub spawners
1. use a simpler landing page, i.e. the page where users land to login into the hub before actually seeing the binderhub UI. This is done by having the hub track the `no-homepage-subsections` branch of the default homepage repo
1. disable `singleuserAdmin.extraVolumeMounts` (no persistent storage so these don't make sense)

```yaml
jupyterhub:
custom:
jupyterhubConfigurator:
enabled: false
binderhubUI:
enabled: true
homepage:
gitRepoBranch: "no-homepage-subsections"
templateVars:
(...)
singleuserAdmin:
extraVolumeMounts: []
```
## Disable user storage
There will be no persistent storage, so disable it. Because of this `singleuser.extraVolumeMounts` and `singleuser.initContainers` should also be emptied.

```yaml
jupyterhub:
singleuser:
storage:
type: none
extraVolumeMounts: []
initContainers: []
```

## Configure the hub services, roles and spawner

There are a few configurations that need to be made on the hub side:

1. setup `binder` as a jupyterhub externally managed service
1. setup a `binder` and a `user` role and make sure the correct permissions are being assigned to this new service but also to the users so that they can access the service.
1. enable authenticated binderhub spawner setup via `hub.config.BinderSpawnerMixin.auth_enabled`

```yaml
jupyterhub:
hub:
redirectToServer: false
services:
binder:
# skip the OAuth confirmation page
oauth_no_confirm: true
# the service will have a public url instead of being contacted via /services/:name
oauth_redirect_uri: https://<binderhub-public-url>/oauth_callback
loadRoles:
# The role binder allows service binder to start and stop servers
# and read (but not modify) any user’s model
binder:
services:
- binder
scopes:
- servers
- read:users # admin:users is required if authentication isn't enabled
# The role user allows access to the user’s own resources and to access
# only the binder service
user:
scopes:
- self
# Admin users will by default have access:services, so this is only
# observed to be required for non-admin users.
- access:services!service=binder
config:
BinderSpawnerMixin:
auth_enabled: true
```

## Setup the image registry

Follow the guide at [](howto:features:imagebuilding-hub:image-registry) of the imagebuilding hub.

## Setup the `binderhub-service` chart

We will use the [binderhub-service](https://github.com/2i2c-org/binderhub-service/) Helm chart to run BinderHub, the Python software, as a standalone service to build and push images with [repo2docker](https://github.com/jupyterhub/repo2docker), next to JupyterHub.

We need to configure it so that it:

1. setup an https reachable domain for the binder service via enabling and configuring `binderhub-service.ingress`
1. configure BinderHub so that it's:
- not running in an API only mode
- it's configured for an authentication use-case
- it knows about where the hub is running
- knows the prefix to use for the images before it pushed them to the registry
1. setup the the credentials needed to push the image to the container registry by the build pods
1. setup some env variables that the pieces of the jupyterhub software that the binderhub software runs will need.

1. Setup the `binderhub-service` config

```yaml
binderhub-service:
enabled: true
# enable the binderhub-service ingress and configure it
# so that we get a public domain for binderhub to run
ingress:
enabled: true
hosts:
- <binderhub-public-url>.2i2c.cloud
tls:
- secretName: binder-https-auto-tls
hosts:
- <binderhub-public-url>.2i2c.cloud
config:
BinderHub:
base_url: /
hub_url: https://<jupyterhub-public-url>.2i2c.cloud
badge_base_url: https://<binderhub-public-url>.2i2c.cloud
auth_enabled: true
enable_api_only_mode: false
image_prefix: <repository_path>
buildPodsRegistryCredentials:
# registry server address like https://quay.io or https://us-central1-docker.pkg.dev
server: <server_address>
# robot account namer or "_json_key" if using grc.io
username: <account_name>
extraConfig:
# FIXME: set KubernetesBuildExecutor.push_secret again
# without this for some reason the build pods
# search after the wrong secret name (i.e. the default name)
# set by binderhub in KubernetesBuildExecutor.push_secret
01-binderhub-service-set-push-secret: |
import os
c.KubernetesBuildExecutor.push_secret = os.environ["PUSH_SECRET_NAME"]
extraEnv:
- name: JUPYTERHUB_API_TOKEN
valueFrom:
# Any JupyterHub Services api_tokens are exposed in this k8s Secret
secretKeyRef:
name: hub
key: hub.services.binder.apiToken
- name: JUPYTERHUB_CLIENT_ID
value: "service-binder"
- name: JUPYTERHUB_API_URL
value: "https://<hub-public-url>.2i2c.cloud/hub/api"
# Without this, the redirect URL to /hub/api/... gets
# appended to binderhub's URL instead of the hub's
- name: JUPYTERHUB_BASE_URL
value: "https://<hub-public-url>.2i2c.cloud/"
- name: JUPYTERHUB_OAUTH_CALLBACK_URL
value: "https://<binderhub-public-url>.2i2c.cloud/oauth_callback"
```

2. Sops-encrypt and store the password for accessing the image registry, in the `enc-<hub>.secret.values.yaml` file.

You should have the password for accessing the image registry from a previous step.

```yaml
binderhub-service:
buildPodsRegistryCredentials:
password: <password>
```

3. If pushing to quay.io registry, also setup the credentials for image pulling

When pushing to the quay registry, the images are pushed as `private` by default (even if the plan doesn't allow it).

A quick workaround for this, is to use the robot's account credentials to also set [`imagePullSecret`](https://z2jh.jupyter.org/en/stable/resources/reference.html#imagepullsecret) in the `enc-<hub>.secret.values.yaml`:

```yaml
jupyterhub:
imagePullSecret:
create: true
registry: quay.io
username: <robot_account_name>
password: <password>
```
2 changes: 2 additions & 0 deletions docs/howto/features/imagebuilding.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
(howto:features:imagebuilding-hub)=
# Make an imagebuilding hub

We can support users who want to build, push and launch user images, from open source GitHub repositories, similar with what [mybinder.org](https://mybinder.org) does, but from within their JupyterHubs
Expand Down Expand Up @@ -69,6 +70,7 @@ jupyterhub:
image: quay.io/jupyter/scipy-notebook:2024-03-18
```

(howto:features:imagebuilding-hub:image-registry)=
## Setup the image registry

### For GCP (via terraform)
Expand Down
1 change: 1 addition & 0 deletions docs/howto/features/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ See the sections below for more details.
allow-unlisted-profile-choice.md
profile-list-restrict.md
anonymized-usernames.md
binderhub-ui.md
buckets.md
cloud-access.md
cryptnono.md
Expand Down
31 changes: 20 additions & 11 deletions docs/hub-deployment-guide/runbooks/phase3/initial-hub-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,30 @@ All of the following steps must be followed in order to consider phase 3.1 compl

1. **Create the relevant `values.yaml` file/s under the appropriate cluster directory**

If the cluster will have multiple hubs, and chances are it will as there's a common 2i2c practice to always deploy a staging hub alongside a production one, then create two values.yaml files under the appropriate cluster directory.
```bash
export CLUSTER_NAME=cluster-name;
export HUB_NAME=hub-name
```

- One file will hold the common hubs configuration and one will hold the specific hub configuration.
- If the cluster has currently no hubs

```bash
export CLUSTER_NAME=cluster-name;
export HUB_NAME=hub-name
```
but will have more than one (and chances are it will as this is a common 2i2c practice to always deploy a staging hub alongside a production one), then create two values.yaml files under the appropriate cluster directory.

- Make sure you are in the root of the infrastructure repository and run:
- One file will hold the common hubs configuration and one will hold the specific hub configuration.

```bash
touch ./config/clusters/$CLUSTER_NAME/$HUB_NAME.values.yaml;
touch ./config/clusters/$CLUSTER_NAME/common.values.yaml
```
- Make sure you are in the root of the infrastructure repository and run:

```bash
touch ./config/clusters/$CLUSTER_NAME/$HUB_NAME.values.yaml;
touch ./config/clusters/$CLUSTER_NAME/common.values.yaml
```

- If you're adding a hub to an existing cluster with hubs on it
then create only one hub to hold the specific hub configuration via
```bash
touch ./config/clusters/$CLUSTER_NAME/$HUB_NAME.values.yaml
```
1. **Determine the address of the storage server that a hub on this cluster should use to connect to it**
Expand Down

0 comments on commit c8ce880

Please sign in to comment.