Package: jhub-swarmspawner
jhub-SwarmSpawnerenablesJupyterHubto spawn jupyter notebooks across Docker Swarm clusterMore info about Docker Serviceshere.PrerequisitesPython version 3.3 or above is required.Installationpipinstalljhub-swarmspawnerInstallation from GitHubgitclonehttps://github.com/rasmunk/SwarmSpawnercdSwarmSpawnerpythonsetup.pyinstallConfigurationYou can find an example jupyter_config.py insideexamples.The spawnerDocker Engine in Swarm mode and the related services work in a different way compared to Docker containers.Tell JupyterHub to use SwarmSpawner by adding the following lines to yourjupyterhub_config.py:c.JupyterHub.spawner_class='jhub.SwarmSpawner'c.JupyterHub.hub_ip='0.0.0.0'# This should be the name of the jupyterhub servicec.SwarmSpawner.jupyterhub_service_name='NameOfTheService'What isjupyterhub_service_name?Inside a Docker engine in Swarm mode the services use anameinstead of aipto communicate with each other.
‘jupyterhub_service_name’ is the name of ther service for the JupyterHub.NetworksIt’s important to put the JupyterHub service (also the proxy) and the services that are running jupyter notebook inside the same network, otherwise they couldn’t reach each other.
SwarmSpawner use the service’s name instead of the service’s ip, as a consequence JupyterHub and servers should share the same overlay network (network across nodes).#list of networksc.SwarmSpawner.networks=["mynetwork"]Define the services inside jupyterhub_config.pyYou can definecontainer_spec, *andnetworksinsidejupyterhub_config.py.Container_specThecommandandargsdefinitions depends on the image that you are using.
I.e the command must be possible to execute in the selected image
The ‘/usr/local/bin/start-singleuser.sh’ is provided by the jupyterbase-notebookThe start-singleuser.shargsassumes that the launched image is extended from a version of this.c.SwarmSpawner.container_spec={# The command to run inside the service'args':['/usr/local/bin/start-singleuser.sh']}Note:in a container spec,argssets the equivalent of CMD in the Dockerfile,commandsets the equivalent of ENTRYPOINT.
The notebook server command should not be the ENTRYPOINT, so generally useargs, notcommand, to specify how to launch the notebook server.See thisissuefor more info.PlacementThe spawner supports Docker Swarm service placement configurations to be imposed on the
spawned services. This includes the option to specifyconstraintsandpreferencesThese can be imposed as a placement policy to all services being spawned. E.g.c.SwarmSpawner.placement={'constraints':['node.hostname==worker1'],'preferences':['spread=node.labels.datacenter']}DockerimagesTo define which images are available to the users, a list ofimagesmust be declared
The individual dictionaries also makes it possible to define whether the image should mount any volumes when it is spawned# Available docker images the user can spawnc.SwarmSpawner.images=[{'image':'jupyter/base-notebook:30f16d52126f','name':'Minimal python notebook'},{'image':'jupyter/base-notebook:latest','name':'Image with automatic mount, supports Py2/3 and R,','mounts':mounts}]It is also possible to specify individual placement policies for each image.
E.g.# Available docker images the user can spawnc.SwarmSpawner.images=[{'image':'jupyter/base-notebook:30f16d52126f','name':'Minimal python notebook','placement':{'constraint':['node.hostname==worker1']}},]Beyond placement policy, it is also possible to specify a ‘whitelist’ of users who have
permission to start a specific image via the ‘access’ key. Such that only mentioned
usernames are able to spawn that particular image.# Available docker images the user can spawnc.SwarmSpawner.images=[{'image':'jupyter/base-notebook:30f16d52126f','name':'Minimal python notebook','access':['admin']},]To make the user able to select between multiple available images, the following must be
set.
If this is not the case, the user will simply spawn an instance of the default image. i.e. images[0]# Before the user can select which image to spawn,# user_options has to be enabledc.SwarmSpawner.use_user_options=TrueThis enables an image select form in the users /hub/home url path when a notebook hasen’t been spawned already.Bind a Host dirWith'type':'bind'you mount a local directory of the host inside the container.Remember that source should exist in the node where you are creating the service.notebook_dir=os.environ.get('NOTEBOOK_DIR')or'/home/jovyan/work'c.SwarmSpawner.notebook_dir=notebook_dirmounts=[{'type':'bind','source':'MountPointOnTheHost','target':'MountPointInsideTheContainer',}]VolumesWith'type':'volume'you mount a Docker Volume inside the container.
If the volume doesn’t exist it will be created.mounts=[{'type':'volume','source':'NameOfTheVolume','target':'MountPointInsideTheContainer',}]Named pathFor both types, volume and bind, you can specify a{name}inside the source:mounts=[{'type':'volume','source':'jupyterhub-user-{name}','target':'MountPointInsideTheContainer',}]username will be the hashed version of the username.Mount an anonymous volumeThis kind of volume will be removed with the service.mounts=[{'type':'volume','source':'','target':'MountPointInsideTheContainer',}]SSHFS mountIt is also possible to mount a volume that is an sshfs mount to another host
supports either passing{id_rsa}or{password}that should be used to authenticate,
in addition the typical sshfs flags are supported, defaults to port 22fromjhub.mountimportSSHFSMountermounts=[SSHFSMounter({'type':'volume','driver_config':{'name':'ucphhpc/sshfs:latest','options':{'sshcmd':'{sshcmd}','id_rsa':'{id_rsa}','big_writes':'','allow_other':'','reconnect':'','port':'2222','autoremove':'True'},}'source':'sshvolume-user-{name}','target':'/home/jovyan/work'})]Automatic removal of VolumesTo enact that a volume should be removed when the service is being terminated, there
are two options available, either use aanonymousvolume as shown above, which will
remove the volume when the owning sevice is removed. Otherwise you can control whether volumes
should be removed or not with the service with theautoremovelabel flag. e.g.mounts=[{'type':'volume','source':'jupyterhub-user-{name}','target':'MountPointInsideTheContainer','label':{'autoremove':'True'}}]Ormounts=[{'type':'volume','source':'jupyterhub-user-{name}','target':'MountPointInsideTheContainer','label':{'autoremove':'False'}}]With the default being ‘False’.Resource_specYou can also specify some resource for each servicec.SwarmSpawner.resource_spec={'cpu_limit':int(1*1e9),# (int) – CPU limit in units of 10^9 CPU shares.'mem_limit':int(512*1e6),# (int) – Memory limit in Bytes.'cpu_reservation':int(1*1e9),# (int) – CPU reservation in units of 10^9 CPU shares.'mem_reservation':int(512*1e6),# (int) – Memory reservation in Bytes}Using user_optionsThere is the possibility to set parameters usinguser_options# To use user_options in service creationc.SwarmSpawner.use_user_options=FalseTo control the creation of the services you have 2 ways, usingjupyterhub_config.pyoruser_options.Remember that at the end you are just using theDocker Engine API.user_options, if used, will overwrite jupyter_config.py for services.If you setc.SwarmSpawner.use_user_option = Truethe spawner will use the dict passed through the form or as json body when using the Hub Api.The spawner expect a dict with these keys:user_options={'container_spec':{# (string or list) command to run in the image.'args':['/usr/local/bin/start-singleuser.sh'],# name of the image'Image':'','mounts':mounts,'' : {# (int) – CPU limit in units of 10^9 CPU shares.'cpu_limit':int(1*1e9),# (int) – Memory limit in Bytes.'mem_limit':int(512*1e6),# (int) – CPU reservation in units of 10^9 CPU shares.'cpu_reservation':int(1*1e9),# (int) – Memory reservation in bytes'mem_reservation':int(512*1e6),},# dict of constraints'placement':{'constraints':[]},# list of networks'network':[],# name of service'name':''}}Names of the Jupyter notebook service inside Docker engine in Swarm modeWhen JupyterHub spawns a new Jupyter notebook server the name of the service will be{service_prefix}-{service_owner}-{service_suffix}You can change the service_prefix in this way:Prefix of the service in Dockerc.SwarmSpawner.service_prefix="jupyterhub"service_owneris the hexdigest() of the hasheduser.name.In case of named servers (more than one server for user)service_suffixis the name of the server, otherwise is always 1.Downloading imagesDocker Engine in Swarm mode downloads images automatically from the repository.
Either the image is available on the remote repository or locally, if not you will get an error.Because before starting the service you have to complete the download of the image is better to have a longer timeout (default is 30 secs)c.SwarmSpawner.start_timeout=60*5You can use all the docker images inside theJupyter docker-stacks.CreditDockerSpawnerCassinyioSpawnerLicenseAll code is licensed under the terms of the revised BSD license.