I am experiencing a “Permission Denied” error when attempting to mount a local NFS (Network File System) directory to the notebook directory in a container using SwarmSpawner for JupyterHub. The goal is to provide persistent storage for user data by utilizing an NFS share. I use jupyterhub:3 for build the image.
jupyterhub_config.py
# Configuration file for jupyterhub.
import os
import sys
from dockerspawner import DockerSpawner
from dockerspawner import SwarmSpawner
from oauthenticator.generic import GenericOAuthenticator
import logging
c = get_config()
class CustomSwarmSpawner(SwarmSpawner):
def _options_form_default(self):
cpu_options = [1, 2] # Possible values for CPUs
memory_options = [1, 2, 4] # Possible values for memory in GB
form_template = """
<div class="form-group">
<label for="stack">Choose an image</label>
<select id="stacks" class="form-control" name="stack">
<option value="jupyter/base-notebook:2023-04-27">base-notebook</option>
<option value="jupyter/scipy-notebook:2023-01-30">scipy-notebook</option>
</select>
</div>
<label for="cpu_limit">CPUs:</label>
<select class="form-control" name="cpu_limit" id="cpu_limit">
{cpu_options}
</select>
<label for="memory_limit">Memory (GiB):</label>
<select class="form-control" name="memory_limit" id="memory_limit">
{memory_options}
</select>
"""
# Populate the CPU options
cpu_select = "\n".join([f"<option>{cpu}</option>" for cpu in cpu_options])
# Populate the memory options
memory_select = "\n".join([f"<option>{memory}</option>" for memory in memory_options])
# Render the final form
options_form = form_template.format(cpu_options=cpu_select, memory_options=memory_select)
return options_form
def options_from_form(self, formdata):
options = {}
options['stack'] = formdata.get('stack', [''])[0].strip()
options['mem_limit'] = formdata.get('memory_limit', [''])[0].strip() + "G"
options['cpu_limit'] = int(formdata.get('cpu_limit', [''])[0].strip())
self.image = options['stack']
self.extra_placement_spec = { 'constraints' : ['node.role==worker'] }
self.mem_limit = options['mem_limit']
self.cpu_limit = options['cpu_limit']
return options
def create_dir_hook(SwarmSpawner):
username = SwarmSpawner.user.name # get the username
volume_path = os.path.join('/mnt/nfs/jupyterhub/volumes', username)
if not os.path.exists(volume_path):
os.makedirs(volume_path, 0o755)
mounts_user = [
{
'type': 'bind',
'source': volume_path,
'target': '/home/jovyan'
}
]
SwarmSpawner.environment = {'GRANT_SUDO': 'yes'}
SwarmSpawner.extra_container_spec = {'mounts': mounts_user}
SwarmSpawner.notebook_dir = '/home/jovyan'
c.JupyterHub.authenticator_class = GenericOAuthenticator
c.GenericOAuthenticator.client_id = 'jupyter'
c.GenericOAuthenticator.client_secret = 'ELTSI1XMbfuvUwNVP98evacc4bStZhhu'
c.GenericOAuthenticator.token_url = 'https://vm02gpu.labs247.id:8083/auth/realms/yavai/protocol/openid-connect/token'
c.GenericOAuthenticator.userdata_url = 'https://vm02gpu.labs247.id:8083/auth/realms/yavai/protocol/openid-connect/userinfo'
c.GenericOAuthenticator.userdata_params = {'state': 'state'}
c.GenericOAuthenticator.username_claim = 'preferred_username'
c.GenericOAuthenticator.login_service = 'Keycloak'
c.GenericOAuthenticator.scope = ['openid', 'profile']
c.GenericOAuthenticator.oauth_callback_url = 'https://172.20.3.60:8333/hub/oauth_callback'
c.JupyterHub.authenticator_class.login_handler._OAUTH_AUTHORIZE_URL = 'https://vm02gpu.labs247.id:8083/auth/realms/yavai/protocol/openid-connect/auth'
c.LocalAuthenticator.create_system_users = True
c.GenericOAuthenticator.allow_all = True
c.GenericOAuthenticator.allow_existing_users = True
c.Authenticator.auto_login = True
c.JupyterHub.ssl_key = '/home/deniy/certApps/labs247.key'
c.JupyterHub.ssl_cert = '/home/deniy/certApps/labs247.crt'
c.JupyterHub.spawner_class = CustomSwarmSpawner
c.Spawner.pre_spawn_hook = create_dir_hook
c.JupyterHub.hub_ip = '0.0.0.0'
c.JupyterHub.hub_connect_ip = 'hub'
network_name = os.environ['DOCKER_NETWORK_NAME']
c.SwarmSpawner.network_name = network_name
#notebook_dir = '/home/jovyan/work'
#c.SwarmSpawner.notebook_dir = notebook_dir
c.SwarmSpawner.http_timeout = 300
c.SwarmSpawner.start_timeout = 300
c.SwarmSpawner.remove_containers = True
c.ResourceUseDisplay.track_cpu_percent = True
c.Spawner.cmd = ['start.sh', 'jupyter', 'labhub']
c.SwarmSpawner.debug = True
c.JupyterHub.log_level = logging.DEBUG
c.JupyterHub.load_roles = [
{
"name": "cull-idle-role",
"scopes": [
"list:users",
"read:users:activity",
"read:servers",
"read:metrics",
"delete:servers",
"access:servers"
# "admin:users", # if using --cull-users
],
# assignment of role's permissions to:
"services": ["cull-idle"],
"users": ['demo'],
}
]
c.JupyterHub.services = [
{
'name': 'cull-idle',
'command': [sys.executable, 'cull_idle_servers.py', '--timeout=3600'],
}
]
error logs at service:
I need help.