一、问题概述
最近项目使用到JupyterHub
,要求版本低于2.0.0,使用Docker部署。小编接到任务后立刻着手处理,过程中遇到了不少困难,特以此记录,供有需要的小伙伴参考。如果你在使用JupyterHub:1.5.1
过程中遇到以下问题,那本篇文章可能会给予你一定解决思路。
- 登录用户名密码未知
- 初始化工作空间失败。浏览器报错
Spawn failed: Server at http://127.0.0.1:58530/user/admin/ didn't respond in 30 seconds
- 初始化工作空间失败。日志报错
ModuleNotFoundError: No module named 'notebook'
- 初始化工作空间失败。日志报错
ModuleNotFoundError: No module named 'notebook.notebookapp'
扩展知识:JupyterHub:2.0.0之后的版本API状态码发生变更,部分API返回状态码从200变为了201
二、过程记录
-
小编从未接触过
JupyterHub
,一切从零开始,按照惯例,先查看了官方文档Docker
A starter docker image for JupyterHub gives a baseline deployment of JupyterHub using Docker.
Important: This
quay.io/jupyterhub/jupyterhub
image contains only the Hub itself, with no configuration. In general, one needs to make a derivative image, with at least ajupyterhub_config.py
setting up an Authenticator and/or a Spawner. To run the single-user servers, which may be on the same system as the Hub or not, Jupyter Notebook version 4 or greater must be installed.The JupyterHub docker image can be started with the following command:
docker run -p 8000:8000 -d --name jupyterhub quay.io/jupyterhub/jupyterhub jupyterhub
This command will create a container named
jupyterhub
that you can stop and resume withdocker stop/start
.The Hub service will be listening on all interfaces at port 8000, which makes this a good choice for testing JupyterHub on your desktop or laptop.
If you want to run docker on a computer that has a public IP then you should (as in MUST) secure it with ssl by adding ssl options to your docker configuration or by using an ssl enabled proxy.
Mounting volumes will allow you to store data outside the docker image (host system) so it will be persistent, even when you start a new image.
The command
docker exec -it jupyterhub bash
will spawn a root shell in your docker container. You can use the root shell to create system users in the container. These accounts will be used for authentication in JupyterHub’s default configuration.文档中提到的什么
衍生镜像
、配置文件
、创建系统用户
,对于初次接触JupyterHub
的小编而言,不是很理解。但小编怎么也没想到,这些不理解的名词最终均成为了使用过程中的拦路虎。 -
按照官方文档,拉取镜像并运行容器
docker pull jupyterhub/jupyterhub:1.5.1 docker run -p 8000:8000 -d --name jupyterhub jupyterhub/jupyterhub:1.5.1
-
通过浏览器访问http://192.168.10.60:8000,注意:192.168.10.60是小编的虚机地址,观众老爷们使用时请更换为自己的IP地址
到目前为止,似乎一切正常,但一切只是暴风雨前的宁静
三、相关问题
1. 登录用户名密码未知
一般docker容器运行起来后,都会预置一个用户,但JupyterHub
没有,需要用户自己创建。参考文档语句
You can use the root shell to create system users in the container.
具体的创建步骤如下:
# 1. 使用 docker exec -it jupyterhub bash 进入容器。
[root@kubernetes ~]# docker exec -it jupyterhub bash
# 2. 使用 adduser 创建用户。用户名密码根据个人喜好而定,以用户名admin为例,则执行命令adduser admin,密码根据后续提示输入
root@8f00c665c51b:/srv/jupyterhub# adduser admin
Adding user `admin' ...
Adding new group `admin' (1000) ...
Adding new user `admin' (1000) with group `admin' ...
Creating home directory `/home/admin' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for admin
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] Y
创建成功后,浏览器访问http://192.168.10.60:8000,使用对应的用户名密码登录,你应该可以看到如下界面
但问题还远远未结束,请继续往下看。
2. 初始化工作空间失败
初次使用账号密码登录后,JupyterHub将生成对应用户的工作空间,但由于JupyterHub缺少部分模块,你将出现如下报错
Spawn failed: Server at http://127.0.0.1:58530/user/admin/ didn’t respond in 30 seconds
通过docker logs -f jupyterhub
查看日志,可发现ModuleNotFoundError: No module named 'notebook'
,即缺少notebook模块
Traceback (most recent call last):
File "/usr/local/bin/jupyterhub-singleuser", line 5, in <module>
from jupyterhub.singleuser import main
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/singleuser/__init__.py", line 5, in <module>
from .app import main
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/singleuser/app.py", line 16, in <module>
App = import_item(JUPYTERHUB_SINGLEUSER_APP)
File "/usr/local/lib/python3.8/dist-packages/traitlets/utils/importstring.py", line 30, in import_item
module = __import__(package, fromlist=[obj])
ModuleNotFoundError: No module named 'notebook'
Task exception was never retrieved
future: <Task finished name='Task-5280' coro=<BaseHandler.spawn_single_user() done, defined at /usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py:802> exception=HTTPError()>
Traceback (most recent call last):
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py", line 1002, in spawn_single_user
await gen.with_timeout(
asyncio.exceptions.TimeoutError: Timeout
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py", line 1036, in spawn_single_user
raise web.HTTPError(
tornado.web.HTTPError: HTTP 500: Internal Server Error (Spawner failed to start [status=1]. The logs for admin may contain details.)
[W 2024-05-30 03:18:12.285 JupyterHub user:767] admin's server never showed up at http://127.0.0.1:58530/user/admin/ after 30 seconds. Giving up
[E 2024-05-30 03:18:12.314 JupyterHub gen:630] Exception in Future <Task finished name='Task-5281' coro=<BaseHandler.spawn_single_user.<locals>.finish_user_spawn() done, defined at /usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py:906> exception=TimeoutError("Server at http://127.0.0.1:58530/user/admin/ didn't respond in 30 seconds")> after timeout
Traceback (most recent call last):
File "/usr/local/lib/python3.8/dist-packages/tornado/gen.py", line 625, in error_callback
future.result()
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py", line 913, in finish_user_spawn
await spawn_future
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/user.py", line 748, in spawn
await self._wait_up(spawner)
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/user.py", line 795, in _wait_up
raise e
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/user.py", line 762, in _wait_up
resp = await server.wait_up(
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/utils.py", line 237, in wait_for_http_server
re = await exponential_backoff(
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/utils.py", line 185, in exponential_backoff
raise TimeoutError(fail_message)
TimeoutError: Server at http://127.0.0.1:58530/user/admin/ didn't respond in 30 seconds
既然缺少模块,那就安装模块。经验丰富的开发者,可能已经想到通过如下命令安装
pip3 install notebook
但如果你真的这样做,模块安装不会报错,但当你浏览器访问http://192.168.10.60:8000,再次初始化工作空间时,你将再次收到报错Spawn failed: Server at http://127.0.0.1:34220/user/admin/ didn't respond in 30 seconds
。同时,日志报错更换为了ModuleNotFoundError: No module named 'notebook.notebookapp'
。
Traceback (most recent call last):
File "/usr/local/bin/jupyterhub-singleuser", line 5, in <module>
from jupyterhub.singleuser import main
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/singleuser/__init__.py", line 5, in <module>
from .app import main
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/singleuser/app.py", line 16, in <module>
App = import_item(JUPYTERHUB_SINGLEUSER_APP)
File "/usr/local/lib/python3.8/dist-packages/traitlets/utils/importstring.py", line 30, in import_item
module = __import__(package, fromlist=[obj])
ModuleNotFoundError: No module named 'notebook.notebookapp'
Task exception was never retrieved
future: <Task finished name='Task-6806' coro=<BaseHandler.spawn_single_user() done, defined at /usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py:802> exception=HTTPError()>
Traceback (most recent call last):
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py", line 1002, in spawn_single_user
await gen.with_timeout(
asyncio.exceptions.TimeoutError: Timeout
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py", line 1036, in spawn_single_user
raise web.HTTPError(
tornado.web.HTTPError: HTTP 500: Internal Server Error (Spawner failed to start [status=1]. The logs for admin may contain details.)
[W 2024-05-30 03:38:52.002 JupyterHub user:767] admin's server never showed up at http://127.0.0.1:34220/user/admin/ after 30 seconds. Giving up
[E 2024-05-30 03:38:52.022 JupyterHub gen:630] Exception in Future <Task finished name='Task-6807' coro=<BaseHandler.spawn_single_user.<locals>.finish_user_spawn() done, defined at /usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py:906> exception=TimeoutError("Server at http://127.0.0.1:34220/user/admin/ didn't respond in 30 seconds")> after timeout
Traceback (most recent call last):
File "/usr/local/lib/python3.8/dist-packages/tornado/gen.py", line 625, in error_callback
future.result()
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py", line 913, in finish_user_spawn
await spawn_future
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/user.py", line 748, in spawn
await self._wait_up(spawner)
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/user.py", line 795, in _wait_up
raise e
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/user.py", line 762, in _wait_up
resp = await server.wait_up(
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/utils.py", line 237, in wait_for_http_server
re = await exponential_backoff(
File "/usr/local/lib/python3.8/dist-packages/jupyterhub/utils.py", line 185, in exponential_backoff
raise TimeoutError(fail_message)
TimeoutError: Server at http://127.0.0.1:34220/user/admin/ didn't respond in 30 seconds
通过小编分析,推断应该是在新版的notebook(小编出现问题的版本是7.2.0)中对notebook.notebookapp
进行了变更,通过百度等手段发现似乎新版的nodebook中notebook.notebookapp
变更为了notebook.app
。
故你不能直接升级最新版的nodebook,对于jupyterhub:1.5.1
,建议你安装notebook:6.5.2
,其他jupyterhub版本详见附录一:查询缺失的模块版本
pip3 install notebook==6.5.2
提示:若网络不好,建议使用pip国内镜像源,如清华镜像源
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple notebook==6.5.2
安装成功后,浏览器访问http://192.168.10.60:8000,你应该就可以发现工作空间初始化成功,并进入到如下界面
安装问题,到此告一段落,但对于Jupyterhub的深度使用者可能还想要自定义jupyterhub配置,详情请见 附录二:自定义JupyterHub配置。
四、总结(省流版)
1. 登录用户名密码未知
一般docker容器运行起来后,都会预置一个用户,但JupyterHub
没有,需要用户自己创建。命令如下:
# 1. 使用 docker exec -it jupyterhub bash 进入容器。
docker exec -it jupyterhub bash
# 2. 使用 adduser 创建用户。用户名密码根据个人喜好而定,以用户名admin为例,则执行命令adduser admin,密码根据后续提示输入
adduser admin
2. 初始化工作空间失败
当使用的JupyterHub
版本为1.5.1,登录后报错Spawn failed: Server at http://127.0.0.1:58530/user/admin/ didn't respond in 30 seconds
。同时通过docker logs -f jupyterhub
查看日志,发现ModuleNotFoundError: No module named 'notebook'
。可通过安装notebook:6.5.2
解决,其他jupyterhub版本详见附录一:查询缺失的模块版本
# 1. 使用 docker exec -it jupyterhub bash 进入容器。
docker exec -it jupyterhub bash
# 2. 安装notebook:6.5.2
pip3 install notebook==6.5.2
提示:若网络不好,建议使用pip国内镜像源,如清华镜像源
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple notebook==6.5.2
五、附录
附录一:查询缺失的模块版本
不同版本的JupyterHub
所需的依赖及依赖版本是不同的,这里给大家提供一种简单的方法。
官方除了提供jupyterhub/jupyterhub
镜像,还提供了一个jupyterhub/jupyterhub-demo
示例镜像,jupyterhub-demo的版本和jupyterhub的版本是相对应的,当jupyterhub依赖缺失时,我们可以去对应版本的jupyterhub-demo中寻找。例如,当jupyterhub:1.5.1存在依赖缺失,我们可以使用如下方式查找
-
创建
jupyterhub-demo
容器docker run -d --name jupyterhub-demo jupyterhub/jupyterhub-demo:1.5.1
-
进入
jupyterhub-demo
容器docker exec -it jupyterhub-demo bash
-
使用
pip3 list
查看依赖及版本demo@e5666f444a6b:/srv/jupyterhub$ pip3 list Package Version -------------------- ----------- alembic 1.8.1 anyio 3.6.2 argon2-cffi 21.3.0 argon2-cffi-bindings 21.2.0 asttokens 2.2.1 async-generator 1.10 attrs 22.1.0 backcall 0.2.0 beautifulsoup4 4.11.1 bleach 5.0.1 certifi 2022.9.24 certipy 0.1.3 cffi 1.15.1 charset-normalizer 2.1.1 cryptography 38.0.4 debugpy 1.6.4 decorator 5.1.1 defusedxml 0.7.1 entrypoints 0.4 executing 1.2.0 fastjsonschema 2.16.2 greenlet 2.0.1 idna 3.4 importlib-metadata 5.1.0 importlib-resources 5.10.0 ipykernel 6.17.1 ipython 8.7.0 ipython-genutils 0.2.0 jedi 0.18.2 Jinja2 3.1.2 jsonschema 4.17.3 jupyter_client 7.4.8 jupyter_core 5.1.0 jupyter-server 1.23.3 jupyter-telemetry 0.1.0 jupyterhub 1.5.1 jupyterlab-pygments 0.2.2 Mako 1.2.4 MarkupSafe 2.1.1 matplotlib-inline 0.1.6 mistune 2.0.4 nbclassic 0.4.8 nbclient 0.7.2 nbconvert 7.2.6 nbformat 5.7.0 nest-asyncio 1.5.6 notebook 6.5.2 notebook_shim 0.2.2 oauthlib 3.2.2 packaging 21.3 pamela 1.0.0 pandocfilters 1.5.0 parso 0.8.3 pexpect 4.8.0 pickleshare 0.7.5 pip 22.3.1 pkgutil_resolve_name 1.3.10 platformdirs 2.5.4 prometheus-client 0.15.0 prompt-toolkit 3.0.33 psutil 5.9.4 ptyprocess 0.7.0 pure-eval 0.2.2 pycparser 2.21 pycurl 7.43.0.2 Pygments 2.13.0 pyOpenSSL 22.1.0 pyparsing 3.0.9 pyrsistent 0.19.2 python-dateutil 2.8.2 python-json-logger 2.0.4 pyzmq 24.0.1 requests 2.28.1 ruamel.yaml 0.17.21 ruamel.yaml.clib 0.2.7 Send2Trash 1.8.0 setuptools 65.6.3 six 1.16.0 sniffio 1.3.0 soupsieve 2.3.2.post1 SQLAlchemy 1.4.44 stack-data 0.6.2 terminado 0.17.1 tinycss2 1.2.1 tornado 6.2 traitlets 5.6.0 urllib3 1.26.13 wcwidth 0.2.5 webencodings 0.5.1 websocket-client 1.4.2 wheel 0.34.2 zipp 3.11.0 [notice] A new release of pip available: 22.3.1 -> 24.0 [notice] To update, run: python3 -m pip install --upgrade pip
提示:如果你仅查询某指定模块版本,可使用如下命令,以notebook为例
demo@e5666f444a6b:/srv/jupyterhub$ pip3 list | grep notebook notebook 6.5.2 notebook_shim 0.2.2 [notice] A new release of pip available: 22.3.1 -> 24.0 [notice] To update, run: python3 -m pip install --upgrade pip
附录二:自定义JupyterHub配置
jupyterhub镜像仅包含Hub本身,不包含配置,详见文档语句:
This
quay.io/jupyterhub/jupyterhub
image contains only the Hub itself, with no configuration
自定义配置可通过如下方式:
-
创建自定义配置,存放位置根据个人喜好,这里使用
/etc/jupyterhub/jupyterhub_config.py
。在例子中,我们配置登录时可使用任意用户名密码# Configuration file for jupyterhub-demo c = get_config() # Use DummyAuthenticator and SimpleSpawner c.JupyterHub.spawner_class = "simple" c.JupyterHub.authenticator_class = "dummy"
提示:不同版本的
jupyterhub
配置文件格式可能不同,具体格式可按照 附录一:查询缺失的模块版本 类似的方式查找,jupyterhub-demo
通常会把配置文件放在/srv/jupyterhub
路径下 -
挂载配置文件,并更改docker容器启动参数
docker run -p 8000:8000 -d --name jupyterhub \ -v /etc/jupyterhub/jupyterhub_config.py:/etc/jupyterhub/jupyterhub_config.py \ jupyterhub/jupyterhub:1.5.1 jupyterhub -f=/etc/jupyterhub/jupyterhub_config.py
等待容器运行后,浏览器访问http://192.168.10.60:8000,便可发现任意用户名密码均可登录系统。