Skip to content

Commit c02823f

Browse files
committed
docs: document pack setup for administrators
Closes #89. Adds five administrator pages to the existing Astro + Starlight site, covering the four questions the issue asks: Administration admin-setup cluster prerequisites, the single required value and everything derived from it, what the chart creates, the Keycloak bootstrap Job, and a verification checklist server-profiles profile_list sizing, profile_options image choices, access: all/yaml/keycloak gating, idle culling, and the two per-user PVCs nebi-integration what nebi-pack must already provide, the derived OIDC client IDs, the 3-step token exchange and its five inputs, both NetworkPolicies, workspace storage, and admin-provisioned registries mlflow-integration tracking URI, the egress rule, the client library, verification, and what the integration does not give you Reference values-reference field-by-field for keycloak, subdomains, nebariapp, singleuser, singleuserCuller, sharedStorage, nebi, rbac.bootstrap, jupyterhub.custom, and the upstream passthrough Content is derived from values.yaml, the templates, _helpers.tpl, and config/jupyterhub/*.py rather than paraphrased from the README, so the derivation rules and defaults match what the chart renders. Three things that are easy to get wrong get explicit callouts: - jupyterhub.hub.extraVolumes / extraVolumeMounts are lists, so overriding either drops the custom-config and oauth-client mounts and the hub silently falls back to dummy auth with an empty jupyterhub_config.d. - The base domain is keycloak.hostname minus its first label, so a single-label value derives an empty hub hostname, Nebi URL, and token URL - the chart renders and nothing routes. - z2jh defaults singleuser egress to deny private IPs, and a NetworkPolicy rule must name the pod port (5000 for MLflow), not the Service port, because the rule is evaluated after kube-proxy has already translated the ClusterIP. Existing pages are touched as little as possible so the other open docs PRs stay mergeable: configuration.md gains a "Detailed guides" list appended below its section table, and index.md gains an Administration section. Neither restructures what is there. Also adds a Documentation section to the README, which had no link to the published site.
1 parent a1e49ab commit c02823f

9 files changed

Lines changed: 1139 additions & 1 deletion

File tree

‎README.md‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,39 @@ To release a new version:
160160
161161
**Note:** Enable GitHub Pages on the `gh-pages` branch in repo settings after the first release.
162162
163+
## Documentation
164+
165+
The docs site lives in [`docs/`](docs/) and is built with [Astro](https://astro.build) +
166+
[Starlight](https://starlight.astro.build) using the shared `@nebari/starlight` theme. It
167+
deploys to [packs.nebari.dev/data-science-pack/](https://packs.nebari.dev/data-science-pack/)
168+
on every merge to `main`; pull requests that touch `docs/` get a preview URL posted as a
169+
comment.
170+
171+
Administrator guides:
172+
173+
- [Admin setup](https://packs.nebari.dev/data-science-pack/admin-setup/) - cluster
174+
prerequisites, the one required value, and what the chart creates.
175+
- [Values reference](https://packs.nebari.dev/data-science-pack/values-reference/) -
176+
field-by-field detail for every value.
177+
- [Server profiles](https://packs.nebari.dev/data-science-pack/server-profiles/) - sizing
178+
JupyterLab servers and gating profiles by group.
179+
- [Nebi integration](https://packs.nebari.dev/data-science-pack/nebi-integration/) - images,
180+
OIDC clients, token exchange, registries.
181+
- [MLflow integration](https://packs.nebari.dev/data-science-pack/mlflow-integration/) -
182+
letting notebooks log experiments to MLflow.
183+
184+
```bash
185+
cd docs
186+
npm ci
187+
npm run dev # dev server with hot reload at http://localhost:4321
188+
npm run build # static build into docs/dist/
189+
npm test # unit tests
190+
```
191+
192+
Pages live in `docs/src/content/docs/` - each `.md` or `.mdx` file becomes a page, and the
193+
sidebar is configured in `docs/astro.config.mjs`. See [`docs/README.md`](docs/README.md) for
194+
details.
195+
163196
## License
164197

165198
Apache License 2.0 - see [LICENSE](LICENSE) for details.

‎docs/astro.config.mjs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,20 @@ export default defineConfig({
3333
{ label: 'Shared Storage', slug: 'shared-storage' },
3434
],
3535
},
36+
{
37+
label: 'Administration',
38+
items: [
39+
{ label: 'Admin setup', slug: 'admin-setup' },
40+
{ label: 'Server profiles', slug: 'server-profiles' },
41+
{ label: 'Nebi integration', slug: 'nebi-integration' },
42+
{ label: 'MLflow integration', slug: 'mlflow-integration' },
43+
],
44+
},
3645
{
3746
label: 'Reference',
3847
items: [
3948
{ label: 'Configuration', slug: 'configuration' },
49+
{ label: 'Values reference', slug: 'values-reference' },
4050
{ label: 'NebariApp Integration', slug: 'nebariapp-integration' },
4151
],
4252
},
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
---
2+
title: Admin setup
3+
description: Deploying and configuring the Data Science Pack as a cluster administrator.
4+
---
5+
6+
This is the administrator's entry point: what the chart needs from the cluster, what it
7+
derives on its own, and where each knob lives. For a five-minute install, start with
8+
[Quick Start](/quick-start/) instead.
9+
10+
## What the cluster must provide
11+
12+
| Requirement | Why | Optional? |
13+
|---|---|---|
14+
| [nebari-operator](https://github.com/nebari-dev/nebari-operator) | Reconciles the `NebariApp` into routing, TLS, and a Keycloak OIDC client | Yes — set `nebariapp.enabled: false` |
15+
| Envoy Gateway | The `NebariApp`'s HTTPRoute attaches to it | With the operator |
16+
| cert-manager | Issues the TLS certificate for the hub hostname | With the operator |
17+
| Keycloak (`bitnami/keycloakx`) | Identity provider; the operator provisions the hub client in it | With the operator |
18+
| A ReadWriteMany StorageClass | Per-group shared directories | Yes — see [Shared Storage](/shared-storage/) |
19+
| A default (RWO) StorageClass | Per-user home PVCs and Nebi workspace PVCs | No |
20+
| Namespace label `nebari.dev/managed=true` | The operator ignores `NebariApp`s in unlabeled namespaces | No, when the operator is used |
21+
22+
Without the operator the chart still installs — dummy authenticator, no routing, no shared
23+
Keycloak. That is the local-development path, not a deployment mode.
24+
25+
## One required field
26+
27+
The chart is built around a single input. Everything else is derived by subdomain
28+
convention and can be overridden individually:
29+
30+
```yaml
31+
keycloak:
32+
hostname: keycloak.example.com
33+
```
34+
35+
From that one value:
36+
37+
| Derived | Rule | Example |
38+
|---|---|---|
39+
| Base domain | `keycloak.hostname` minus its first label | `example.com` |
40+
| Hub hostname | `<subdomains.hub>.<base>` | `hub.example.com` |
41+
| Nebi external URL | `https://<subdomains.nebi>.<base>` | `https://nebi.example.com` |
42+
| Keycloak token URL | `https://<keycloak.hostname>/realms/<realm>/…/token` | — |
43+
| Hub OIDC client ID | `jupyterhub-<release>-<chart>` | `jupyterhub-data-science-pack-nebari-data-science-pack` |
44+
| Nebi OIDC client ID | `nebi-<nebi.releaseName>-nebari-nebi-pack` | `nebi-nebi-pack-nebari-nebi-pack` |
45+
46+
:::caution[Derivation needs a dotted hostname]
47+
The base domain is `keycloak.hostname` with its first label stripped. A single-label value
48+
like `keycloak` yields an empty base domain, and the hub hostname, Nebi URL, and token URL
49+
all come out empty — the chart renders, and nothing routes. Set `nebariapp.hostname` and
50+
`nebi.remoteURL` explicitly in that case.
51+
:::
52+
53+
## Install
54+
55+
```bash
56+
helm repo add nebari https://raw.githubusercontent.com/nebari-dev/helm-repository/gh-pages/
57+
helm repo update
58+
59+
kubectl create namespace data-science
60+
kubectl label namespace data-science nebari.dev/managed=true
61+
62+
helm install data-science-pack nebari/nebari-data-science-pack \
63+
--namespace data-science \
64+
--set keycloak.hostname=keycloak.example.com
65+
```
66+
67+
Also available as an OCI artifact:
68+
69+
```bash
70+
helm install data-science-pack \
71+
oci://quay.io/nebari/charts/nebari-data-science-pack --version <version>
72+
```
73+
74+
:::note[The release name is load-bearing]
75+
Two things are derived from it: the hub OIDC client ID, and the Secret name the hub mounts
76+
at `/etc/oauth`
77+
(`{Release.Name}-{Chart.Name}-oidc-client`, hardcoded in
78+
`jupyterhub.hub.extraVolumes`). Installing under a non-default release name means updating
79+
that `secretName` — and the matching `secretKeyRef` under `jupyterhub.hub.extraEnv` — or the
80+
hub silently falls back to dummy auth.
81+
:::
82+
83+
## Where each knob lives
84+
85+
Configuration splits across three layers. Knowing which one you are in explains most
86+
"my value did nothing" reports.
87+
88+
| Layer | Path | What it is |
89+
|---|---|---|
90+
| Chart values | `keycloak`, `subdomains`, `nebariapp`, `singleuser`, `singleuserCuller`, `sharedStorage`, `nebi`, `rbac` | This chart's own values |
91+
| Chart-derived hub config | `jupyterhub.custom.*` | Read by the Python files in `jupyterhub_config.d/` via `get_chart_config()` |
92+
| Upstream passthrough | everything else under `jupyterhub.*` | Handed verbatim to [Zero to JupyterHub](https://z2jh.jupyter.org/) |
93+
94+
Field-by-field detail for all three is in the [Values reference](/values-reference/).
95+
96+
:::caution[Two z2jh lists replace rather than merge]
97+
`jupyterhub.hub.extraVolumes` and `extraVolumeMounts` are lists, so overriding either
98+
**replaces** the chart's entries. Both carry required mounts — `custom-config`
99+
(the `jupyterhub_config.d` ConfigMap) and `oauth-client` (the OIDC Secret). Drop them and
100+
the hub comes up with an empty config directory and dummy auth.
101+
102+
Re-include the chart's entries in any override. The same applies to
103+
`jupyterhub.hub.initContainers`, which carries the CA-bundle merge step.
104+
:::
105+
106+
## What the chart creates
107+
108+
Beyond the z2jh subchart's own objects:
109+
110+
| Object | Template | Purpose |
111+
|---|---|---|
112+
| `NebariApp` | `nebariapp.yaml` | Routing, TLS, Keycloak client, landing-page card |
113+
| Hub config ConfigMap | `hub-config.yaml` | The four `jupyterhub_config.d/` Python files |
114+
| Singleuser config ConfigMap | `singleuser-config.yaml` | Per-pod config mounted by the spawner |
115+
| Nebi config ConfigMap | `singleuser-nebi-config.yaml` | Admin-provisioned Nebi registries — only when customized |
116+
| Shared PVC (+ NFS server) | `shared-pvc.yaml`, `nfs-server.yaml` | Per-group shared storage |
117+
| NFS client installer | `nfs-client-installer.yaml` | DaemonSet installing `nfs-common`, opt-in |
118+
| Keycloak RBAC bootstrap Job | `keycloak-rbac-bootstrap-job.yaml` | post-install/upgrade hook; groups mapper + shared-mount role |
119+
| Two NetworkPolicies | `singleuser-gateway-egress.yaml`, `hub-nebi-networkpolicy.yaml` | Egress the subchart's policy does not cover |
120+
121+
## The Keycloak bootstrap job
122+
123+
`rbac.bootstrap.enabled` defaults to `true`. It runs as a post-install/post-upgrade hook in
124+
the `keycloak` namespace, authenticates with the admin credentials Secret, and is
125+
idempotent — it skips cleanly when `kcAdminCredentialSecret` is unset, so the chart still
126+
installs on clusters that have not surfaced one.
127+
128+
It does four things:
129+
130+
1. Adds the `oidc-group-membership-mapper` to the `groups` client scope. Without it the
131+
`groups` claim is empty, and both shared storage and `access: yaml` profile gating
132+
silently fall back to "no groups".
133+
2. Creates the `allow-group-directory-creation-role` client role on the hub client.
134+
3. Enables `serviceAccountsEnabled` on the hub client and binds
135+
`realm-management.{view-clients,view-groups,view-realm}` to its service account.
136+
4. Assigns the shared-mount role to the groups listed in `rbac.bootstrap.sharedMountGroups`.
137+
138+
Set `enabled: false` for BYO-Keycloak or local development. Override `namespace`,
139+
`kcAdminCredentialSecret`, and `kcHost` for non-bitnami Keycloak layouts.
140+
141+
## Integrations
142+
143+
- **[Nebi](/nebi-integration/)** — the environment manager. Ships into user pods via an init
144+
container and needs a matching OIDC client for token exchange.
145+
- **[MLflow](/mlflow-integration/)** — experiment tracking. Two values, one of which is a
146+
NetworkPolicy that has to name the pod port rather than the service port.
147+
- **[NebariApp](/nebariapp-integration/)** — the CRD fields this chart sets and why.
148+
149+
## User-facing configuration
150+
151+
- **[Server profiles](/server-profiles/)** — sizes, images, and per-group gating.
152+
- **[Shared Storage](/shared-storage/)** — per-group directories and RWX requirements.
153+
154+
## Verify a deployment
155+
156+
```bash
157+
kubectl -n data-science get pods
158+
kubectl -n data-science get nebariapp,httproute,certificate
159+
160+
# The operator only acts on labeled namespaces
161+
kubectl get namespace data-science -o jsonpath='{.metadata.labels}'
162+
163+
# The hub reads its OAuth client from this Secret; absent means dummy auth
164+
kubectl -n data-science get secret data-science-pack-nebari-data-science-pack-oidc-client
165+
166+
# Did the Keycloak bootstrap hook succeed?
167+
kubectl -n keycloak get jobs -l app.kubernetes.io/instance=data-science-pack
168+
```
169+
170+
Then log in through Keycloak and check that the profile selector appears with the sizes you
171+
expect. An empty or unexpectedly short list usually means the `groups` claim is missing —
172+
see [Server profiles](/server-profiles/#gating-profiles-by-group).

‎docs/src/content/docs/configuration.md‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,21 @@ Every derived value can still be overridden explicitly. Values under
2323

2424
For the full set of fields and their defaults, read
2525
[`values.yaml`](https://github.com/nebari-dev/data-science-pack/blob/main/values.yaml)
26-
directly — it is heavily commented and is the source of truth.
26+
directly — it is heavily commented and is the source of truth. The
27+
[Values reference](/values-reference/) covers the same ground field by field, including the
28+
derivation rules and the defaults worth knowing about before you override them.
29+
30+
## Detailed guides
31+
32+
- [Admin setup](/admin-setup/) — cluster prerequisites, the derivation model, and what the
33+
chart creates.
34+
- [Values reference](/values-reference/) — every field in every section.
35+
- [Server profiles](/server-profiles/) — `jupyterhub.custom.profiles`, image choices, and
36+
per-group gating.
37+
- [Nebi integration](/nebi-integration/) — the `nebi.*` values and the token exchange behind
38+
them.
39+
- [MLflow integration](/mlflow-integration/) — `jupyterhub.singleuser.extraEnv` plus an
40+
egress rule.
2741

2842
## Local development
2943

‎docs/src/content/docs/index.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,17 @@ Nebari's custom images, per-group shared storage, and integration with the
2828
- [Architecture](/architecture/) — how the proxy, hub, jhub-apps, and user pods fit together.
2929
- [Shared Storage](/shared-storage/) — per-group directories, StorageClass requirements, and the transitional NFS mode.
3030

31+
## Administration
32+
33+
- [Admin setup](/admin-setup/) — cluster prerequisites, the one required value, and what the chart creates.
34+
- [Server profiles](/server-profiles/) — sizing JupyterLab servers and gating profiles by group.
35+
- [Nebi integration](/nebi-integration/) — wiring the environment manager: images, OIDC, registries.
36+
- [MLflow integration](/mlflow-integration/) — letting notebooks log experiments to MLflow.
37+
3138
## Reference
3239

3340
- [Configuration](/configuration/) — the top-level `values.yaml` sections.
41+
- [Values reference](/values-reference/) — field-by-field detail for every value.
3442
- [NebariApp Integration](/nebariapp-integration/) — the CRD fields this chart sets and why.
3543

3644
Source, issues, and the full `values.yaml` live in the

0 commit comments

Comments
 (0)