搭建 ipython/jupyter notebook 服务器

Jupyter notebook(原 ipython notebook)是统计、数据分析和机器学习的利器。一般情况下,jupyter notebook 网页应用运行在 http://localhost:8888,用户可以在本地计算机通过浏览器访问和使用。但在某些时候,我们想把 jupyter notebook 应用架设在公开的服务器中,以便获取更强的计算能力和更多的内存,同时省去在每台电脑上安装和配置 jupyter notebook 的过程。

本文以 debian/ubuntu 系统为例,讲解如何安全地搭建 jupyter notebook 服务器,以允许授权用户使用任意浏览器在任意设备上访问这款应用。


创建 Linux 用户

为 notebook 应用创建一个单独的 Linux 用户不是必须的,但使用独立用户有两点好处:

  1. 在隔离的环境下操作 notebook 应用
  2. 避免(恶意)访问者使用 sudo 提升权限,进而控制整个系统

创建一个新用户的方法很简单

1
2
# 请把 ipynb 替换成你想要的用户名,以下相同
sudo adduser ipynb

系统会要求你为 ipynb 提供一个密码。你可以填写一个很简单的密码。不用担心,我们有办法确保该用户的安全。创建成功后,/home/ipynb 即为用户的家目录。

一般来说,新创建的用户不会自动取得 .ssh 文件夹。如有必要,我们可以用 sudo rm -rf /home/ipynb/.ssh 移除它。此举会封禁通过 ssh 证书方式登录 ipynb 用户的渠道。

下一步,再禁止以密码方式登陆该用户

1
sudo passwd -l ipynb

这样,入侵者即便知道服务器地址、用户名和密码,也无法登录。想要登录这个用户,只能使用 sudo su ipynb。这个指令会在后面的配置中用到。


安装和启动 notebook 应用

如果你的服务器上已经安装了 jupyter notebook,请移步至下一节。

现代的 Linux 服务器一般会预装 python 和 python3 两个软件包,我们来验证一下

1
2
3
4
5
python --version
# 输出 Python 2.7.12

python3 --version
# 输出 Python 3.5.2

但是我们手头上可能没有 Python 的包管理器 pip。如果你发现 which pip 的输出为空,就需要安装它。Python 2 和 Python 3 分别有自己版本的包管理器

1
2
3
4
5
# Python 2 的包管理器
sudo apt-get install python-pip

# Python 3 的包管理器
sudo apt-get install python3-pip

随后就可以用 pip 安装 jupyter notebook 了

1
2
3
4
5
# Python 2 的 notebook
sudo pip install ipython[all]

# Python 3 的 notebook
sudo pip3 install ipython[all]

最后,尝试在服务器本地启动 jupyter notebook 应用

1
jupyter notebook --no-browser

如果终端看到下面的提示,就说明 notebook 启动成功了。



由于众所周知的兼容性问题,Python 以及 Python 包的版本相当混乱。如果你已经有使用 virtualenv 的经验,强烈建议在虚拟环境中安装所需版本的 jupyter notebook,以免污染全局环境,影响其他的 Python 应用。


用密码保护 notebook

下面,我们将为 jupyter notebook 加上密码保护,这样只有授权用户才能够访问和使用它。

存储明文密码显然不是一个安全的做法。我们需要提供一个 hash 过的密码。登录时,notebook 应用获取用户提交的密码,进行 hash 计算,再与预先存储的值比对。如果两者相同,则认为用户提交的密码正确,予以放行。获取 hash 密码的方式如下图所示。



不过,仅仅为 notebook 设定密码并不能保障安全。如果密码在网络中以明文传输,就为中间人提供了截获的机会。稍有网络安全常识的人都知道,密码等敏感信息必须通过加密方式传输。因此,我们还需要提供 HTTPS 连接。

如果你还没有 SSL 证书,请先移步至《Let's Encrypt 入门教程》。如果服务器上已有 CA 认证的 SSL 证书,则需要把证书文件 cert.pem 和私钥文件 privkey.pem 复制到 ipynb 的家目录下,并赋予合适的属主和权限。这是因为通常只有 root 用户才有权读取这两个文件,但出于安全的考虑,我们的 notebook 应用不能由 root 运行,所以这里需要额外为 ipynb 添加例外。


配置 notebook 应用

最后一步,是更改 jupyter notebook 的配置,使其对外部访问者可见。这一步既可以使用 notebook config 文件,也可以通过添加 ipython profile 实现。虽然 ipython profile 不是官方推荐的方式,但由于可以存在多个 profile,有助于灵活地切换 notebook 的设置。本节的指令需要以 ipynb 用户的身份执行。

倘若使用 notebook config 文件,首先需要生成一个模板

1
2
# 生成 /home/ipynb/.jupyter/jupyter_notebook_config.py 文件
jupyter notebook --generate-config

这个模板文件很长,但所有的内容都被注释掉了。请跳转到文件的末尾,添加如下条目,并按你的实际情况填写数值。

1
2
3
4
5
6
7
8
9
#------------------------------------------------------------------------------
# User configuration
#------------------------------------------------------------------------------
c.NotebookApp.certfile = u'/home/ipynb/cert.pem' # 证书文件
c.NotebookApp.keyfile = u'/home/ipynb/privkey.pem' # 私钥文件
c.NotebookApp.ip = '*' # 允许从任意 IP 访问
c.NotebookApp.open_browser = False # 运行 notebook 应用时不打开浏览器
c.NotebookApp.password = u'sha1:e9e608cacbb2:da88........2cf0c8c26877b7179100d8fd545a' # 密码的 hash 值
c.NotebookApp.port = 28888 # 运行端口

保存文件后,启动 notebook 应用

1
2
3
4
5
# 启动 notebook 服务
nohup jupyter notebook &

# 或者更加高级的用法
nohup jupyter notebook > /dev/null 2>&1 &

为 Linux 的小白们多费两句口舌,nohup 确保这个进程在断开 ssh 连接后依旧运行,& 让进程在后台运行。如果想终止 notebook 应用,请找到含有 jupyter-notebook 的进程,并用 kill 杀掉它。

如果你的防火墙规则设置正确,现在就可以在公共网络用浏览器访问它了。在开启 SSL 的情况下,需要使用以 https:// 开头的地址。输入密码登录后,就会看到熟悉的界面,与在本地计算机上使用并无差异。



如果使用 ipython profile,流程没有太大的区别。第一步依旧是生成模板

1
2
3
4
5
# 创建一个名为 public 的 profile
# 会自动生成两个文件
# /home/ipynb/.ipython/profile_public/ipython_config.py
# /home/ipynb/.ipython/profile_public/ipython_kernel_config.py
ipython profile create public

第二步,与 jupyter_notebook_config.py 一样,请按相同的格式修改 ipython_config.py 文件。

最后,在启动时,告知 notebook 需要激活的 config 文件

1
2
3
4
5
# 启动 notebook 服务
nohup jupyter notebook --config=/home/ipynb/.ipython/profile_public/ipython_config.py &

# 或者更加高级的用法
nohup jupyter notebook --config=/home/ipynb/.ipython/profile_public/ipython_config.py > /dev/null 2>&1 &

等哪天想要调整 notebook 的设置,可以再创建一个新的 profile,这样就不会丢失原先的设置。

本文结束之前,再给大伙儿提个醒。notebook 的根目录是由运行 jupyter notebook 指令的工作目录决定的。把整个家目录暴露出去不是一个好想法,使用时请多加注意。