Skip to content

Commit

Permalink
Merge branch 'master' into nightly
Browse files Browse the repository at this point in the history
  • Loading branch information
regisb committed Jun 29, 2023
2 parents 26446fe + 4021115 commit 8ac2440
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 21 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ instructions, because git commits are used to generate release notes:

<!-- scriv-insert-here -->

<a id='changelog-16.0.1'></a>
## v16.0.1 (2023-06-29)

- [Feature] Run JupyterHub on Kubernetes. This is an alpha feature. Feedback is welcome! (by @regisb)

<a id='changelog-16.0.0'></a>
## v16.0.0 (2023-06-15)

Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This is a plugin for Tutor that makes it easy to integrate `Jupyter <https://jup

In pratice, it means that students will be allocated Docker containers with limited CPU and memory to run their custom notebooks.

⚠️ This plugin is not compatible with Kubernetes. If you wish to run JupyterHub on Kubernetes, you are encouraged to check the documentation of the `Zero to JupyterHub with Kubernetes <https://z2jh.jupyter.org/en/stable/resources/reference.html>`__ project.
⚠️ Compatibility with Kubernetes was not battle-tested. Please report any issue you face. For a more production-ready Kubernetes environment, you are encouraged to check the documentation of the `Zero to JupyterHub with Kubernetes <https://z2jh.jupyter.org/en/stable/resources/reference.html>`__ project.

Installation
------------
Expand Down
2 changes: 1 addition & 1 deletion tutorjupyter/__about__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "16.0.0"
__version__ = "16.0.1"
__package_version__ = __version__

# Handle version suffix for nightly, just like tutor core.
Expand Down
72 changes: 72 additions & 0 deletions tutorjupyter/patches/k8s-deployments
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jupyterhub
labels:
app.kubernetes.io/name: jupyterhub
spec:
selector:
matchLabels:
app.kubernetes.io/name: jupyterhub
template:
metadata:
labels:
app.kubernetes.io/name: jupyterhub
spec:
containers:
- name: jupyterhub
image: {{ JUPYTER_DOCKER_IMAGE_HUB }}
env:
- name: JUPYTERHUB_CRYPT_KEY
value: "{{ JUPYTER_HUB_CRYPT_KEY }}"
- name: SPAWNER
value: kubernetes
ports:
- containerPort: 9045
- containerPort: 8081
volumeMounts:
- mountPath: /srv/jupyterhub/
name: config
serviceAccountName: jupyterhub
volumes:
- name: config
configMap:
name: jupyterhub-config
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jupyterhub
labels:
app.kubernetes.io/name: jupyterhub
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jupyterhub
labels:
app.kubernetes.io/name: jupyterhub
rules:
- apiGroups: [""]
resources: ["pods", "persistentvolumeclaims", "secrets", "services"]
verbs: ["get", "watch", "list", "create", "delete"]
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jupyterhub
labels:
app.kubernetes.io/name: jupyterhub
subjects:
- kind: ServiceAccount
name: jupyterhub
namespace: "{{ K8S_NAMESPACE }}"
roleRef:
kind: Role
name: jupyterhub
apiGroup: rbac.authorization.k8s.io

24 changes: 24 additions & 0 deletions tutorjupyter/patches/k8s-jobs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: jupyterhub-job
labels:
app.kubernetes.io/component: job
spec:
template:
spec:
restartPolicy: Never
containers:
- name: jupyterhub
image: {{ JUPYTER_DOCKER_IMAGE_HUB }}
env:
- name: JUPYTERHUB_CRYPT_KEY
value: "{{ JUPYTER_HUB_CRYPT_KEY }}"
volumeMounts:
- mountPath: /srv/jupyterhub/
name: config
volumes:
- name: config
configMap:
name: jupyterhub-config
16 changes: 16 additions & 0 deletions tutorjupyter/patches/k8s-services
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
apiVersion: v1
kind: Service
metadata:
name: jupyterhub
spec:
type: NodePort
ports:
- port: 9045
protocol: TCP
name: hub
- port: 8081
protocol: TCP
name: proxy
selector:
app.kubernetes.io/name: jupyterhub
6 changes: 6 additions & 0 deletions tutorjupyter/patches/kustomization-configmapgenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- name: jupyterhub-config
files:
- plugins/jupyter/apps/jupyterhub_config.py
options:
labels:
app.kubernetes.io/name: jupyterhub
1 change: 1 addition & 0 deletions tutorjupyter/patches/local-docker-compose-services
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ jupyterhub:
environment:
JUPYTERHUB_CRYPT_KEY: "{{ JUPYTER_HUB_CRYPT_KEY }}"
NETWORK_NAME: "{{ LOCAL_PROJECT_NAME }}_default"
SPAWNER: "docker"
volumes:
- ../plugins/jupyter/apps/jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py:ro
# Spawn Docker containers
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Install Jupyter XBlock
# Remember to bump the version when we upgrade from Olive.
# Remember to bump the version when we upgrade from Palm.
# https://pypi.org/project/jupyter-xblock/
RUN pip install "jupyter-xblock>=15.0.3,<16.0.0"
RUN pip install "jupyter-xblock>=16.0.0,<17.0.0"
46 changes: 30 additions & 16 deletions tutorjupyter/templates/jupyter/apps/jupyterhub_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
# Database
c.JupyterHub.db_url = "mysql+pymysql://{{ JUPYTER_HUB_MYSQL_USERNAME }}:{{ JUPYTER_HUB_MYSQL_PASSWORD }}@{{ MYSQL_HOST }}:{{ MYSQL_PORT }}/{{ JUPYTER_HUB_MYSQL_DATABASE }}"
c.JupyterHub.cookie_secret = "{{ JUPYTER_HUB_COOKIE_SECRET }}"
# Don't write pid file to current folder, where we may not have write access
c.ConfigurableHTTPProxy.pid_file = "/tmp/jupyter-proxy.pid"

# Authorise embedding in some iframes.
# Add "*" to allow embedding in all iframes (though it's dangerous and you probably
Expand Down Expand Up @@ -69,26 +71,38 @@
c.ServerApp.nbserver_extensions = {"nbgitpuller": True}

# Spawner

# Run as Docker containers
# https://jupyterhub-dockerspawner.readthedocs.io/en/latest/api/index.html
c.JupyterHub.spawner_class = "dockerspawner.DockerSpawner"
c.DockerSpawner.image = "{{ JUPYTER_DOCKER_IMAGE_LAB }}"
c.DockerSpawner.debug = True
c.DockerSpawner.remove = True
c.DockerSpawner.use_internal_ip = True
c.DockerSpawner.network_name = os.environ.get("NETWORK_NAME")
c.DockerSpawner.environment = {"CONTENT_SECURITY_POLICY": content_security_policy}
# Persist user data: this will create a new "jupyterhub-user-{username}" named volume on
# the host for every Docker container.
# https://jupyterhub-dockerspawner.readthedocs.io/en/latest/data-persistence.html
notebook_dir = "/home/jovyan/work"
c.DockerSpawner.notebook_dir = notebook_dir
c.DockerSpawner.volumes = {"jupyterhub-user-{username}": notebook_dir}
# Limit spawner cpu and memory
{% if JUPYTER_LAB_CPU_LIMIT %}
c.Spawner.cpu_limit = {{ JUPYTER_LAB_CPU_LIMIT }}
{% endif %}
c.Spawner.mem_limit = "{{ JUPYTER_LAB_MEMORY_LIMIT }}"

if os.environ.get("SPAWNER") == "docker":
# Run as Docker containers
# https://jupyterhub-dockerspawner.readthedocs.io/en/latest/api/index.html
c.JupyterHub.spawner_class = "dockerspawner.DockerSpawner"
c.DockerSpawner.image = "{{ JUPYTER_DOCKER_IMAGE_LAB }}"
c.DockerSpawner.debug = True
c.DockerSpawner.remove = True
c.DockerSpawner.use_internal_ip = True
c.DockerSpawner.network_name = os.environ.get("NETWORK_NAME")
c.DockerSpawner.environment = {"CONTENT_SECURITY_POLICY": content_security_policy}
# Persist user data: this will create a new "jupyterhub-user-{username}" named volume on
# the host for every Docker container.
# https://jupyterhub-dockerspawner.readthedocs.io/en/latest/data-persistence.html
c.DockerSpawner.notebook_dir = "/home/jovyan/work"
c.DockerSpawner.volumes = {"jupyterhub-user-{username}": c.DockerSpawner.notebook_dir}
elif os.environ.get("SPAWNER") == "kubernetes":
# Run as kubernetes pods
# https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html
# https://z2jh.jupyter.org/en/stable/resources/reference.html#helm-chart-configuration-reference
# spoiler: you're in for one hell of a ride...
c.JupyterHub.spawner_class = "kubespawner.KubeSpawner"
c.KubeSpawner.debug = True
c.KubeSpawner.hub_connect_url = "http://jupyterhub:8081"
# c.KubeSpawner.port = 8081
c.KubeSpawner.service_account = "jupyterhub"
c.KubeSpawner.image = "{{ JUPYTER_DOCKER_IMAGE_LAB }}"
c.KubeSpawner.environment = {"CONTENT_SECURITY_POLICY": content_security_policy}

{{ patch("jupyterhub-config") }}
7 changes: 6 additions & 1 deletion tutorjupyter/templates/jupyter/build/hub/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
FROM docker.io/jupyterhub/jupyterhub:4.0.0

# https://pypi.org/project/dockerspawner/
# https://pypi.org/project/jupyterhub-kubespawner/
# https://pypi.org/project/jupyterhub-ltiauthenticator/
# https://pypi.org/project/pymysql/
RUN pip install dockerspawner==12.1.0 jupyterhub-ltiauthenticator==1.5.1 pymysql==1.0.3
RUN pip install \
dockerspawner==12.1.0 \
jupyterhub-kubespawner==6.0.0 \
jupyterhub-ltiauthenticator==1.5.1 \
pymysql==1.0.3

CMD ["jupyterhub"]

0 comments on commit 8ac2440

Please sign in to comment.