From 427825404220067b362d0a55456f73d382ecdb0c Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Fri, 28 Feb 2025 16:08:57 +0100 Subject: [PATCH 01/19] initial page --- modules/tutorials/nav.adoc | 1 + modules/tutorials/pages/jupyterhub.adoc | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 modules/tutorials/pages/jupyterhub.adoc diff --git a/modules/tutorials/nav.adoc b/modules/tutorials/nav.adoc index aa1004100..b5b5a170a 100644 --- a/modules/tutorials/nav.adoc +++ b/modules/tutorials/nav.adoc @@ -1,3 +1,4 @@ * xref:index.adoc[] ** xref:authentication_with_openldap.adoc[] +** xref:jupyterhub.adoc[] ** xref:logging-vector-aggregator.adoc[] diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc new file mode 100644 index 000000000..3d41b6091 --- /dev/null +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -0,0 +1,5 @@ += JupyterHub +:description: A tutorial on how to configure various aspects of JupyterHub on Kubernetes. +:keywords: notebook, JupyterHub, Kubernetes, k8s, Spark, HDFS, S3 + +This tutorial illustrates various configuration settings when using JupyterHub on Kubernetes. From 8996e92db61dc35d9ba5e78811b5590458063a53 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 3 Mar 2025 12:51:24 +0100 Subject: [PATCH 02/19] keycloak notes --- modules/tutorials/pages/jupyterhub.adoc | 248 +++++++++++++++++++++++- 1 file changed, 247 insertions(+), 1 deletion(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 3d41b6091..6b8fddbe1 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -2,4 +2,250 @@ :description: A tutorial on how to configure various aspects of JupyterHub on Kubernetes. :keywords: notebook, JupyterHub, Kubernetes, k8s, Spark, HDFS, S3 -This tutorial illustrates various configuration settings when using JupyterHub on Kubernetes. +.Drop-down example +[%collapsible] +==== +xxx: + +[source,console] +---- +xxx +---- +==== + +This tutorial illustrates various scenarios and configuration options when using JupyterHub on Kubernetes. +The custom resources and configuration settings that are discussed here are based on the xref:demos:jupyterhub-keycloak.adoc[JupyterHub-Keycloak demo], so you may find it helpful to have that demo running to reference things as you read through this tutorial. + +== Keycloak + +Keycloak is installed using a https://github.com/stackabletech/demos/blob/feat/keycloak-jupyterhub/stacks/jupyterhub-keycloak/keycloak.yaml[Deployment] that loads realm configuration mounted as a ConfigMap. + +=== Services + +In the demo, the keycloak and jupyter hub service (proxy-public) ports are fixed e.g. + +[source,yaml] +--- +apiVersion: v1 +kind: Service +metadata: + name: keycloak + labels: + app: keycloak +spec: + type: NodePort + selector: + app: keycloak + ports: + - name: https + port: 8443 + targetPort: 8443 + nodePort: 31093 # <1> +---- + +<1> Static value for the purposed of the demo. + +They are: + +- `31093` for keycloak +- `31095` for jupyterhub/proxy-public + +The keycloak and jupyterhub endpoints are defined in the jupyter hub chart values i.e. for the purposes of the demo (that does not use any pre-defined DNS settings), the ports have to be known before the jupyter hub components are deployed. + +This can be achieved by having the keycloak deployment write out its co-ordinates into a ConfigMap during start-up, which can then be referenced by the JupyterHub chart like this: + +[source,yaml] +--- +options: + hub: + config: + ... + extraEnv: + ... + KEYCLOAK_NODEPORT_URL: + valueFrom: + configMapKeyRef: + name: keycloak-address + key: keycloakAddress # <1> + KEYCLOAK_NODE_IP: + valueFrom: + configMapKeyRef: + name: keycloak-address + key: keycloakNodeIp + ... + extraConfig: + ... + 03-set-endpoints: | + import os + from oauthenticator.generic import GenericOAuthenticator + keycloak_url = os.getenv("KEYCLOAK_NODEPORT_URL") # <2> + ... + keycloak_node_ip = os.getenv("KEYCLOAK_NODE_IP") + ... + c.GenericOAuthenticator.oauth_callback_url: f"http://{keycloak_node_ip}:31095/hub/oauth_callback" # <3> + c.GenericOAuthenticator.authorize_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/auth" + c.GenericOAuthenticator.token_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/token" + c.GenericOAuthenticator.userdata_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/userinfo" +---- + +<1> endpoint information read from the ConfigMap +<2> this information is passed to a variable in one of the start-up config scripts... +<3> ...and then used for JupyterHub settings + +=== Discovery + +As mentioned above, keycloak writes out it endpoint information to ConfigMap, like this: + +[source,yaml] +---- +--- +apiVersion: apps/v1 +kind: Deployment +... + spec: + containers: + ... + - name: create-configmap + resources: {} + image: oci.stackable.tech/sdp/testing-tools:0.2.0-stackable0.0.0-dev + command: ["/bin/bash", "-c"] + args: + - | + pid= + trap 'echo SIGINT; [[ $pid ]] && kill $pid; exit' SIGINT + trap 'echo SIGTERM; [[ $pid ]] && kill $pid; exit' SIGTERM + + while : + do + echo "Determining Keycloak public reachable address" + KEYCLOAK_ADDRESS=$(kubectl get svc keycloak -o json | jq -r --argfile endpoints <(kubectl get endpoints keycloak -o json) --argfile nodes <(kubectl get nodes -o json) '($nodes.items[] | select(.metadata.name == $endpoints.subsets[].addresses[].nodeName) | .status.addresses | map(select(.type == "ExternalIP" or .type == "InternalIP")) | min_by(.type) | .address | tostring) + ":" + (.spec.ports[] | select(.name == "https") | .nodePort | tostring)') + echo "Found Keycloak running at $KEYCLOAK_ADDRESS" + + if [ ! -z "$KEYCLOAK_ADDRESS" ]; then + KEYCLOAK_HOSTNAME="$(echo $KEYCLOAK_ADDRESS | grep -oP '^[^:]+')" + KEYCLOAK_PORT="$(echo $KEYCLOAK_ADDRESS | grep -oP '[0-9]+$')" + + cat << EOF | kubectl apply -f - + apiVersion: v1 + kind: ConfigMap + metadata: + name: keycloak-address + data: + keycloakAddress: "$KEYCLOAK_HOSTNAME:$KEYCLOAK_PORT" + keycloakNodeIp: "$KEYCLOAK_HOSTNAME" + EOF + fi + + sleep 30 & pid=$! + wait + done +---- + +=== Security + +We create a keystore with a self-generated and self-signed certificate and mount it so that the keystore file can be used when starting keycloak: + +[source,yaml] +---- + spec: + containers: + - name: keycloak + ... + args: + - start + - --hostname-strict=false + - --https-key-store-file=/tls/keystore.p12 # <3> + - --https-key-store-password=changeit + - --import-realm + volumeMounts: + - name: tls + mountPath: /tls/ # <2> + ... + volumes: + ... + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/class: tls # <1> + secrets.stackable.tech/format: tls-pkcs12 + secrets.stackable.tech/format.compatibility.tls-pkcs12.password: changeit + secrets.stackable.tech/scope: service=keycloak,node + spec: + storageClassName: secrets.stackable.tech + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "1" +---- + +<1> Create a volume holding the self-signed certificate information +<2> Mount this volume for keycloak to use +<3> Pass the keystore file as an argument on start-up + +For the self-signed certificate to be accepted during the handshake between JupyterHub and Keycloak it is important to create the jupyterhub-side certificate using the same secret class, although the format can be a different one: + +[source,yaml] +---- + extraVolumes: + - name: tls-ca-cert + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/class: tls + spec: + storageClassName: secrets.stackable.tech + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "1" +---- + +=== Realm + +The Keycloak https://github.com/stackabletech/demos/blob/feat/keycloak-jupyterhub/stacks/jupyterhub-keycloak/keycloak-realm-config.yaml for the demo basically contains a set of users and groups, along with a simple client definition: + +[source,yaml] +---- +"clients" : [ { + "clientId": "jupyterhub", + "enabled": true, + "protocol": "openid-connect", + "clientAuthenticatorType": "client-secret", + "secret": ..., + "redirectUris" : [ "*" ], + "webOrigins" : [ "*" ], + "standardFlowEnabled": true + } ] +---- + +Not that the standard flow is enabled and no other OAuth-specific settings are required. +Wildcards are used for redirectUris and webOrigins, mainly for the sake of simplicity: in production environments this would typically be limited or filtered in an appropriate way. + +== JupyterHub + +=== Authentication + +==== Native Authenticator + +==== OAuth Authenticator (Keycloak) + +=== Certificates + +=== Driver Service + +=== Endpoints + +=== Profiles + +== Images + +== Example Notebook + +=== Provisos + +=== Overview \ No newline at end of file From 89e7557714248c527f2134edc681936085e4c0bd Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 3 Mar 2025 13:08:20 +0100 Subject: [PATCH 03/19] removed not-yet-existent link --- modules/tutorials/pages/jupyterhub.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 6b8fddbe1..806dcfa77 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -14,7 +14,7 @@ xxx ==== This tutorial illustrates various scenarios and configuration options when using JupyterHub on Kubernetes. -The custom resources and configuration settings that are discussed here are based on the xref:demos:jupyterhub-keycloak.adoc[JupyterHub-Keycloak demo], so you may find it helpful to have that demo running to reference things as you read through this tutorial. +The custom resources and configuration settings that are discussed here are based on the JupyterHub-Keycloak demo, so you may find it helpful to have that demo running to reference things as you read through this tutorial. == Keycloak From f8bfb8563d6ae4af3b8112732a40f8926419ac03 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 3 Mar 2025 14:02:51 +0100 Subject: [PATCH 04/19] linting --- modules/tutorials/pages/jupyterhub.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 806dcfa77..97cbe4141 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -248,4 +248,4 @@ Wildcards are used for redirectUris and webOrigins, mainly for the sake of simpl === Provisos -=== Overview \ No newline at end of file +=== Overview From b850bd5c9997b6eba4453589db73fff80431d289 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 3 Mar 2025 18:05:20 +0100 Subject: [PATCH 05/19] wip: native auth --- .../tutorials/images/jupyterhub/admin-4.x.png | Bin 0 -> 59648 bytes .../images/jupyterhub/admin-user.png | Bin 0 -> 67134 bytes .../tutorials/images/jupyterhub/sign-up.png | Bin 0 -> 34887 bytes modules/tutorials/pages/jupyterhub.adoc | 45 ++++++++++++++++-- 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 modules/tutorials/images/jupyterhub/admin-4.x.png create mode 100644 modules/tutorials/images/jupyterhub/admin-user.png create mode 100644 modules/tutorials/images/jupyterhub/sign-up.png diff --git a/modules/tutorials/images/jupyterhub/admin-4.x.png b/modules/tutorials/images/jupyterhub/admin-4.x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe809a24800198a0e0e367d865017707f0bb7596 GIT binary patch literal 59648 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≀c?9`>t6_{ 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 Q(jOZQ^h~Z%bZ&?L7o)wz#Q*>R literal 0 HcmV?d00001 diff --git a/modules/tutorials/images/jupyterhub/admin-user.png b/modules/tutorials/images/jupyterhub/admin-user.png new file mode 100644 index 0000000000000000000000000000000000000000..4a425134260b1d57974f4145e9a55abdeffe6202 GIT binary patch literal 67134 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�Cq5nyRSG0 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 literal 0 HcmV?d00001 diff --git a/modules/tutorials/images/jupyterhub/sign-up.png b/modules/tutorials/images/jupyterhub/sign-up.png new file mode 100644 index 0000000000000000000000000000000000000000..7e0c9f9103fa27dc4b14df5f4ca63fe216a062b9 GIT binary patch literal 34887 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 literal 0 HcmV?d00001 diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 97cbe4141..e791e97bc 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -224,22 +224,61 @@ The Keycloak https://github.com/stackabletech/demos/blob/feat/keycloak-jupyterhu ---- Not that the standard flow is enabled and no other OAuth-specific settings are required. -Wildcards are used for redirectUris and webOrigins, mainly for the sake of simplicity: in production environments this would typically be limited or filtered in an appropriate way. +Wildcards are used for `redirectUris` and `webOrigins`, mainly for the sake of simplicity: in production environments this would typically be limited or filtered in an appropriate way. == JupyterHub === Authentication +This tutorial covers two methods of authentication: Native and OAuth. +Other implementations are documented https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html[here]. + ==== Native Authenticator +This tutorial and the accompanying demo assume that Keycloak is used for user authentication. +However, a simpler alternative is to use the Native Authenticator that allows users to be added "on-the-fly". + +[source,yaml] +---- +options: + hub: + config: + Authenticator: + allow_all: true + admin_users: + - admin + JupyterHub: + authenticator_class: nativeauthenticator.NativeAuthenticator + NativeAuthenticator: + open_signup: true + proxy: + ... +---- + +Users must either be included in an `allowed_users` list, or the property `allow_all` must be set to `true`. +The creation of new users will be checked against these settings and refused if appropriate. +If an admin_users property is defined, then associated users will see an additional tab on the JupyterHub home screen, allowing them to carry out user management actions (e.g. create user groups and assign users to them, assign users to the admin role, delete users). + +NOTE: The above applies to version 4.x of the JupyterHub Helm chart. +Version 3.x does not impose these limitations and users can be added and used without any constraints. + ==== OAuth Authenticator (Keycloak) -=== Certificates +To authenticate against a Keycloak instance it is necessary to provide the following: -=== Driver Service +* configuration for GenericOAuthenticator +* certificates that can be used between JupyterHub and Keycloak +* several URls (callback, authorize etc.) necessary for the authentication handshake +** in this tutorial these URls will be defined dynamically using start-up scripts, a ConfigMap and environment variables + +=== GenericOAuthenticator + +=== Certificates === Endpoints +=== Driver Service + === Profiles == Images From 62638b4d8e6362158acc909e44c70b1aeb4807ec Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Tue, 4 Mar 2025 15:46:22 +0100 Subject: [PATCH 06/19] added initial jupyterhub sections --- .../tutorials/images/jupyterhub/admin-4.x.png | Bin 59648 -> 0 bytes modules/tutorials/pages/jupyterhub.adoc | 103 +++++++++++++++--- 2 files changed, 89 insertions(+), 14 deletions(-) delete mode 100644 modules/tutorials/images/jupyterhub/admin-4.x.png diff --git a/modules/tutorials/images/jupyterhub/admin-4.x.png b/modules/tutorials/images/jupyterhub/admin-4.x.png deleted file mode 100644 index fe809a24800198a0e0e367d865017707f0bb7596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59648 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≀c?9`>t6_{ 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 Q(jOZQ^h~Z%bZ&?L7o)wz#Q*>R diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index e791e97bc..a988b6a63 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -2,17 +2,6 @@ :description: A tutorial on how to configure various aspects of JupyterHub on Kubernetes. :keywords: notebook, JupyterHub, Kubernetes, k8s, Spark, HDFS, S3 -.Drop-down example -[%collapsible] -==== -xxx: - -[source,console] ----- -xxx ----- -==== - This tutorial illustrates various scenarios and configuration options when using JupyterHub on Kubernetes. The custom resources and configuration settings that are discussed here are based on the JupyterHub-Keycloak demo, so you may find it helpful to have that demo running to reference things as you read through this tutorial. @@ -55,7 +44,7 @@ The keycloak and jupyterhub endpoints are defined in the jupyter hub chart value This can be achieved by having the keycloak deployment write out its co-ordinates into a ConfigMap during start-up, which can then be referenced by the JupyterHub chart like this: [source,yaml] ---- +---- options: hub: config: @@ -94,8 +83,11 @@ options: === Discovery -As mentioned above, keycloak writes out it endpoint information to ConfigMap, like this: +As mentioned above, keycloak writes out its endpoint information to a ConfigMap, shown in the code section below. +.Writing the ConfigMap +[%collapsible] +==== [source,yaml] ---- --- @@ -140,6 +132,8 @@ kind: Deployment wait done ---- +==== + === Security @@ -255,12 +249,16 @@ options: ... ---- +image::../images/jupyterhub/sign-up.png[Create a user] + Users must either be included in an `allowed_users` list, or the property `allow_all` must be set to `true`. The creation of new users will be checked against these settings and refused if appropriate. If an admin_users property is defined, then associated users will see an additional tab on the JupyterHub home screen, allowing them to carry out user management actions (e.g. create user groups and assign users to them, assign users to the admin role, delete users). +image::../images/jupyterhub/admin-user.png[Admin tab] + NOTE: The above applies to version 4.x of the JupyterHub Helm chart. -Version 3.x does not impose these limitations and users can be added and used without any constraints. +Version 3.x does not impose these limitations and users can be added and used without specifying `allowed_users` or `allow_all`. ==== OAuth Authenticator (Keycloak) @@ -273,8 +271,85 @@ To authenticate against a Keycloak instance it is necessary to provide the follo === GenericOAuthenticator +This section of the JupyterHub values specifies that we are using GenericOAuthenticator for our authentication. + +[source,yaml] +---- +... + hub: + config: + Authenticator: + # don't filter here: delegate to Keycloak + allow_all: true # <1> + admin_users: + - isla.williams # <2> + GenericOAuthenticator: + client_id: jupyterhub + client_secret: ... + username_claim: preferred_username + scope: + - openid # <3> + JupyterHub: + authenticator_class: generic-oauth # <4> +... +---- + +<1> We need to either provide a list of users using `allowed_users`, or to explicitly allow _all_ users, as done here. +We will delegate this to Keycloak so that we do not have to maintain users in two places. +<2> Each admin user will have access to an "Admin" tab on the JupyterHub UI where certain user-management actions can be carried out. +<3> Define the Keycloak scope +<4> Specifies which authenticator class to use + +The endpoints can be defined directly under `GenericOAuthenticator` as well, though for our purposes we will set them in a configuration script (see below). + === Certificates +The demo uses a self-signed certificate that needs to be accepted by JupyterHub. +This involves: + +* mounting a secret created with the same secret class as used for the self-signed certificate used by Keycloak +* make this secret available to JupyterHub +* it may also be necessary to point python at this specific certificate + +This can be seen below: + +[source,yaml] +---- + extraEnv: # <1> + CACERT: /etc/ssl/certs/ca-certificates.crt + CERT: /etc/ssl/certs/ca-certificates.crt + CURLOPT_CAINFO: /etc/ssl/certs/ca-certificates.crt + ... + extraVolumes: + - name: tls-ca-cert # <2> + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/class: tls + spec: + storageClassName: secrets.stackable.tech + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "1" + extraVolumeMounts: + - name: tls-ca-cert + # Alternative: mount to another filename in this folder and call update-ca-certificates + mountPath: /etc/ssl/certs/ca-certificates.crt # <3> + subPath: ca.crt + - name: tls-ca-cert + mountPath: /usr/local/lib/python3.12/site-packages/certifi/cacert.pem # <4> + subPath: ca.crt +---- + +<1> Specify which certificate(s) should be used internally (in the code above this is using the default certificate, but is included for the sake of completion) +<2> Create the certificate with the same secret class (`tls`) as Keycloak +<3> Mount this certificate. +If the default file is not overwritten, but is mounted to a new file in the same directory, then the certificates should be updated by calling e.g. `update-ca-certificates`. +<4> ensure python is using the same certificate. + === Endpoints === Driver Service From 42713291824854b71f860a3e428a2d41251e3aeb Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Tue, 4 Mar 2025 15:54:25 +0100 Subject: [PATCH 07/19] correct image refs --- modules/tutorials/pages/jupyterhub.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index a988b6a63..f94e60623 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -249,13 +249,13 @@ options: ... ---- -image::../images/jupyterhub/sign-up.png[Create a user] +image::jupyterhub/sign-up.png[Create a user] Users must either be included in an `allowed_users` list, or the property `allow_all` must be set to `true`. The creation of new users will be checked against these settings and refused if appropriate. If an admin_users property is defined, then associated users will see an additional tab on the JupyterHub home screen, allowing them to carry out user management actions (e.g. create user groups and assign users to them, assign users to the admin role, delete users). -image::../images/jupyterhub/admin-user.png[Admin tab] +image::jupyterhub/admin-user.png[Admin tab] NOTE: The above applies to version 4.x of the JupyterHub Helm chart. Version 3.x does not impose these limitations and users can be added and used without specifying `allowed_users` or `allow_all`. From bb801047b30a36a4b8e57a533441bba436d387a3 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Tue, 4 Mar 2025 17:25:48 +0100 Subject: [PATCH 08/19] jhub: extra configs and profiles --- .../images/jupyterhub/server-options.png | Bin 0 -> 27572 bytes modules/tutorials/pages/jupyterhub.adoc | 123 +++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 modules/tutorials/images/jupyterhub/server-options.png diff --git a/modules/tutorials/images/jupyterhub/server-options.png b/modules/tutorials/images/jupyterhub/server-options.png new file mode 100644 index 0000000000000000000000000000000000000000..a1d366ef1f27a8008ef548d80aa7aece267fcf98 GIT binary patch literal 27572 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 literal 0 HcmV?d00001 diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index f94e60623..354f0ab8b 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -352,10 +352,131 @@ If the default file is not overwritten, but is mounted to a new file in the same === Endpoints -=== Driver Service +The Helm chart for JupyterHub allows us to augment the standard configuration with one or more scripts. +As mentioned in an earlier section, we want to define the endpoints dynamically - by making use of the ConfigMap written out by the Keycloak Deployment - and we can do this by adding a script under `extraConfig`: + +[source,yaml] +---- + extraConfig: + ... + 03-set-endpoints: | + import os + from oauthenticator.generic import GenericOAuthenticator + keycloak_url = os.getenv("KEYCLOAK_NODEPORT_URL") # <2> + ... + keycloak_node_ip = os.getenv("KEYCLOAK_NODE_IP") + ... + c.GenericOAuthenticator.oauth_callback_url: f"http://{keycloak_node_ip}:31095/hub/oauth_callback" # <3> + c.GenericOAuthenticator.authorize_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/auth" + c.GenericOAuthenticator.token_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/token" + c.GenericOAuthenticator.userdata_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/userinfo" +---- + +=== Driver Service (Spark) + +NOTE: when using Spark, please the `Provisos` section below. + +In the same way, we can use another script to define a driver service for each user. +This is essential when using Spark from within a JupyterHUb notebook so that executor pods can be spawned from the user's kernel in a user-specific way. +This script instructs JupyterHub to use `KubeSpawner` to create a service referenced by the UID of the parent Pod. + +[source,yaml] +---- + extraConfig: + ... + 02-create-spark-driver-service-hook: | + # Thanks to https://github.com/jupyterhub/kubespawner/pull/644 + from jupyterhub.utils import exponential_backoff + from kubespawner import KubeSpawner + from kubespawner.objects import make_owner_reference + from kubernetes_asyncio.client.models import V1ServicePort + from functools import partial + + async def after_pod_created_hook(spawner: KubeSpawner, pod: dict): + owner_reference = make_owner_reference( + pod["metadata"]["name"], pod["metadata"]["uid"] + ) + service_manifest = spawner.get_service_manifest(owner_reference) + + service_manifest.spec.type = "ClusterIP" + service_manifest.spec.clusterIP = "None" # Headless Services is all we need + service_manifest.spec.ports += [ + V1ServicePort(name='spark-ui', port=4040, target_port=4040), + V1ServicePort(name='spark-driver', port=2222, target_port=2222), + V1ServicePort(name='spark-block-manager', port=7777, target_port=7777) + ] + + await exponential_backoff( + partial( + spawner._ensure_not_exists, + "service", + service_manifest.metadata.name, + ), + f"Failed to delete service {service_manifest.metadata.name}", + ) + await exponential_backoff( + partial(spawner._make_create_resource_request, "service", service_manifest), + f"Failed to create service {service_manifest.metadata.name}", + ) + + c.KubeSpawner.after_pod_created_hook = after_pod_created_hook +---- === Profiles +The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, Memory and Image combinations that can be selected. For instance, the profiles below allows to select 2/4/... CPUs, 4/8/... GB RAM and between two images. + + singleuser: + ... + profileList: + - display_name: "Default" + description: "Default profile" + default: true + profile_options: + cpu: + display_name: CPU + choices: + "2": + display_name: "2" + kubespawner_override: + cpu_guarantee: 2 + cpu_limit: 2 + "4": + display_name: "4" + kubespawner_override: + cpu_guarantee: 4 + cpu_limit: 4 + ... + memory: + display_name: Memory + choices: + "4 GB": + display_name: "4 GB" + kubespawner_override: + mem_guarantee: "4G" + mem_limit: "4G" + "8 GB": + display_name: "8 GB" + kubespawner_override: + mem_guarantee: "8G" + mem_limit: "8G" + ... + image: + display_name: Image + choices: + "quay.io/jupyter/pyspark-notebook:python-3.11.9": + display_name: "quay.io/jupyter/pyspark-notebook:python-3.11.9" + kubespawner_override: + image: "quay.io/jupyter/pyspark-notebook:python-3.11.9" + "quay.io/jupyter/pyspark-notebook:spark-3.5.2": + display_name: "quay.io/jupyter/pyspark-notebook:spark-3.5.2" + kubespawner_override: + image: "quay.io/jupyter/pyspark-notebook:spark-3.5.2" + +These options are then displayed as drop-down lists for the user once logged in: + +image::jupyterhub/server-options.png[Server options] + == Images == Example Notebook From 4c262b797b5968df7b964107e2f513da45aa5ac9 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 5 Mar 2025 12:44:47 +0100 Subject: [PATCH 09/19] initial image/notebook comments --- modules/tutorials/pages/jupyterhub.adoc | 57 ++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 354f0ab8b..72991db6f 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -1,9 +1,10 @@ = JupyterHub :description: A tutorial on how to configure various aspects of JupyterHub on Kubernetes. -:keywords: notebook, JupyterHub, Kubernetes, k8s, Spark, HDFS, S3 +:keywords: notebook, JupyterHub, Kubernetes, k8s, Apache Spark, HDFS, S3 This tutorial illustrates various scenarios and configuration options when using JupyterHub on Kubernetes. The custom resources and configuration settings that are discussed here are based on the JupyterHub-Keycloak demo, so you may find it helpful to have that demo running to reference things as you read through this tutorial. +The example notebook is used to demonstrate simple read/write interactions with an S3 storage backend using Apache Spark. == Keycloak @@ -426,6 +427,8 @@ This script instructs JupyterHub to use `KubeSpawner` to create a service refere The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, Memory and Image combinations that can be selected. For instance, the profiles below allows to select 2/4/... CPUs, 4/8/... GB RAM and between two images. +[source,yaml] +---- singleuser: ... profileList: @@ -472,6 +475,7 @@ The `singleuser.profileList` section of the Helm chart values allows us to defin display_name: "quay.io/jupyter/pyspark-notebook:spark-3.5.2" kubespawner_override: image: "quay.io/jupyter/pyspark-notebook:spark-3.5.2" +---- These options are then displayed as drop-down lists for the user once logged in: @@ -479,8 +483,59 @@ image::jupyterhub/server-options.png[Server options] == Images +The demo uses the following images: + +* Notebook images +** `quay.io/jupyter/pyspark-notebook:spark-3.5.2` +** `quay.io/jupyter/pyspark-notebook:python-3.11.9` +* Spark image +** `oci.stackable.tech/sandbox/spark:3.5.2-python311` (custom image adding python 3.11, built on `spark:3.5.2-scala2.12-java17-ubuntu`) + +.Dockerfile for the custom image +[%collapsible] +==== +[source, dockerfile] +---- +FROM spark:3.5.2-scala2.12-java17-ubuntu + +USER root + +RUN set -ex; \ + apt-get update; \ + # Install dependencies for Python 3.11 + apt-get install -y \ + software-properties-common \ + && apt-get update && apt-get install -y \ + python3.11 \ + python3.11-venv \ + python3.11-dev \ + && rm -rf /var/lib/apt/lists/*; \ + # Install pip manually for Python 3.11 + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \ + python3.11 get-pip.py && \ + rm get-pip.py + +# Make Python 3.11 the default Python version +RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 \ + && update-alternatives --install /usr/bin/pip pip /usr/local/bin/pip3 1 + +USER spark +---- +==== + +NOTE: The example notebook in the demo will start a distributed Spark cluster, whereby the notebook acts as the driver which spawns a number of executors. +The driver uses the user-specific driver service (see above) to pass job dependencies to each executor. +The Spark versions of these dependencies must be the same, or else serialization errors can occur. +This is increasingly likely in cases where Java or Scala classes do not have a specified `serialVersionUID`, in which case one will be calculated at runtime based on the contents of each class (method signatures etc.): if the contents of these class files have been changed, then the UID may differ between driver and executor. +To avoid this, care needs to be taken to use images for the notebook and the Spark job that are using a common Spark build. + == Example Notebook === Provisos +WARNING: When running a distributed Spark cluster from within a JupyterHub notebook, the notebook acts as the driver and requests executors Pods from k8s. +These Pods in turn can mount *all* volumes and Secrets in that namespace. +To prevent this from breaking user separation, it is planned to use an OPA gatekeeper to define OPA rules that restrict what the created executor Pods can mount. This is not yet implemented in the demo nor reflected in this tutorial. + === Overview + From d8454620ddb7a7f7a630877fa094a0492e384431 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 5 Mar 2025 12:51:20 +0100 Subject: [PATCH 10/19] linting --- modules/tutorials/pages/jupyterhub.adoc | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 72991db6f..8bf7790a2 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -538,4 +538,3 @@ These Pods in turn can mount *all* volumes and Secrets in that namespace. To prevent this from breaking user separation, it is planned to use an OPA gatekeeper to define OPA rules that restrict what the created executor Pods can mount. This is not yet implemented in the demo nor reflected in this tutorial. === Overview - From 9828d97d78fc1b9173b92b89d880e0509c702e72 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 5 Mar 2025 13:33:37 +0100 Subject: [PATCH 11/19] notebook summary --- modules/tutorials/pages/jupyterhub.adoc | 53 ++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 8bf7790a2..f32c7328c 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -375,7 +375,7 @@ As mentioned in an earlier section, we want to define the endpoints dynamically === Driver Service (Spark) -NOTE: when using Spark, please the `Provisos` section below. +NOTE: When using Spark from within a notebook, please the `Provisos` section below. In the same way, we can use another script to define a driver service for each user. This is essential when using Spark from within a JupyterHUb notebook so that executor pods can be spawned from the user's kernel in a user-specific way. @@ -538,3 +538,54 @@ These Pods in turn can mount *all* volumes and Secrets in that namespace. To prevent this from breaking user separation, it is planned to use an OPA gatekeeper to define OPA rules that restrict what the created executor Pods can mount. This is not yet implemented in the demo nor reflected in this tutorial. === Overview + +The notebook starts a distributed Spark cluster, which runs until the notebook kernel is stopped. +In order to connect to the S3 backend, the following settings must be configured in the Spark session: + +[source, python] +---- + ... + .config("spark.hadoop.fs.s3a.endpoint", "http://minio:9000/") + .config("spark.hadoop.fs.s3a.path.style.access", "true") + .config("spark.hadoop.fs.s3a.access.key", ...) + .config("spark.hadoop.fs.s3a.secret.key", ...) + .config("spark.hadoop.fs.s3a.aws.credentials.provider", "org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider") + .config("spark.jars.packages", "org.apache.hadoop:hadoop-client-api:3.3.4,org.apache.hadoop:hadoop-client-runtime:3.3.4,org.apache.hadoop:hadoop-aws:3.3.4,org.apache.hadoop:hadoop-common:3.3.4,com.amazonaws:aws-java-sdk-bundle:1.12.162") + ... +---- + +Since the notebook image does not include any AWS or Hadoop libraries, these are listed under `spark.jars.packages`. +How these libraries are handled can be seen by looking at the logs for the user pod and the executor pods that are spawned when the Spark session is created. +In the notebook pod (e.g. `jupyter-isla-williams---14730816`) we see that JupyterHub uses Ivy to fetch each library and resolve the dependencies: + +[source, console] +---- +:: loading settings :: url = jar:file:/usr/local/spark-3.5.2-bin-hadoop3/jars/ivy-2.5.1.jar!/org/apache/ivy/core/settings/ivysettings.xml +Ivy Default Cache set to: /home/jovyan/.ivy2/cache +The jars for the packages stored in: /home/jovyan/.ivy2/jars +org.apache.hadoop#hadoop-client-api added as a dependency +org.apache.hadoop#hadoop-client-runtime added as a dependency +org.apache.hadoop#hadoop-aws added as a dependency +org.apache.hadoop#hadoop-common added as a dependency +com.amazonaws#aws-java-sdk-bundle added as a dependency +:: resolving dependencies :: org.apache.spark#spark-submit-parent-bf8973c2-1a2f-425e-a272-2ef86cb852f8;1.0 + confs: [default] + found org.apache.hadoop#hadoop-client-api;3.3.4 in central + found org.xerial.snappy#snappy-java;1.1.8.2 in central + ... +---- + +And in the executor, we see from the logs (simplified for clarity) that the user-specific driver service is used to provide these libraries. +The executor connects to the service and then iterates through the list of resolved dependencies, fetching each package to a temporary folder (`/var/data/spark-bfed3050-5f63-441d-9799-a196d7b54ce9/spark-a03b09a7-869e-4778-ac04-fa935bbca5ab`) before copying it to the working folder (`/opt/spark/work-dir`): +[source, console] +---- +Successfully created connection to jupyter-isla-williams---14730816/10.96.29.131:2222 +Created local directory at /var/data/spark-bfed3050-5f63-441d-9799-a196d7b54ce9/blockmgr-5b70510d-7d4d-452f-818a-2a02bd0d4227 +Connecting to driver: spark://CoarseGrainedScheduler@jupyter-isla-williams---14730816:2222 +Successfully registered with driver +Fetching spark://jupyter-isla-williams---14730816:2222/files/org.checkerframework_checker-qual-2.5.2.jar with timestamp 1741174390840 +Fetching spark://jupyter-isla-williams---14730816:2222/files/org.checkerframework_checker-qual-2.5.2.jar to /var/data/spark-bfed3050-5f63-441d-9799-a196d7b54ce9/spark-a03b09a7-869e-4778-ac04-fa935bbca5ab/fetchFileTemp8701341596301771486.tmp +Copying /var/data/spark-bfed3050-5f63-441d-9799-a196d7b54ce9/spark-a03b09a7-869e-4778-ac04-fa935bbca5ab/1075326831741174390840_cache to /opt/spark/work-dir/./org.checkerframework_checker-qual-2.5.2.jar +---- + +Once the Spark session has been created, the notebook reads data from S3, performs a simple aggregation and re-writes it in different formats. From e574a43559c12eb16a91fc36a9e4b3d7c18c565e Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 5 Mar 2025 14:52:45 +0100 Subject: [PATCH 12/19] added in-doc links --- modules/tutorials/pages/jupyterhub.adoc | 32 ++++++++++++++----------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index f32c7328c..7b2bdbc89 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -8,11 +8,12 @@ The example notebook is used to demonstrate simple read/write interactions with == Keycloak -Keycloak is installed using a https://github.com/stackabletech/demos/blob/feat/keycloak-jupyterhub/stacks/jupyterhub-keycloak/keycloak.yaml[Deployment] that loads realm configuration mounted as a ConfigMap. +Keycloak is installed using a https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak/keycloak.yaml[Deployment] that loads realm configuration mounted as a ConfigMap. +[#services] === Services -In the demo, the keycloak and jupyter hub service (proxy-public) ports are fixed e.g. +In the demo, the keycloak and jupyter hub service (`proxy-public`) ports are fixed e.g. [source,yaml] --- @@ -84,7 +85,7 @@ options: === Discovery -As mentioned above, keycloak writes out its endpoint information to a ConfigMap, shown in the code section below. +As mentioned above in <>, keycloak writes out its endpoint information to a ConfigMap, shown in the code section below. .Writing the ConfigMap [%collapsible] @@ -202,7 +203,7 @@ For the self-signed certificate to be accepted during the handshake between Jupy === Realm -The Keycloak https://github.com/stackabletech/demos/blob/feat/keycloak-jupyterhub/stacks/jupyterhub-keycloak/keycloak-realm-config.yaml for the demo basically contains a set of users and groups, along with a simple client definition: +The Keycloak https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak/keycloak-realm-config.yaml for the demo basically contains a set of users and groups, along with a simple client definition: [source,yaml] ---- @@ -218,8 +219,8 @@ The Keycloak https://github.com/stackabletech/demos/blob/feat/keycloak-jupyterhu } ] ---- -Not that the standard flow is enabled and no other OAuth-specific settings are required. -Wildcards are used for `redirectUris` and `webOrigins`, mainly for the sake of simplicity: in production environments this would typically be limited or filtered in an appropriate way. +Note that the standard flow is enabled and no other OAuth-specific settings are required. +Wildcards are used for `redirectUris` and `webOrigins`, mainly for the sake of simplicity: in production environments these would typically be limited or filtered in an appropriate way. == JupyterHub @@ -254,7 +255,7 @@ image::jupyterhub/sign-up.png[Create a user] Users must either be included in an `allowed_users` list, or the property `allow_all` must be set to `true`. The creation of new users will be checked against these settings and refused if appropriate. -If an admin_users property is defined, then associated users will see an additional tab on the JupyterHub home screen, allowing them to carry out user management actions (e.g. create user groups and assign users to them, assign users to the admin role, delete users). +If an `admin_users` property is defined, then associated users will see an additional tab on the JupyterHub home screen, allowing them to carry out certain user management actions (e.g. create user groups and assign users to them, assign users to the admin role, delete users). image::jupyterhub/admin-user.png[Admin tab] @@ -297,11 +298,11 @@ This section of the JupyterHub values specifies that we are using GenericOAuthen <1> We need to either provide a list of users using `allowed_users`, or to explicitly allow _all_ users, as done here. We will delegate this to Keycloak so that we do not have to maintain users in two places. -<2> Each admin user will have access to an "Admin" tab on the JupyterHub UI where certain user-management actions can be carried out. +<2> Each admin user will have access to an Admin tab on the JupyterHub UI where certain user-management actions can be carried out. <3> Define the Keycloak scope <4> Specifies which authenticator class to use -The endpoints can be defined directly under `GenericOAuthenticator` as well, though for our purposes we will set them in a configuration script (see below). +The endpoints can be defined directly under `GenericOAuthenticator` as well, though for our purposes we will set them in a configuration script (see <> below). === Certificates @@ -351,10 +352,11 @@ This can be seen below: If the default file is not overwritten, but is mounted to a new file in the same directory, then the certificates should be updated by calling e.g. `update-ca-certificates`. <4> ensure python is using the same certificate. +[#endpoints] === Endpoints The Helm chart for JupyterHub allows us to augment the standard configuration with one or more scripts. -As mentioned in an earlier section, we want to define the endpoints dynamically - by making use of the ConfigMap written out by the Keycloak Deployment - and we can do this by adding a script under `extraConfig`: +As mentioned in the <> section above, we want to define the endpoints dynamically - by making use of the ConfigMap written out by the Keycloak Deployment - and we can do this by adding a script under `extraConfig`: [source,yaml] ---- @@ -373,9 +375,10 @@ As mentioned in an earlier section, we want to define the endpoints dynamically c.GenericOAuthenticator.userdata_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/userinfo" ---- +[#driver] === Driver Service (Spark) -NOTE: When using Spark from within a notebook, please the `Provisos` section below. +NOTE: When using Spark from within a notebook, please the <> section below. In the same way, we can use another script to define a driver service for each user. This is essential when using Spark from within a JupyterHUb notebook so that executor pods can be spawned from the user's kernel in a user-specific way. @@ -425,7 +428,7 @@ This script instructs JupyterHub to use `KubeSpawner` to create a service refere === Profiles -The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, Memory and Image combinations that can be selected. For instance, the profiles below allows to select 2/4/... CPUs, 4/8/... GB RAM and between two images. +The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, Memory and Image combinations that can be selected. For instance, the profiles below allows us to select `2/4/...` CPUs, `4/8/...` GB RAM and to select one of two images. [source,yaml] ---- @@ -524,13 +527,14 @@ USER spark ==== NOTE: The example notebook in the demo will start a distributed Spark cluster, whereby the notebook acts as the driver which spawns a number of executors. -The driver uses the user-specific driver service (see above) to pass job dependencies to each executor. +The driver uses the user-specific <> to pass job dependencies to each executor. The Spark versions of these dependencies must be the same, or else serialization errors can occur. This is increasingly likely in cases where Java or Scala classes do not have a specified `serialVersionUID`, in which case one will be calculated at runtime based on the contents of each class (method signatures etc.): if the contents of these class files have been changed, then the UID may differ between driver and executor. To avoid this, care needs to be taken to use images for the notebook and the Spark job that are using a common Spark build. == Example Notebook +[#provisos] === Provisos WARNING: When running a distributed Spark cluster from within a JupyterHub notebook, the notebook acts as the driver and requests executors Pods from k8s. @@ -588,4 +592,4 @@ Fetching spark://jupyter-isla-williams---14730816:2222/files/org.checkerframewor Copying /var/data/spark-bfed3050-5f63-441d-9799-a196d7b54ce9/spark-a03b09a7-869e-4778-ac04-fa935bbca5ab/1075326831741174390840_cache to /opt/spark/work-dir/./org.checkerframework_checker-qual-2.5.2.jar ---- -Once the Spark session has been created, the notebook reads data from S3, performs a simple aggregation and re-writes it in different formats. +Once the Spark session has been created, the notebook reads data from S3, performs a simple aggregation and re-writes it in different formats. Further comments can be found in the notebook itself. From 1ce303779d24e5b76ff4b15fa96b33825803d437 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Wed, 5 Mar 2025 16:48:47 +0100 Subject: [PATCH 13/19] cleanup --- modules/tutorials/pages/jupyterhub.adoc | 36 ++++++++++++++----------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 7b2bdbc89..89add259e 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -3,7 +3,7 @@ :keywords: notebook, JupyterHub, Kubernetes, k8s, Apache Spark, HDFS, S3 This tutorial illustrates various scenarios and configuration options when using JupyterHub on Kubernetes. -The custom resources and configuration settings that are discussed here are based on the JupyterHub-Keycloak demo, so you may find it helpful to have that demo running to reference things as you read through this tutorial. +The custom resources and configuration settings that are discussed here are based on the xref:demos:jupyterhub-keycloak.adoc[JupyterHub-Keycloak demo], so you may find it helpful to have that demo running to reference the various https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak[resources] as you read through this tutorial. The example notebook is used to demonstrate simple read/write interactions with an S3 storage backend using Apache Spark. == Keycloak @@ -79,9 +79,14 @@ options: c.GenericOAuthenticator.userdata_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/userinfo" ---- -<1> endpoint information read from the ConfigMap -<2> this information is passed to a variable in one of the start-up config scripts... -<3> ...and then used for JupyterHub settings +<1> Endpoint information read from the ConfigMap +<2> This information is passed to a variable in one of the start-up config scripts +<3> And then used for JupyterHub settings (this is where port `31095` is hard-coded for the proxy service) + +NOTE: The node port IP found in the ConfigMap `keycloak-address` can be used for opening the JupyterHb UI. +On Kind this can be any node - not necessarily the one where the proxy Pod is running. +This is due to the way in which Docker networking is used within the cluster. +On other clusters it might be necessary to use the exact Node on which the proxy is running. === Discovery @@ -136,7 +141,6 @@ kind: Deployment ---- ==== - === Security We create a keystore with a self-generated and self-signed certificate and mount it so that the keystore file can be used when starting keycloak: @@ -203,7 +207,7 @@ For the self-signed certificate to be accepted during the handshake between Jupy === Realm -The Keycloak https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak/keycloak-realm-config.yaml for the demo basically contains a set of users and groups, along with a simple client definition: +The Keycloak https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak/keycloak-realm-config.yaml[realm configuration] for the demo basically contains a set of users and groups, along with a JupyterHub client definition: [source,yaml] ---- @@ -273,7 +277,7 @@ To authenticate against a Keycloak instance it is necessary to provide the follo === GenericOAuthenticator -This section of the JupyterHub values specifies that we are using GenericOAuthenticator for our authentication. +This section of the JupyterHub configuration specifies that we are using GenericOAuthenticator for our authentication: [source,yaml] ---- @@ -296,8 +300,8 @@ This section of the JupyterHub values specifies that we are using GenericOAuthen ... ---- -<1> We need to either provide a list of users using `allowed_users`, or to explicitly allow _all_ users, as done here. -We will delegate this to Keycloak so that we do not have to maintain users in two places. +<1> We need to either provide a list of users using `allowed_users`, or to explicitly allow _all_ users, as done here +We will delegate this to Keycloak so that we do not have to maintain users in two places <2> Each admin user will have access to an Admin tab on the JupyterHub UI where certain user-management actions can be carried out. <3> Define the Keycloak scope <4> Specifies which authenticator class to use @@ -348,9 +352,9 @@ This can be seen below: <1> Specify which certificate(s) should be used internally (in the code above this is using the default certificate, but is included for the sake of completion) <2> Create the certificate with the same secret class (`tls`) as Keycloak -<3> Mount this certificate. +<3> Mount this certificate If the default file is not overwritten, but is mounted to a new file in the same directory, then the certificates should be updated by calling e.g. `update-ca-certificates`. -<4> ensure python is using the same certificate. +<4> Ensure python is using the same certificate [#endpoints] === Endpoints @@ -365,11 +369,11 @@ As mentioned in the <> section above, we want to define the 03-set-endpoints: | import os from oauthenticator.generic import GenericOAuthenticator - keycloak_url = os.getenv("KEYCLOAK_NODEPORT_URL") # <2> + keycloak_url = os.getenv("KEYCLOAK_NODEPORT_URL") ... keycloak_node_ip = os.getenv("KEYCLOAK_NODE_IP") ... - c.GenericOAuthenticator.oauth_callback_url: f"http://{keycloak_node_ip}:31095/hub/oauth_callback" # <3> + c.GenericOAuthenticator.oauth_callback_url: f"http://{keycloak_node_ip}:31095/hub/oauth_callback" c.GenericOAuthenticator.authorize_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/auth" c.GenericOAuthenticator.token_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/token" c.GenericOAuthenticator.userdata_url = f"https://{keycloak_url}/realms/demo/protocol/openid-connect/userinfo" @@ -428,7 +432,7 @@ This script instructs JupyterHub to use `KubeSpawner` to create a service refere === Profiles -The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, Memory and Image combinations that can be selected. For instance, the profiles below allows us to select `2/4/...` CPUs, `4/8/...` GB RAM and to select one of two images. +The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, Memory and Image combinations that can be selected. For instance, the profiles below allows us to select 2/4/etc. CPUs, 4/8/etc. GB RAM and to select one of two images. [source,yaml] ---- @@ -528,8 +532,8 @@ USER spark NOTE: The example notebook in the demo will start a distributed Spark cluster, whereby the notebook acts as the driver which spawns a number of executors. The driver uses the user-specific <> to pass job dependencies to each executor. -The Spark versions of these dependencies must be the same, or else serialization errors can occur. -This is increasingly likely in cases where Java or Scala classes do not have a specified `serialVersionUID`, in which case one will be calculated at runtime based on the contents of each class (method signatures etc.): if the contents of these class files have been changed, then the UID may differ between driver and executor. +The Spark versions of these dependencies must be the same on both the driver and executor, or else serialization errors can occur. +For Java or Scala classes that do not have a specified `serialVersionUID`, one will be calculated at runtime based on the contents of each class (method signatures etc.): if the contents of these class files have been changed, then the UID may differ between driver and executor. To avoid this, care needs to be taken to use images for the notebook and the Spark job that are using a common Spark build. == Example Notebook From f8477fb43d6e20a82d8187051afee8295774bb8d Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy <1712947+adwk67@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:47:22 +0100 Subject: [PATCH 14/19] Apply suggestions from code review Co-authored-by: Malte Sander --- modules/tutorials/pages/jupyterhub.adoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 89add259e..24837b819 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -8,7 +8,7 @@ The example notebook is used to demonstrate simple read/write interactions with == Keycloak -Keycloak is installed using a https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak/keycloak.yaml[Deployment] that loads realm configuration mounted as a ConfigMap. +Keycloak is installed using a https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak/keycloak.yaml[Deployment] that loads its realm configuration mounted as a ConfigMap. [#services] === Services @@ -43,7 +43,7 @@ They are: The keycloak and jupyterhub endpoints are defined in the jupyter hub chart values i.e. for the purposes of the demo (that does not use any pre-defined DNS settings), the ports have to be known before the jupyter hub components are deployed. -This can be achieved by having the keycloak deployment write out its co-ordinates into a ConfigMap during start-up, which can then be referenced by the JupyterHub chart like this: +This can be achieved by having the keycloak deployment write out its node URL and node IP into a ConfigMap during start-up, which can then be referenced by the JupyterHub chart like this: [source,yaml] ---- @@ -83,14 +83,14 @@ options: <2> This information is passed to a variable in one of the start-up config scripts <3> And then used for JupyterHub settings (this is where port `31095` is hard-coded for the proxy service) -NOTE: The node port IP found in the ConfigMap `keycloak-address` can be used for opening the JupyterHb UI. +NOTE: The node port IP found in the ConfigMap `keycloak-address` can be used for opening the JupyterHub UI. On Kind this can be any node - not necessarily the one where the proxy Pod is running. This is due to the way in which Docker networking is used within the cluster. On other clusters it might be necessary to use the exact Node on which the proxy is running. === Discovery -As mentioned above in <>, keycloak writes out its endpoint information to a ConfigMap, shown in the code section below. +As mentioned above in <>, a Keycloak sidecar container writes out its endpoint information to a ConfigMap, shown in the code section below. .Writing the ConfigMap [%collapsible] @@ -300,7 +300,7 @@ This section of the JupyterHub configuration specifies that we are using Generic ... ---- -<1> We need to either provide a list of users using `allowed_users`, or to explicitly allow _all_ users, as done here +<1> We need to either provide a list of users using `allowed_users`, or to explicitly allow _all_ users, as done here. We will delegate this to Keycloak so that we do not have to maintain users in two places <2> Each admin user will have access to an Admin tab on the JupyterHub UI where certain user-management actions can be carried out. <3> Define the Keycloak scope @@ -385,7 +385,7 @@ As mentioned in the <> section above, we want to define the NOTE: When using Spark from within a notebook, please the <> section below. In the same way, we can use another script to define a driver service for each user. -This is essential when using Spark from within a JupyterHUb notebook so that executor pods can be spawned from the user's kernel in a user-specific way. +This is essential when using Spark from within a JupyterHub notebook so that executor pods can be spawned from the user's kernel in a user-specific way. This script instructs JupyterHub to use `KubeSpawner` to create a service referenced by the UID of the parent Pod. [source,yaml] From ad4b066cc970f4f69b92949e394e72905e078d07 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 10 Mar 2025 09:54:06 +0100 Subject: [PATCH 15/19] consistent caps --- modules/tutorials/pages/jupyterhub.adoc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 24837b819..7a271ceb6 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -3,7 +3,7 @@ :keywords: notebook, JupyterHub, Kubernetes, k8s, Apache Spark, HDFS, S3 This tutorial illustrates various scenarios and configuration options when using JupyterHub on Kubernetes. -The custom resources and configuration settings that are discussed here are based on the xref:demos:jupyterhub-keycloak.adoc[JupyterHub-Keycloak demo], so you may find it helpful to have that demo running to reference the various https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak[resources] as you read through this tutorial. +The custom Resources and configuration settings that are discussed here are based on the xref:demos:jupyterhub-keycloak.adoc[JupyterHub-Keycloak demo], so you may find it helpful to have that demo running to reference the various https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak[resources] as you read through this tutorial. The example notebook is used to demonstrate simple read/write interactions with an S3 storage backend using Apache Spark. == Keycloak @@ -13,7 +13,7 @@ Keycloak is installed using a https://github.com/stackabletech/demos/blob/main/s [#services] === Services -In the demo, the keycloak and jupyter hub service (`proxy-public`) ports are fixed e.g. +In the demo, the Keycloak and JupyterHub service (`proxy-public`) ports are fixed e.g. [source,yaml] --- @@ -38,12 +38,12 @@ spec: They are: -- `31093` for keycloak +- `31093` for Keycloak - `31095` for jupyterhub/proxy-public -The keycloak and jupyterhub endpoints are defined in the jupyter hub chart values i.e. for the purposes of the demo (that does not use any pre-defined DNS settings), the ports have to be known before the jupyter hub components are deployed. +The Keycloak and JupyterHub endpoints are defined in the JupyterHub chart values i.e. for the purposes of the demo (that does not use any pre-defined DNS settings), the ports have to be known before the JupyterHub components are deployed. -This can be achieved by having the keycloak deployment write out its node URL and node IP into a ConfigMap during start-up, which can then be referenced by the JupyterHub chart like this: +This can be achieved by having the Keycloak Deployment write out its node URL and node IP into a ConfigMap during start-up, which can then be referenced by the JupyterHub chart like this: [source,yaml] ---- @@ -90,7 +90,7 @@ On other clusters it might be necessary to use the exact Node on which the proxy === Discovery -As mentioned above in <>, a Keycloak sidecar container writes out its endpoint information to a ConfigMap, shown in the code section below. +As mentioned above in <>, a Keycloak sidecar Container writes out its endpoint information to a ConfigMap, shown in the code section below. .Writing the ConfigMap [%collapsible] @@ -143,7 +143,7 @@ kind: Deployment === Security -We create a keystore with a self-generated and self-signed certificate and mount it so that the keystore file can be used when starting keycloak: +We create a keystore with a self-generated and self-signed certificate and mount it so that the keystore file can be used when starting Keycloak: [source,yaml] ---- @@ -182,10 +182,10 @@ We create a keystore with a self-generated and self-signed certificate and mount ---- <1> Create a volume holding the self-signed certificate information -<2> Mount this volume for keycloak to use +<2> Mount this volume for Keycloak to use <3> Pass the keystore file as an argument on start-up -For the self-signed certificate to be accepted during the handshake between JupyterHub and Keycloak it is important to create the jupyterhub-side certificate using the same secret class, although the format can be a different one: +For the self-signed certificate to be accepted during the handshake between JupyterHub and Keycloak it is important to create the JupyterHub-side certificate using the same secret class, although the format can be a different one: [source,yaml] ---- @@ -385,7 +385,7 @@ As mentioned in the <> section above, we want to define the NOTE: When using Spark from within a notebook, please the <> section below. In the same way, we can use another script to define a driver service for each user. -This is essential when using Spark from within a JupyterHub notebook so that executor pods can be spawned from the user's kernel in a user-specific way. +This is essential when using Spark from within a JupyterHub notebook so that executor Pods can be spawned from the user's kernel in a user-specific way. This script instructs JupyterHub to use `KubeSpawner` to create a service referenced by the UID of the parent Pod. [source,yaml] @@ -563,8 +563,8 @@ In order to connect to the S3 backend, the following settings must be configured ---- Since the notebook image does not include any AWS or Hadoop libraries, these are listed under `spark.jars.packages`. -How these libraries are handled can be seen by looking at the logs for the user pod and the executor pods that are spawned when the Spark session is created. -In the notebook pod (e.g. `jupyter-isla-williams---14730816`) we see that JupyterHub uses Ivy to fetch each library and resolve the dependencies: +How these libraries are handled can be seen by looking at the logs for the user Pod and the executor Pods that are spawned when the Spark session is created. +In the notebook Pod (e.g. `jupyter-isla-williams---14730816`) we see that JupyterHub uses Ivy to fetch each library and resolve the dependencies: [source, console] ---- From e56aa610aac8fe1997fe31be316cfae2485ba508 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 10 Mar 2025 10:00:14 +0100 Subject: [PATCH 16/19] consistent caps II --- modules/tutorials/pages/jupyterhub.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 7a271ceb6..77c3c1310 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -3,7 +3,7 @@ :keywords: notebook, JupyterHub, Kubernetes, k8s, Apache Spark, HDFS, S3 This tutorial illustrates various scenarios and configuration options when using JupyterHub on Kubernetes. -The custom Resources and configuration settings that are discussed here are based on the xref:demos:jupyterhub-keycloak.adoc[JupyterHub-Keycloak demo], so you may find it helpful to have that demo running to reference the various https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak[resources] as you read through this tutorial. +The Custom Resources and configuration settings that are discussed here are based on the xref:demos:jupyterhub-keycloak.adoc[JupyterHub-Keycloak demo], so you may find it helpful to have that demo running to reference the various https://github.com/stackabletech/demos/blob/main/stacks/jupyterhub-keycloak[Resource definitions] as you read through this tutorial. The example notebook is used to demonstrate simple read/write interactions with an S3 storage backend using Apache Spark. == Keycloak @@ -34,12 +34,12 @@ spec: nodePort: 31093 # <1> ---- -<1> Static value for the purposed of the demo. +<1> Static value for the purposes of the demo. They are: - `31093` for Keycloak -- `31095` for jupyterhub/proxy-public +- `31095` for JupyterHub/proxy-public The Keycloak and JupyterHub endpoints are defined in the JupyterHub chart values i.e. for the purposes of the demo (that does not use any pre-defined DNS settings), the ports have to be known before the JupyterHub components are deployed. From 6dd64f2e3c2e042dc541a2e043f559666639a434 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 10 Mar 2025 10:09:24 +0100 Subject: [PATCH 17/19] typos etc. --- modules/tutorials/pages/jupyterhub.adoc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 77c3c1310..aa3390448 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -34,7 +34,7 @@ spec: nodePort: 31093 # <1> ---- -<1> Static value for the purposes of the demo. +<1> Static value for the purposes of the demo They are: @@ -302,7 +302,7 @@ This section of the JupyterHub configuration specifies that we are using Generic <1> We need to either provide a list of users using `allowed_users`, or to explicitly allow _all_ users, as done here. We will delegate this to Keycloak so that we do not have to maintain users in two places -<2> Each admin user will have access to an Admin tab on the JupyterHub UI where certain user-management actions can be carried out. +<2> Each admin user will have access to an Admin tab on the JupyterHub UI where certain user-management actions can be carried out <3> Define the Keycloak scope <4> Specifies which authenticator class to use @@ -352,8 +352,7 @@ This can be seen below: <1> Specify which certificate(s) should be used internally (in the code above this is using the default certificate, but is included for the sake of completion) <2> Create the certificate with the same secret class (`tls`) as Keycloak -<3> Mount this certificate -If the default file is not overwritten, but is mounted to a new file in the same directory, then the certificates should be updated by calling e.g. `update-ca-certificates`. +<3> Mount this certificate: if the default file is not overwritten, but is mounted to a new file in the same directory, then the certificates should be updated by calling e.g. `update-ca-certificates` <4> Ensure python is using the same certificate [#endpoints] @@ -382,7 +381,7 @@ As mentioned in the <> section above, we want to define the [#driver] === Driver Service (Spark) -NOTE: When using Spark from within a notebook, please the <> section below. +NOTE: When using Spark from within a notebook, please take note of the <> section below. In the same way, we can use another script to define a driver service for each user. This is essential when using Spark from within a JupyterHub notebook so that executor Pods can be spawned from the user's kernel in a user-specific way. @@ -432,7 +431,7 @@ This script instructs JupyterHub to use `KubeSpawner` to create a service refere === Profiles -The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, Memory and Image combinations that can be selected. For instance, the profiles below allows us to select 2/4/etc. CPUs, 4/8/etc. GB RAM and to select one of two images. +The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, Memory and Image combinations that can be selected. For instance, the profiles below allows us to select 2/4/etc. CPUs, 4/8/etc. GB RAM and to choose between one of two images. [source,yaml] ---- @@ -541,9 +540,9 @@ To avoid this, care needs to be taken to use images for the notebook and the Spa [#provisos] === Provisos -WARNING: When running a distributed Spark cluster from within a JupyterHub notebook, the notebook acts as the driver and requests executors Pods from k8s. +WARNING: When running a distributed Spark cluster from within a JupyterHub notebook, the notebook acts as the driver and requests executor Pods from k8s. These Pods in turn can mount *all* volumes and Secrets in that namespace. -To prevent this from breaking user separation, it is planned to use an OPA gatekeeper to define OPA rules that restrict what the created executor Pods can mount. This is not yet implemented in the demo nor reflected in this tutorial. +To prevent this from breaking user isolation, it is planned to use an OPA gatekeeper to define OPA rules that restrict what the created executor Pods can mount. This is not yet implemented in the demo nor reflected in this tutorial. === Overview From ff0d1156918cfda0cf2fb2b084e1ac86672349c1 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy <1712947+adwk67@users.noreply.github.com> Date: Mon, 10 Mar 2025 10:54:16 +0100 Subject: [PATCH 18/19] Apply suggestions from code review Co-authored-by: Malte Sander --- modules/tutorials/pages/jupyterhub.adoc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index aa3390448..0519ef63d 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -16,7 +16,7 @@ Keycloak is installed using a https://github.com/stackabletech/demos/blob/main/s In the demo, the Keycloak and JupyterHub service (`proxy-public`) ports are fixed e.g. [source,yaml] ---- +---- apiVersion: v1 kind: Service metadata: @@ -185,7 +185,7 @@ We create a keystore with a self-generated and self-signed certificate and mount <2> Mount this volume for Keycloak to use <3> Pass the keystore file as an argument on start-up -For the self-signed certificate to be accepted during the handshake between JupyterHub and Keycloak it is important to create the JupyterHub-side certificate using the same secret class, although the format can be a different one: +For the self-signed certificate to be accepted during the handshake between JupyterHub and Keycloak it is important to create the JupyterHub-side certificate using the same SecretClass, although the format can be a different one: [source,yaml] ---- @@ -272,8 +272,8 @@ To authenticate against a Keycloak instance it is necessary to provide the follo * configuration for GenericOAuthenticator * certificates that can be used between JupyterHub and Keycloak -* several URls (callback, authorize etc.) necessary for the authentication handshake -** in this tutorial these URls will be defined dynamically using start-up scripts, a ConfigMap and environment variables +* several URLs (callback, authorize etc.) necessary for the authentication handshake +** in this tutorial these URLs will be defined dynamically using start-up scripts, a ConfigMap and environment variables === GenericOAuthenticator @@ -315,7 +315,7 @@ This involves: * mounting a secret created with the same secret class as used for the self-signed certificate used by Keycloak * make this secret available to JupyterHub -* it may also be necessary to point python at this specific certificate +* it may also be necessary to point Python at this specific certificate This can be seen below: @@ -431,7 +431,7 @@ This script instructs JupyterHub to use `KubeSpawner` to create a service refere === Profiles -The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, Memory and Image combinations that can be selected. For instance, the profiles below allows us to select 2/4/etc. CPUs, 4/8/etc. GB RAM and to choose between one of two images. +The `singleuser.profileList` section of the Helm chart values allows us to define notebook profiles by setting the CPU, memory and image combinations that can be selected. For instance, the profiles below allows us to select 2/4/etc. CPUs, 4/8/etc. GB RAM and to choose between one of two images. [source,yaml] ---- @@ -540,7 +540,7 @@ To avoid this, care needs to be taken to use images for the notebook and the Spa [#provisos] === Provisos -WARNING: When running a distributed Spark cluster from within a JupyterHub notebook, the notebook acts as the driver and requests executor Pods from k8s. +WARNING: When running a distributed Spark cluster from within a JupyterHub notebook, the notebook acts as the driver and requests executor Pods from Kubernetes. These Pods in turn can mount *all* volumes and Secrets in that namespace. To prevent this from breaking user isolation, it is planned to use an OPA gatekeeper to define OPA rules that restrict what the created executor Pods can mount. This is not yet implemented in the demo nor reflected in this tutorial. From 07025d1c1090e52451d8125e3451bd4d37c8756c Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 10 Mar 2025 11:00:25 +0100 Subject: [PATCH 19/19] consistent caps III --- modules/tutorials/pages/jupyterhub.adoc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/tutorials/pages/jupyterhub.adoc b/modules/tutorials/pages/jupyterhub.adoc index 0519ef63d..a4a8e29df 100644 --- a/modules/tutorials/pages/jupyterhub.adoc +++ b/modules/tutorials/pages/jupyterhub.adoc @@ -86,7 +86,7 @@ options: NOTE: The node port IP found in the ConfigMap `keycloak-address` can be used for opening the JupyterHub UI. On Kind this can be any node - not necessarily the one where the proxy Pod is running. This is due to the way in which Docker networking is used within the cluster. -On other clusters it might be necessary to use the exact Node on which the proxy is running. +On other clusters it might be necessary to use the exact node on which the proxy is running. === Discovery @@ -313,8 +313,8 @@ The endpoints can be defined directly under `GenericOAuthenticator` as well, tho The demo uses a self-signed certificate that needs to be accepted by JupyterHub. This involves: -* mounting a secret created with the same secret class as used for the self-signed certificate used by Keycloak -* make this secret available to JupyterHub +* mounting a Secret created with the same SecretClass as used for the self-signed certificate used by Keycloak +* make this Secret available to JupyterHub * it may also be necessary to point Python at this specific certificate This can be seen below: @@ -351,9 +351,9 @@ This can be seen below: ---- <1> Specify which certificate(s) should be used internally (in the code above this is using the default certificate, but is included for the sake of completion) -<2> Create the certificate with the same secret class (`tls`) as Keycloak +<2> Create the certificate with the same SecretClass (`tls`) as Keycloak <3> Mount this certificate: if the default file is not overwritten, but is mounted to a new file in the same directory, then the certificates should be updated by calling e.g. `update-ca-certificates` -<4> Ensure python is using the same certificate +<4> Ensure Python is using the same certificate [#endpoints] === Endpoints @@ -530,10 +530,10 @@ USER spark ==== NOTE: The example notebook in the demo will start a distributed Spark cluster, whereby the notebook acts as the driver which spawns a number of executors. -The driver uses the user-specific <> to pass job dependencies to each executor. +The driver uses the user-specific <> to pass dependencies to each executor. The Spark versions of these dependencies must be the same on both the driver and executor, or else serialization errors can occur. For Java or Scala classes that do not have a specified `serialVersionUID`, one will be calculated at runtime based on the contents of each class (method signatures etc.): if the contents of these class files have been changed, then the UID may differ between driver and executor. -To avoid this, care needs to be taken to use images for the notebook and the Spark job that are using a common Spark build. +To avoid this, care needs to be taken to use images for the notebook and Spark that are using a common Spark build. == Example Notebook