VPS折腾记(四):搭建JupyterLab服务器

折腾了一下午,终于成功安装好了python笔记神器JupyterLab,仍然使用docker部署,记录一下,免得以后忘了。

JupyterLab是什么

Jupyter notebook相信很多人都听说——一个长得很像Mathematica的笔记本形式的交互式IDE,基于web,主要是支持python,特别适合做科学数据分析、代码演示,后来支持越来越多的编程语言,如RJulia等解释型编程语言,甚至可以支持GoC等编译型语言,可谓是Jupyter在手,天下我有。

一直以来,Jupyter都以notebook形式存在,虽然够用了,但是总还是差点感觉。因此,Jupyter推出下一代Jupyter notebook,即JupyterLab,支持对单个cell进行操作、更多插件等,总之,可以理解为Jupyter Notebook的改进版。

选择docker镜像

我对JupyterLab的需求主要有两个:Python和R,因为自己平时用Python比较多,但是工作要求用R,所以需要支持两种语言。

JupyterLab可供选择的docker镜像不多,不过在Jupyter docker上第三个例子(jupyter/datascience-notebook)就满足我对PythonR的需求,甚至还支持Julia,有空的时候还可以学学,因为选择了这个镜像进行安装。

登录VPS,新建个文件夹,里面建个docker-compose.yml,根据Example 3配置,同时把数据挂载到系统盘上,方便以后直接使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
root@VPS:~# mkdir lab
root@VPS:~# cd lab
root@VPS:~# vim docker-compose.yml
root@VPS:~# cat docker-compose.yml
version: '2.0'
services:
        jupyterlab:
                image: b
                container_name: jupyterlab
                restart: always
                environment:
                        - JUPYTER_ENABLE_LAB = yes
                        - TIME_ZONE = Asia/Shanghai
                volumes:
                        - /opt/jupyter/work:/home/jovyan/work
                        - /opt/jupyter/jupyter:/home/jovyan/.jupyter
                ports:
                        - "127.0.0.1:8888:8888"

需要注意的是,我把/home/jovyan/work/home/jovyan/.jupyter挂载到系统盘/opt/jupyter中,方便以后保存文件和修改配置文档。

另外,端口映射时指定的是”127.0.0.1:8888:8888”而不是”8888:8888”,主要是防止其他ip地址去访问8888端口,而改用nginx去做映射,更加安全

运行docker-compose up -d,此时已经可以配置nginx然后去访问JupyterLab了,但是需要输入密码,这个密码需要在docker运行log时找,很长(sha1),比较麻烦,我们可以自己设置喜欢的密码,并配置到系统中,方便之后访问。

先进入docker容器中生成配置文件。

1
2
3
4
docker exec -it --user root jupyterlab bash # 进入容器
jupyter lab --generate-config # 生成jupyter配置文档
chmod 777 .jupyter/*      # 生成配置文档后,至少要把同时生成的migrated文件chmod 777,否则会出错,原因未知。
exit

由于docker容器中没有vim,我也不想在里面安装vim,所以退出容器后,在挂载在/opt/jupyter/jupyter里去修改刚生成的配置文档,需要改动两个地方:

1
2
c.NotebookApp.password = u'sha1:b92f3fb7d848:a5d40ab2e26aa3b296ae1faa17aa34d3df351704'
c.NotebookApp.default_url = '/lab'

注意,c.NotebookApp.password的字符串不能跟我这里的一样,应该是你根据自己的密码生成的Hash密码:

1
2
3
ipython
from notebook.auto import passwd
passwd() # 输入两次想设置的密码,会出现sha1字符串,即为Hash密码

最后,JupyterLab可以进入docker容器后升级:

1
2
docker exec -it --user root jupyterlab bash
pip install -U jupyterlab

Ningx配置

最后,还需要配置一下nginx实现域名访问,跟seafile的nginx配置差不多,不再多讲,下面是我的nginx配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
server {
        listen 80;
        server_name lab.callmsn.top;
        rewrite ^ https://$http_host$request_uri? permanent;
}

server {
        listen 443;
        server_name lab.callmsn.top;
        ssl on;
        ssl_certificate /etc/nginx/ssl/lab/lab.pem;
        ssl_certificate_key     /etc/nginx/ssl/lab/lab.key;

        location / {
                proxy_pass http://127.0.0.1:8888;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Host $server_name;
                proxy_set_header X-Forwarded-Proto https;

                # WebSocket support
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";

                access_log /var/log/nginx/lab.access.log;
                error_log /var/log/nginx/lab.error.log;
        }
}

注意: JupyterLab使用websocket进行通信,所以这几行非常关键:

1
2
3
4
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";

如果不配置这几行,则会一直显示Kernel Reconnecting

安装插件

JupyterLab支持安装插件,现在插件数量还不算多,但是基本上我需要的都已经有了,下面是我安装的插件:

@jupyter-widgets/jupyterlab-manager # 镜像自带
jupyterlab_bokeh # 镜像自带,bokeh绘图库
@jupyterlab/toc # 目录
jupyterlab-drawio # drawio画流程图
@krassowski/jupyterlab_go_to_definition # 自动跳转到变量/函数定义
@lckr/jupyterlab_variableinspector # 像Rstudio一样以表格形式查看变量
@ryantam626/jupyterlab_code_formatter # 代码自动格式化

安装方法很简单,浏览器访问jupyterlab,点击菜单Settings→Enable Extension Management(Experimental),这时左边侧边栏会出现一个插件的图标,点击插件图标,会列出已安装的插件列表、插件搜索结果,搜索插件并点击Install,过一会儿就安装好了。

安装完插件会提示A build is needed to include the latest changes,如果还想继续安装插件就点Ignore或者不理它;如果插件都安装好了,就点Rebuild完成更新。

JupyterLab无法Build

上一步中,在点了Rebuild后出错了,进行docker,查看LOG,发现并没有什么异常,这时候就要考虑是不是内存不够了

1
cat /opt/conda/share/jupyter/lab/staging/package.json

package.json里有jupyterlab build的配置参数,发现有三行配置是启用了4G的内存。

1
2
3
"build:prod": "cross-env NODE_OPTIONS=--max_old_space_size=4096 webpack --config webpack.prod.config.js",
"build:prod:minimize": "cross-env NODE_OPTIONS=--max_old_space_size=4096 webpack --config webpack.prod.minimize.config.js",
"build:prod:stats": "cross-env NODE_OPTIONS=--max_old_space_size=4096 webpack --profile --config webpack.prod.minimize.config.js --json > stats.json"

这对我这个1G内存的VPS来说真是强人所难了,因此需要把--max_old_space_size=4096改成--max_old_space_size=2048

1
2
3
"build:prod": "cross-env NODE_OPTIONS=--max_old_space_size=2048 webpack --config webpack.prod.config.js",
"build:prod:minimize": "cross-env NODE_OPTIONS=--max_old_space_size=2048 webpack --config webpack.prod.minimize.config.js",
"build:prod:stats": "cross-env NODE_OPTIONS=--max_old_space_size=2048 webpack --profile --config webpack.prod.minimize.config.js --json > stats.json"

再点Rebuild或者直接在docker容器里运行jupyter lab build就行了。

docker commit

前面我们对docker安装运行的JupyterLab做了很多修改,如更新了jupyterlab内核安装了一些插件修改了package.json等,这时候如果运行docker-compose up -d则会重新创建容器,所做的一切修改都会恢复原状,因此需要把这些修改都固化下来。

docker提供了commit指令,可以把修改后的容器保存成新的镜像。

1
docker commit -a "Alston <alston2020@outlook.com>" -m "更新了jupyterlab、安装了一些插件" XXXXXXXXX alston/jupyterlab

-a后接作者,-m后接注释备注,XXXXXXXX可以从docker ps中找到对应的容器的CONTAINER_ID中找,alston/jupyterlab可以改成自己想要的名字。

然后编辑docker-compose.yml,把image: jupyter/datascience-notebook改成image: alston/jupyterlab或对应镜像名字,再docker-compose up -d就行了。

注:登录JupyterLab发现无法显示安装的extension列表了,查看log发现有一句是

1
ValueError: "['@jupyter-widgets/jupyterlab-manager@1.0.2', 'jupyterlab_bokeh@1.0.0', '@jupyterlab/toc@1.0.1', 'jupyterlab-drawio@0.6.0', '@krassowski/jupyterlab_go_to_definition@0.7.1', '@lckr/jupyterlab_variableinspector@0.3.0', '@ryantam626/jupyterlab_code_formatter@0.5.0']" is not a valid npm package

原因可能是因为docker运行的用户没有权限去读写.npm文件,以root用户进入docker,然后给.npm读写权限就行了1

1
2
docker exec -it --user root jupyterlab bash
chmod 777 -R .npm