Hi,
I am working on a small Jupyterhub deployment for my university group. I try to spawn the singleuser servers in docker containers, while mounting the home directory of the user into the container. Therefore I want to use the SystemUserSpawner as it seems to provide the correct functionality. However, after spawning the container the server is run by root instead of the user, which is not as intended.
The image is build by repo2docker and pushed to a local registry from where it is pulled by DockerSpawner:
jupyter-repo2docker --image-name localhost:5000/test_tools:latest --ref 0.1.0 --target-repo-dir /home/test/test_tools --user-name jovyan --no-run --push test_tools
Configuration of the TLJH is:
from jupyter_client.localinterfaces import public_ips
c.JupyterHub.spawner_class = 'dockerspawner.SystemUserSpawner'
c.SystemUserSpawner.host_homedir_format_string = '/home/jupyter-{username}'
c.DockerSpawner.image = 'localhost:5000/test_tools:latest'
c.JupyterHub.hub_ip = public_ips()[0]
c.JupyterHub.cleanup_servers = False
c.DockerSpawner.cmd = ["jupyterhub-singleuser"]
c.DockerSpawner.remove = True
The handling of the user ids, to fix the file persmissions of the host-home-directory and the hub-home-directory is done by a start script at the entrypoint of the image, which was adapted from the docker-stacks repository (https://github.com/jupyter/docker-stacks/blob/c99a152f86065e0a1ef7b302d85f508b2d1c8a6a/base-notebook/start.sh) for which the SystemUserSpawner seems to be optimized:
#!/bin/bash
if [ $(id -u) == 0 ] ; then
# Only attempt to change the jovyan username if it exists
if id jovyan &> /dev/null ; then
echo "Set username to: $NB_USER"
usermod -d /home/$NB_USER -l $NB_USER jovyan
fi
# handle home and working directory if the username changed
if [[ "$NB_USER" != "jovyan" ]]; then
# changing username, make sure homedir exists
# (it could be mounted, and we shouldn't create it if it already exists)
if [[ ! -e "/home/$NB_USER" ]]; then
echo "Relocating home dir to /home/$NB_USER"
mv /home/jovyan "/home/$NB_USER"
fi
# if workdir is in /home/jovyan, cd to /home/$NB_USER
if [[ "$PWD/" == "/home/jovyan/"* ]]; then
newcwd="/home/$NB_USER/${PWD:13}"
echo "Setting CWD to $newcwd"
cd "$newcwd"
fi
fi
# Change UID of NB_USER to NB_UID if it does not match
if [ "$NB_UID" != $(id -u $NB_USER) ] ; then
echo "Set $NB_USER UID to: $NB_UID"
usermod -u $NB_UID $NB_USER
fi
# Set NB_USER primary gid to NB_GID (after making the group). Set
# supplementary gids to NB_GID and 100.
if [ "$NB_GID" != $(id -g $NB_USER) ] ; then
echo "Add $NB_USER to group: $NB_GID"
groupadd -g $NB_GID -o ${NB_GROUP:-${NB_USER}}
usermod -g $NB_GID -aG 100 $NB_USER
fi
fi
exec "$@"
When spinning up a server with this configuration the startup fails with a “Bad Gateway” message on the hub, while the logs of the container show
Running as root is not recommended. Use --allow-root to bypass.
However, if I change the Dockerspawner command to
c.DockerSpawner.cmd = ["jupyterhub-singleuser", "--allow-root"]
the server starts up fine, but it is now executed by root instead of the user.
I hope someone has an idea. I think I am missing only something small, but it can not figure out where it is. When I crawled the web I found similar questions but without solutions, so solving this might also be interesting for others.
Greetings
Lars