Skip to content

Commit 2b9f31e

Browse files
authored
make jupyterhub idle server more easily configurable (#390)
## Overview Add new variables to easily configure idle jupyter user instances. ## Changes **Non-breaking changes** - Jupyterhub configurable idle server culling. - Add optional variables `JUPYTER_IDLE_SERVER_CULL_TIMEOUT`, `JUPYTER_IDLE_KERNEL_CULL_TIMEOUT` and `JUPYTER_IDLE_KERNEL_CULL_INTERVAL` that allows fined-grained configuration of user-kernel and server-wide docker image culling when their activity status reached a certain idle timeout threshold. - Enable idle kernel culling by default with a timeout of 1 day, and user server culling with timeout of 3 days. - Avoids the need for custom `JUPYTERHUB_CONFIG_OVERRIDE` specifically for idle server culling. If similar argument parameters should be defined using an older `JUPYTERHUB_CONFIG_OVERRIDE` definition, the new configuration strategy can be skipped by setting `JUPYTER_IDLE_KERNEL_CULL_TIMEOUT=0`. **Breaking changes** - n/a ## Related Issue / Discussion - Closes #389 (replaces) - Closes Ouranosinc/jupyterhub#21 (not required anymore)
2 parents 2da7a69 + 01b575c commit 2b9f31e

File tree

10 files changed

+103
-40
lines changed

10 files changed

+103
-40
lines changed

.bumpversion.cfg

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.34.0
2+
current_version = 1.35.0
33
commit = True
44
tag = False
55
tag_name = {new_version}
@@ -30,11 +30,11 @@ search = {current_version}
3030
replace = {new_version}
3131

3232
[bumpversion:file:RELEASE.txt]
33-
search = {current_version} 2023-10-10T15:33:10Z
33+
search = {current_version} 2023-10-16T14:37:32Z
3434
replace = {new_version} {utcnow:%Y-%m-%dT%H:%M:%SZ}
3535

3636
[bumpversion:part:releaseTime]
37-
values = 2023-10-10T15:33:10Z
37+
values = 2023-10-16T14:37:32Z
3838

3939
[bumpversion:file(version):birdhouse/config/canarie-api/docker_configuration.py.template]
4040
search = 'version': '{current_version}'

CHANGES.md

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@
1717

1818
[//]: # (list changes here, using '-' for each new entry, remove this when items are added)
1919

20+
[1.35.0](https://github.com/bird-house/birdhouse-deploy/tree/1.35.0) (2023-10-16)
21+
------------------------------------------------------------------------------------------------------------------
22+
23+
## Changes
24+
- Jupyterhub configurable idle server culling.
25+
- Add optional variables `JUPYTER_IDLE_SERVER_CULL_TIMEOUT`, `JUPYTER_IDLE_KERNEL_CULL_TIMEOUT` and
26+
`JUPYTER_IDLE_KERNEL_CULL_INTERVAL` that allows fined-grained configuration of user-kernel and server-wide
27+
docker image culling when their activity status reached a certain idle timeout threshold.
28+
- Enable idle kernel culling by default with a timeout of 1 day, and user server culling with timeout of 3 days.
29+
- Avoids the need for custom `JUPYTERHUB_CONFIG_OVERRIDE` specifically for idle server culling.
30+
If similar argument parameters should be defined using an older `JUPYTERHUB_CONFIG_OVERRIDE` definition,
31+
the new configuration strategy can be skipped by setting `JUPYTER_IDLE_KERNEL_CULL_TIMEOUT=0`.
32+
2033
[1.34.0](https://github.com/bird-house/birdhouse-deploy/tree/1.34.0) (2023-10-10)
2134
------------------------------------------------------------------------------------------------------------------
2235

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Generic variables
22
override SHELL := bash
33
override APP_NAME := birdhouse-deploy
4-
override APP_VERSION := 1.34.0
4+
override APP_VERSION := 1.35.0
55

66
# utility to remove comments after value of an option variable
77
override clean_opt = $(shell echo "$(1)" | $(_SED) -r -e "s/[ '$'\t'']+$$//g")

README.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ for a full-fledged production platform.
1414
* - releases
1515
- | |latest-version| |commits-since|
1616

17-
.. |commits-since| image:: https://img.shields.io/github/commits-since/bird-house/birdhouse-deploy/1.34.0.svg
17+
.. |commits-since| image:: https://img.shields.io/github/commits-since/bird-house/birdhouse-deploy/1.35.0.svg
1818
:alt: Commits since latest release
19-
:target: https://github.com/bird-house/birdhouse-deploy/compare/1.34.0...master
19+
:target: https://github.com/bird-house/birdhouse-deploy/compare/1.35.0...master
2020

21-
.. |latest-version| image:: https://img.shields.io/badge/tag-1.34.0-blue.svg?style=flat
21+
.. |latest-version| image:: https://img.shields.io/badge/tag-1.35.0-blue.svg?style=flat
2222
:alt: Latest Tag
23-
:target: https://github.com/bird-house/birdhouse-deploy/tree/1.34.0
23+
:target: https://github.com/bird-house/birdhouse-deploy/tree/1.35.0
2424

2525
.. |readthedocs| image:: https://readthedocs.org/projects/birdhouse-deploy/badge/?version=latest
2626
:alt: ReadTheDocs Build Status (latest version)

RELEASE.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.34.0 2023-10-10T15:33:10Z
1+
1.35.0 2023-10-16T14:37:32Z

birdhouse/config/canarie-api/docker_configuration.py.template

+4-4
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ SERVICES = {
109109
# NOTE:
110110
# Below version and release time auto-managed by 'make VERSION=x.y.z bump'.
111111
# Do NOT modify it manually. See 'Tagging policy' in 'birdhouse/README.rst'.
112-
'version': '1.34.0',
113-
'releaseTime': '2023-10-10T15:33:10Z',
112+
'version': '1.35.0',
113+
'releaseTime': '2023-10-16T14:37:32Z',
114114
'institution': 'Ouranos',
115115
'researchSubject': 'Climatology',
116116
'supportEmail': '${SUPPORT_EMAIL}',
@@ -142,8 +142,8 @@ PLATFORMS = {
142142
# NOTE:
143143
# Below version and release time auto-managed by 'make VERSION=x.y.z bump'.
144144
# Do NOT modify it manually. See 'Tagging policy' in 'birdhouse/README.rst'.
145-
'version': '1.34.0',
146-
'releaseTime': '2023-10-10T15:33:10Z',
145+
'version': '1.35.0',
146+
'releaseTime': '2023-10-16T14:37:32Z',
147147
'institution': 'Ouranos',
148148
'researchSubject': 'Climatology',
149149
'supportEmail': '${SUPPORT_EMAIL}',

birdhouse/config/jupyterhub/default.env

+15
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ export JUPYTER_LOGIN_BANNER_BOTTOM_SECTION=""
4343
# server for the change to take effect.
4444
export JUPYTERHUB_README=""
4545

46+
# Timeout (in seconds, default: 3 days) to shut down the user server when no kernels or terminals
47+
# are running and there is no activity. If undefined or set to zero, the feature will not be enabled.
48+
export JUPYTER_IDLE_SERVER_CULL_TIMEOUT=259200
49+
# Timeout (in seconds, default: 1 day) after which individual
50+
# user kernels/terminals are considered idle and ready to be culled.
51+
export JUPYTER_IDLE_KERNEL_CULL_TIMEOUT=86400
52+
# Interval (in seconds) on which to check for idle kernels exceeding the cull timeout value.
53+
# Enabled only if 'JUPYTER_IDLE_KERNEL_CULL_TIMEOUT' is provided and greater than zero.
54+
# If this value is not provided, equal to zero, or is set higher than 'JUPYTER_IDLE_KERNEL_CULL_TIMEOUT',
55+
# it will be automatically reduced by half of the timeout value to ensure that it can be effective.
56+
export JUPYTER_IDLE_KERNEL_CULL_INTERVAL=0
57+
4658
# Allow for adding new config or override existing config in
4759
# config/jupyterhub/jupyterhub_config.py.template.
4860
export JUPYTERHUB_CONFIG_OVERRIDE=""
@@ -68,6 +80,9 @@ OPTIONAL_VARS="
6880
\$JUPYTERHUB_CONFIG_OVERRIDE
6981
\$JUPYTERHUB_DOCKER
7082
\$JUPYTERHUB_VERSION
83+
\$JUPYTER_IDLE_SERVER_CULL_TIMEOUT
84+
\$JUPYTER_IDLE_KERNEL_CULL_TIMEOUT
85+
\$JUPYTER_IDLE_KERNEL_CULL_INTERVAL
7186
"
7287

7388
# add any component that this component requires to run

birdhouse/config/jupyterhub/jupyterhub_config.py.template

+34
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,38 @@ blocked_users = {'authtest', '${CATALOG_USERNAME}', 'anonymous'}
187187
c.Authenticator.blacklist = blocked_users # v0.9+
188188
c.Authenticator.blocked_users = blocked_users # v1.2+
189189

190+
191+
# ------------------------------------------------------------------------------
192+
# Shutdown idle user server based on configured timeouts.
193+
# ------------------------------------------------------------------------------
194+
# Timeout (in seconds, default: 3 days) to shut down the user server when no kernels or terminals
195+
# are running and there is no activity. If undefined or set to zero, the feature will not be enabled.
196+
jupyter_idle_server_cull_timeout = int("${JUPYTER_IDLE_SERVER_CULL_TIMEOUT}" or 0)
197+
if jupyter_idle_server_cull_timeout:
198+
c.Spawner.args.append('--NotebookApp.shutdown_no_activity_timeout={}'.format(jupyter_idle_server_cull_timeout))
199+
# Timeout (in seconds, default: 1 day) after which individual
200+
# user kernels/terminals are considered idle and ready to be culled.
201+
jupyter_idle_kernel_cull_timeout = int("${JUPYTER_IDLE_KERNEL_CULL_TIMEOUT}" or 0)
202+
# Interval (in seconds, default: half of timeout) on which to check for idle kernels exceeding the cull timeout value.
203+
jupyter_idle_kernel_cull_interval = int("${JUPYTER_IDLE_KERNEL_CULL_INTERVAL}" or 0)
204+
if jupyter_idle_kernel_cull_timeout:
205+
if not jupyter_idle_kernel_cull_interval or jupyter_idle_kernel_cull_interval > jupyter_idle_kernel_cull_timeout:
206+
jupyter_idle_kernel_cull_interval = jupyter_idle_kernel_cull_timeout / 2
207+
c.Spawner.args.extend([
208+
'--MappingKernelManager.cull_idle_timeout={}'.format(jupyter_idle_kernel_cull_timeout),
209+
'--MappingKernelManager.cull_interval={}'.format(jupyter_idle_kernel_cull_interval),
210+
'--TerminalManager.cull_inactive_timeout={}'.format(jupyter_idle_kernel_cull_timeout),
211+
'--TerminalManager.cull_interval={}'.format(jupyter_idle_kernel_cull_interval),
212+
])
213+
# Culling kernels which have one or more connections for idle but open notebooks and/or terminals.
214+
# Otherwise, browser tabs, notebooks and terminals all have to be closed for culling to work.
215+
if jupyter_idle_server_cull_timeout or jupyter_idle_kernel_cull_timeout:
216+
c.Spawner.args.extend([
217+
'--MappingKernelManager.cull_connected=True',
218+
'--TerminalManager.cull_connected=True',
219+
])
220+
221+
# ------------------------------------------------------------------------------
222+
# Configuration overrides
223+
# ------------------------------------------------------------------------------
190224
${JUPYTERHUB_CONFIG_OVERRIDE} # noqa

birdhouse/env.local.example

+26-25
Original file line numberDiff line numberDiff line change
@@ -267,17 +267,18 @@ export GEOSERVER_ADMIN_PASSWORD=geoserverpass
267267
# allow jupyterhub user selection of which notebook image to run
268268
# see https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html
269269
#export ENABLE_JUPYTERHUB_MULTI_NOTEBOOKS="
270-
#c.DockerSpawner.image_whitelist = {os.environ['JUPYTERHUB_IMAGE_SELECTION_NAMES'].split()[0]: os.environ['DOCKER_NOTEBOOK_IMAGES'].split()[0],
271-
# os.environ['JUPYTERHUB_IMAGE_SELECTION_NAMES'].split()[1]: os.environ['DOCKER_NOTEBOOK_IMAGES'].split()[1],
272-
# os.environ['JUPYTERHUB_IMAGE_SELECTION_NAMES'].split()[2]: os.environ['DOCKER_NOTEBOOK_IMAGES'].split()[2],
273-
# os.environ['JUPYTERHUB_IMAGE_SELECTION_NAMES'].split()[3]: os.environ['DOCKER_NOTEBOOK_IMAGES'].split()[3],
274-
# 'jupyter/scipy-notebook': 'jupyter/scipy-notebook',
275-
# 'jupyter/r-notebook': 'jupyter/r-notebook',
276-
# 'jupyter/tensorflow-notebook': 'jupyter/tensorflow-notebook',
277-
# 'jupyter/datascience-notebook': 'jupyter/datascience-notebook',
278-
# 'jupyter/pyspark-notebook': 'jupyter/pyspark-notebook',
279-
# 'jupyter/all-spark-notebook': 'jupyter/all-spark-notebook',
280-
# }
270+
#c.DockerSpawner.image_whitelist = {
271+
# os.environ['JUPYTERHUB_IMAGE_SELECTION_NAMES'].split()[0]: os.environ['DOCKER_NOTEBOOK_IMAGES'].split()[0],
272+
# os.environ['JUPYTERHUB_IMAGE_SELECTION_NAMES'].split()[1]: os.environ['DOCKER_NOTEBOOK_IMAGES'].split()[1],
273+
# os.environ['JUPYTERHUB_IMAGE_SELECTION_NAMES'].split()[2]: os.environ['DOCKER_NOTEBOOK_IMAGES'].split()[2],
274+
# os.environ['JUPYTERHUB_IMAGE_SELECTION_NAMES'].split()[3]: os.environ['DOCKER_NOTEBOOK_IMAGES'].split()[3],
275+
# 'jupyter/scipy-notebook': 'jupyter/scipy-notebook',
276+
# 'jupyter/r-notebook': 'jupyter/r-notebook',
277+
# 'jupyter/tensorflow-notebook': 'jupyter/tensorflow-notebook',
278+
# 'jupyter/datascience-notebook': 'jupyter/datascience-notebook',
279+
# 'jupyter/pyspark-notebook': 'jupyter/pyspark-notebook',
280+
# 'jupyter/all-spark-notebook': 'jupyter/all-spark-notebook',
281+
#}
281282
#"
282283

283284
# Load jobs to automatically deploy the custom notebooks from the specific images
@@ -311,7 +312,8 @@ export GEOSERVER_ADMIN_PASSWORD=geoserverpass
311312
# Path to the file containing the clientID for the google drive extension for jupyterlab
312313
# This file will be mounted into JupyterLab instances.
313314
# It should contain the following data : {"clientId":"<add_client_id_here>"}
314-
# To setup a project and find the clientID, check the doc at : https://github.com/jupyterlab/jupyterlab-google-drive/blob/master/docs/setup.md
315+
# To setup a project and find the clientID, check the doc at :
316+
# https://github.com/jupyterlab/jupyterlab-google-drive/blob/master/docs/setup.md
315317
#export JUPYTER_GOOGLE_DRIVE_SETTINGS=
316318

317319
# URL to terms and conditions for logging into Jupyter.
@@ -332,24 +334,23 @@ export GEOSERVER_ADMIN_PASSWORD=geoserverpass
332334
# export JUPYTERHUB_README=""
333335
#fi
334336

337+
# Timeout (in seconds, default: 3 days) to shut down the user server when no kernels or terminals
338+
# are running and there is no activity. If undefined or set to zero, the feature will not be enabled.
339+
#export JUPYTER_IDLE_SERVER_CULL_TIMEOUT=259200
340+
# Timeout (in seconds, default: 1 day) after which individual
341+
# user kernels/terminals are considered idle and ready to be culled.
342+
#export JUPYTER_IDLE_KERNEL_CULL_TIMEOUT=86400
343+
# Interval (in seconds) on which to check for idle kernels exceeding the cull timeout value.
344+
# Enabled only if 'JUPYTER_IDLE_KERNEL_CULL_TIMEOUT' is provided and greater than zero.
345+
# If this value is not provided, equal to zero, or is set higher than 'JUPYTER_IDLE_KERNEL_CULL_TIMEOUT',
346+
# it will be automatically reduced by half of the timeout value to ensure that it can be effective.
347+
#export JUPYTER_IDLE_KERNEL_CULL_INTERVAL=0
348+
335349
# Allow for adding new config or override existing config in
336350
# config/jupyterhub/jupyterhub_config.py.template.
337351
#
338352
#export JUPYTERHUB_CONFIG_OVERRIDE="
339353
#
340-
# Sample below will shutdown idle server after 3 days and idle kernel after 1 day.
341-
#
342-
#c.Spawner.args.extend([
343-
## Shut down the server after N seconds with no kernels or terminals running and no activity.
344-
#'--NotebookApp.shutdown_no_activity_timeout={}'.format(3*24*60*60) , # 3 days
345-
## Timeout (in seconds) after which a kernel is considered idle and ready to be culled.
346-
#'--MappingKernelManager.cull_idle_timeout={}'.format(24*60*60), # 1 day
347-
## Culling kernels which have one or more connections for idle but open notebooks.
348-
## Otherwise, browser have to be closed for culling to work.
349-
#'--MappingKernelManager.cull_connected=True',
350-
#])
351-
#
352-
#
353354
# Sample below will allow for sharing notebooks between Jupyter users.
354355
# Note all shares are public.
355356
#

docs/source/conf.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@
6969
# built documents.
7070
#
7171
# The short X.Y version.
72-
version = '1.34.0'
72+
version = '1.35.0'
7373
# The full version, including alpha/beta/rc tags.
74-
release = '1.34.0'
74+
release = '1.35.0'
7575

7676
# The language for content autogenerated by Sphinx. Refer to documentation
7777
# for a list of supported languages.

0 commit comments

Comments
 (0)