For container admins#
Container admins have a regular user account on the host machine. They may access the host machine via SSH to install, configure, start and stop the Podman container running the JupyterHub.
SSH login to host machine#
The host machine (if configured along the lines of the instructions for host admins) provides SSH access for container admins with two-factor authentication: password plus cryptographic key.
Login to the host machine:
ssh -p 986 testhub_user@your-host.your-domain.org
where 986
is to be replaced by the host machine’s SSH port (ask your host admin for this number).
After the first login, change your password:
passwd
Logout from the host machine with
logout
If you are on Windows, you may try PuTTY. Newer variants of Windows 10 (and also Windows 11) provide Linux-like SSH.
File Transfer#
To transfer files to the host machine connect via sshfs
:
sshfs -p 986 testhub_user@your-host.your-domain.org: /your/local/mount/point
where 986
is to be replaced by the host machine’s SSH port and /your/local/mount/point
is an empty directory on your local machine (create before first use).
Contents of your home directory then should appear in that directory.
To close the connection run:
fusermount -u /your/local/mount/point
If you are in Windows, you may try WinSCP.
Run an Ananke container#
Ananke uses Podman containers for deployment.
Introduction to images and containers#
An image is a blueprint for containers. A container is similar to a virtual machine. Its purpose is to run a program in isolation from other programs on the machine. From one image, one may create one or more containers.
Images are built from image definitions consisting of
a
Containerfile
containing image building instructions for Podman,assets (files to copy to the image).
Ananke’s image definitions are in ananke/images
.
Containers are built from images. Ananke containers require several config files and directories for storing runtime data. These files and directories are referred to as container definition and reside in ananke/containers
.
Currently, Ananke provides two images:
ananke-base
for JupyerHub without additional assessment tools,ananke-nbgrader
for JupyterHub with nbgrader.
If you want to run more than one Ananke container, that is, if you want to run several JupyterHubs, each container needs a separate container definition (subdirectory of ananke/containers
).
Introduction to Podman#
Note
You may skip this section, because under normal circumstances you won’t have to use Podman directly.
Podman provides several (sub-)commands to manage images and containers. To see all currently installed images, run
podman image ls
To remove an image run
podman rmi image_label_or_id
With
podman ps
podman ps --all
we get a list of currently running containers or of all containers on the system, respectively. To stop a currently running container run
podman stop container_label_or_id
Then either remove the container with
podman rm container_label_or_id
or continue its execution with
podman restart container_label_or_id
Install and run a container#
To get an Ananke container running proceed as follows (instructions are for Ananke without nbgrader, replace base
by nbgrader
wherever it appears to get Ananke with nbgrader):
Step 1: Get Ananke files#
There are two alternatives to get Ananke
Alternative 1: Download Ananke’s zipped GitHub repository and unzip it to your home directory on the host machine. On the host machine:
cd ~
wget https://github.com/jeflem/ananke/archive/refs/heads/main.zip
unzip main.zip
mv ananke-main ananke
Alternative 2: Clone Ananke’s GitHub repository. On the host machine:
cd ~
git clone https://github.com/jeflem/ananke.git
Then you should have a directory named ananke
in your home directory.
Step 2: Get an Ananke image#
There are two alternatives to get an Ananke image.
Alternative 1 (faster): Run
cd ~/ananke
./ananke load
This asks for an image to load and then downloads the image file from Ananke website.
Alternative 2 (customizable): Run
cd ~/ananke
./ananke build
This asks for an image definition and then builds a new image from that definition.
Note
If you want to build the ananke-nbgrader
image, you first have to build ananke-base
, because ananke-nbgrader
is built on top of ananke-base
.
Step 3: Choose container definition template#
In ananke/containers
there are several subdirectories starting with template-
. Check each templates readme.md
and decide for the most suitable one. Then copy the template (here we go with template-base
):
cd ~/ananke/containers
cp -R template-base my-hub
Step 4: Adjust container configuration#
Open your container definition’s config.py
in a text editor:
nano ~/ananke/containers/my-hub/config.py
Adjust settings as needed. In most cases the port
has to be set to a value provided to you by your host machine’s admin.
If you plan to mount external data directories to the container, do it now. Mounting directories to running containers is not supported by Podman. See Shared directories for more details.
If you plan to use NVIDIA GPUs inside the container, set the required
option as described in Tensorflow with GPU support.
Step 5: Adjust JupyterHub configuration#
JupyterHub configuration files are in ananke/containers/my-hub/jupyterhubc_conf.d
. Settings may be changed during container runtime, too. But some settings are required for successful start-up.
In 00_base.py
set c.JupyterHub.base_url
to the value provided by your host machine’s admin.
Configure communication with your learning management system (LMS) in 30_lms.py
. See LTI configuration for details.
Step 6: Create and start the container#
Now it’s time to start the container:
cd ~/ananke
./ananke create
Select the container definition to use and answer all questions.
If you go to https://your-domain.org/your_hub_name
with your web browser, you should see some message that login requires LTI. Direct login without LMS is not possible!
Step 7: (Almost) done#
Configure your LMS, see LTI configuration for details. Then go to JupyterHub via your LMS.
If something isn’t working properly, open a root shell inside the container:
cd ~/ananke
./ananke-my-hub.sh
In the root shell type
journalctl
to see the logs.
Step 8: Optional features#
You may install some or all optional features provided my Ananke. See Useful optional features for details.
Root access to a container#
For each container created with ananke create
there is a shell script ananke-container-name.sh
starting a root shell inside the container:
cd ~/ananke
./ananke-my-hub.sh
This opens a shell inside the container. There you are the container’s root user. You may check the logs (journalctl
) or install additional software.
The hub users home directories are in /var/lib/private
inside the container.
Remove a container#
To remove an Ananke container run
cd ~/ananke
./ananke remove
Choose the container to remove.
Files living in volumes mounted to the container (everything you see in ananke/containers/my-hub
) won’t be removed. During the removal procedure you’ll be asked whether ownership of those file shall be transfered to you. Without transfering ownership you may have problems deleting files because you do not have sufficient permissions. Do NOT transfer ownership if you plan to reuse the volumes in a new container!
Important
If you remove a container, all modifications to files inside the container not living in a mounted volume will be lost.
JupyterHub configuration options#
Behavior of JupyterHub can be adjusted in ananke/containers/my-hub/jupyterhub_config.d
. After modifying files there, you have to restart the hub: open a root shell and run
systemctl restart jupyterhub
Restarting the hub does not kill user’s JupyterLabs. Thus, the hub can be restarted whenever necessary. Only users currently active users may experience cumbersome error messages for some seconds.
LTI configuration#
LTI communication between JupyterHub and your learning management system (LMS) has to be configured on both sides.
JupyterHub#
On hub side LTI configuration is in ananke/containers/my-hub/jupyterhub_config.d/30_lms.py
. Write the URLs provided by your LMS to corresponding lines.
Important
Note that the value for c.LTI13Authenticator.client_id
has to be a list of strings even if only one client ID is present.
Don’t abuse 30_lms.py
for other configuration purposes than the described LTI configuration.
This may lead to unexpected behavior.
LMS#
For your LMS you need the following configuration information (field names are taken from Moodle here and may be slightly different in other LMS):
tool URL:
https://your-domain.org/your_hub_name/
public key type:
Keyset URL
public keyset:
https://your-domain.org/your_hub_name/services/kore/jwks
initiate login URL:
https://your-domain.org/your_hub_name/hub/lti13/oauth_login
redirection URI(s):
https://your-domain.org/your_hub_name/hub/lti13/oauth_callback
default launch container:
Existing window
Important
For security reasons JupyterHub does not allow to be embedded into another website.
Thus, in Moodle only Existing window
works.
Even new window
is not possible due to it’s implementation in Moodle via embedding techniques.
Hub admins#
To give a hub user admin privileges inside the hub (see For hub admins), get the user’s username (from URL .../user/USERNAME/...
when user visits the hub) and write it to ananke/containers/my-hub/jupyterhub_config.d/20_users.py
:
c.Authenticator.admin_users.add('hub_admin_user_id')
If there are more than one hub admin, use one such line per hub admin.
User server behavior#
In ananke/containers/my-hub/jupyterhub_config.d/10_servers.py
you may modify JupyterHub’s behavior concerning single user’s JupyterLabs.
Time zone#
After start-up the container’s time zone is set to Europe/Berlin
. To modify the time zone run timedatectl list-timezones
in the container’s root shell and then set the time zone with timedatectl set-timezone TIME_ZONE_FROM_LIST
.
Backups#
Hub user’s home directories and the hub’s configuration are accessible from outside the container.
To back up home directories and configuration, simply make a copy of the ananke/containers/my-hub
directory.
Example backup procedure:
Log in to the host machine via SSH (see SSH login to host machine).
Go to the
containers
directory:cd ~/ananke/containers
Copy all relevant files to a tar archive:
tar czfv backup.tar.gz ./my-hub
(some files aren’t readable, but those files only contain runtime information from Jupyter and can be safely ignored).
Move the tar archive to some save place (see File Transfer).
Modify global Python environment#
There are two default Python environments: jhub
(contains all the Jupyter stuff, do not modify), python3
(the environment in which notebooks run, install all required packages here): inside the container’s root shell run
conda install package_name
Important
Modification of Python environment is done inside the container. Replacing the container by a new one (even from the same image) resets the Python environment.
Python environments live in /opt/conda/envs
inside the container.
Additional global Python environments#
To create another conda environment for all users next to the default python3
environment in the container’s root shell proceed as follows:
Create a new conda environment and install the
ipykernel
package to the new environment.conda create -y --name NAME_OF_NEW_ENV ipykernel
(optional) Rename the new environment’s Python kernel. Default name is
Python 3 (ipykernel)
.conda activate NAME_OF_NEW_ENV python -m ipykernel install --sys-prefix --display-name 'NEW DISPLAY NAME OF KERNEL'
Update Jupyter’s kernel list.
conda activate jhub python -m nb_conda_kernels list --CondaKernelSpecManager.kernelspec_path='--sys-prefix' --CondaKernelSpecManager.env_filter=None
The new environment’s kernel appears in all users’ JupyterLabs after a few seconds without restarting the hub or user servers.
Important
Creation of an additional global Python environment is done inside the container. Replacing the container by a new one (even from the same image) removes all additional Python environments.
Python environments live in /opt/conda/envs
inside the container.
Log files#
To see the container’s log files, run
journalctl -r
inside the container.
The -r
option shows the newest messages first.
There’s also a list of all users having visited the hub via LTI.
It’s a JSON file with hub username, first name, last name, email, LMS username.
It’s at /opt/userdata.json
.
Check resource limits#
To see resource limits of the container, run
cat /sys/fs/cgroup/cpu.max
cat /sys/fs/cgroup/memory.max
in the container’s root shell.
You may set a container’s resource limits by editing ananke/containers/my-hub/config.py
before (!) creating the container.
Per-user resource limits can be configured in ananke/containers/my-hub/jupyterhub_config.d/10_servers.py
.
Updates#
You should update your JupyterHub container regularly.
Updates inside container#
You may run standard update commands in the container’s root shell:
apt update
apt upgrade
Update your Python environments, too, by running
conda update --all
in each environment.
Important
Updating packages in the jhub
environment may cause lots of troubles. Unexperienced users better do not touch this environment. To get newer versions of Jupyter components update the whole container (see below).
Update the whole container#
Alternatively to in-container updates yoyu may replace your container by a new one based on the latest Ananke release.
Remember to back up your user’s home directories and modifications you made to the container (Python environments, …).
Update from Ananke 0.4 to Ananke 0.5#
Ananke 0.5 brings breaking changes in course handling for the Ananke nbgrader image and modified directory structure for both images base and nbgrader. Thus, update from Ananke 0.4 needs some extra attention.
All courses have to be recreated and container configuration files as well as hub user data have to be moved. We assume that you have following directory structure:
~/ananke/ --> copy of Ananke 0.4 repository
ananke-nbgrader-hub/ --> the container to update
runtime/ --> container configuration and data
images/ ---> Ananke 0.4 image definitions
Proceed as follows:
Follow steps 1 and 2 of above install instructions, but name the base directory
ananke_0.5
instead ofananke
(don’t overwrite your Ananke 0.4 install).(nbgrader only) Tell your instructor users to backup all their courses. Alternatively, Backup all courses of your hub.
Start a root shell in the old container with
shell.sh
and remove all directories in/home
starting withc-
(the courses).Remove the old container with
remove.sh
from Ananke 0.4.Rename the container directory from
ananke-nbgrader-hub
tocontainers
.Rename the runtime directory from
containers/runtime
tocontainers/my-hub
.Copy
config.py
from aananke_0.5/containers/templates-base_or_nbgrader
toananke/containers/my-hub
.Adjust settings in
config.py
according to settings in formerrun.sh
.Copy all relevant files from
ananke_0.5
toananke
. These includeimages
(replace old directory),ananke
(the new management script),containers/templates-*
(if you need them).Start the new container with
cd ~/ananke
./ananke create
Tell your instructor users to log in to the hub from all their LMS courses. Then backup data can be copied to the new courses.
Useful optional features#
Base packages#
To install NumPy, Pandas, Matplotlib, Seaborn, Plotly run /opt/install/base.sh
in the container’s root shell.
You have to restart all user servers to make Matplotlib and Plotly work.
To get interactive Matplotlib output use the cell magic %matplotlib widget
. To deactivate interactive output use %matplotlib inline
.
MyST markdown rendering#
To make JupyterLab render MyST markdown run /opt/install/myst.sh
in the container’s root shell and restart all user servers.
WebDAV and other file systems#
The JupyterLab extension jupyter-fs allows adding additional file managers based on a wide range of file systems, including WebDAV. WebDAV provides access to Nextcloud accounts, for instance.
To install jupyter-fs run /opt/install/jupyterfs.sh
in the container’s root shell and restart all user servers.
Note
Files cannot be copied or moved between jupyter-fs file browsers and JupyterLab’s standard file browser. Thus, the install script will add a jupyter-fs file browser to all users’ JupyterLabs showing a user’s home directory. To copy/move files from a user-defined WebDAV or other source the files have to be pasted in the jupyter-fs home file browser, not in the standard file browser.
To add further default file browsers for all users edit /opt/conda/envs/jhub/etc/jupyter/jupyter_server_config.py
.
See File transfer for hub users for configuration of user-defined file systems.
JupyterLab real-time collaboration#
The jupyterlab-collaboration
extension provides real-time collaboration for working with notebooks.
Several users share one JupyterLab session and instantly see other users’ edits and cell execution results.
Install#
Install the collaboration extension by running /opt/install/rtc.sh
in the container’s root shell.
Rename the 80_rtc.py.disabled
in ananke/containers/my-hub/jupyterhub_config.d/
to 80_rtc.py
and define your public and private collaboration rooms in that file.
Then restart the hub with systemctl restart jupyterhub
.
Note
For each collaboration room there is a user account inside the container. All files created during collaboration sessions are stored in /home/rtc-name-of-room
inside the container.
Note
The collaboration extension is disabled by default for all hub users. But users may enable the extension on their own. See JupyterLab RTC for hub users.
Usage#
Usage of the collaboration feature is described in JupyterLab RTC for hub users.
Remove a collaboration room#
To remove a collaboration room modify 80_rtc.py
, and remove the room’s user account inside the container: userdel rtc-ROOM-NAME
.
This does not remove any files create during corresponding collaboration sessions. To remove files, too, either use userdel -r rtc-ROOM-NAME
instead or remove /home/rtc-ROOM-NAME
manually.
Language server protocols (LSP)#
LSP support allows for code completion, automatic code formatting and several other useful features. To install LSP support for JupyterLab run /opt/install/lsp.sh
in the container’s root shell and restart the hub as well as all user servers.
TensorFlow with GPU support#
If the host machine has got one or more GPUs and Podman is configured to provide GPU access inside containers, then TensorFlow should be installed with GPU support. Have a look at GPU support in the documentation for host admins and/or ask your host admin for details on GPU availability.
Configuration#
Ananke supports autoconfiguration of NVIDIA GPUs during container creation (if GPU support for Podman is available on your host machine). If no GPUs are found by the container creation procedure (either not NVIDIA or for other reasons), add a line
config['podman_args'].append('--device=YOUR_GPU_DEVICE_NAME')
to your container definition’s config.py
. Multiple such lines are possible, too, if you want to have access to multiple GPUs.
Test Podman GPU support#
In the container’s root shell run nvidia-smi
to see whether and how many GPUs are available inside the container (NVIDIA only).
Install Tensorflow#
To install TensorFlow run /opt/install/tensorflow.sh
in the container’s root shell.
The install script also runs some TensorFlow commands to test the installation. Carefully check the output for errors.
Important
TensorFlow 2.17 does not have NumPy 2 support. The install script will downgrade NumPy to 1.26.4!
Assign GPUs to users#
Every hub user has access to all GPUs. How to confine a user’s TensorFlow commands to a subset of GPUs is described in TensorFlow and GPUs.