集群上构建外网可访问的jupyter notebook
预备知识
由PBS系统所管理的集群,一般由一个管理节点mu01和多个计算节点gpu01,gpu02,gpu02等构成。
通常用户在使用集群进行计算时,一般有两种方案:
- 利用
qsub提交.sh脚本作为任务,调度任务在某个计算节点运行
执行qsub run.sh
run.sh
1 | !/bin/bash |
其中,以#PBS开头的注释行,将会被qsub读取后作为PBS启动参数,影响到创建的工作空间
- 利用
qsub启动交互式终端,直接进入计算节点进行操作
1 | qsub -I -N Notebook -W x=GRES:gpu@1 -l nodes=gpu01:ppn=1,walltime=8:00:00 -q gpuq |
其中,-I指定qsub启动交互式终端而不是提交任务
这两种方法的PBS指令基本相同,从前到后可以依次理解为
graph LR; A[将任务命名为Notebook] --> B[申请1个GPU环境]; B[申请1个GPU环境] --> C[限制使用gpu01计算节点]; C[限制使用gpu01计算节点] --> D[限制最大时限为8小时]; D[限制最大时限为8小时] --> E[任务提交给gpuq队列]; E[任务提交给gpuq调度器] -->|1| F[执行脚本]; E[任务提交给gpuq调度器] -->|2| G[启动交互式终端];
工作环境
通常情况下,管理节点mu01有外网权限,但没有可用的计算资源,计算节点gpu01有计算资源,但是没有外网权限。
尽管管理节点和计算节点共享相同的计算环境如conda,因为jupyter涉及到从外网访问计算资源,单纯在两者上开启jupyter都不可行。如果在管理节点上直接开启jupyter,conda环境将无法访问到显卡。而如果在计算节点上直接开启jupyter,conda环境将只能从计算节点被访问。
解决方案
显然,唯一可行的方法就是在计算节点上启动jupyter,然后从管理节点进行访问,通过端口转发,保证外网对于管理节点的http请求,通过管理节点被转发给计算节点上的jupyter服务。
我们利用
ssh -N -f -R $ADMINPORT:localhost:$NOTEBOOKPORT $PBS_O_HOST
创建一条从计算节点到管理节点的反向隧道
其中参数解释如下
| 参数 | 说明 | 备注 | 需要自定义 |
|---|---|---|---|
-N |
不启动终端shell |
只创建隧道 | |
-f |
ssh启动后进入后台 | ||
-R |
隧道为反向隧道 | 和-L相对 |
|
$ADMINPORT |
远程端口 | 实际是管理节点的端口 | 是 |
localhost |
本地地址 | 实际是计算节点的地址 | |
$NOTEBOOKPORT |
本地jupyter服务端口 |
实际是计算节点的端口 | 是 |
$PBS_O_HOST |
远程地址 | 实际是管理节点的地址 |
在此之后,我们确保管理节点上向$ADMINPORT发出的请求,可以转发到计算节点上的$NOTEBOOKPORT,在计算节点上启动jupyter之后,利用wget http://localhost:$ADMINPORT进行测试(这里的$ADMINPORT填入实际值)。
只要结果不是failed: Connection refused,除某些特殊情况(见备注)以外,一般可以认为端口转发成功。
工作流
构建如下脚本,根据实际情况酌情修改
run_jupyter.sh
1 | #!/bin/bash |
根据实际情况,修改LOCALPORT,NOTEBOOKPORT,PYTHONPATH,NOTEBOOK_LOGFILE等参数后,利用qsub run_jupyter.sh提交任务
备注
- 为什么不用从管理节点到计算节点的正向隧道?
并非绝对不行,但一方面,建立正向隧道需要在管理节点额外执行脚本,需要更多工作量。另一方面,从管理节点访问计算节点,并无如$PBS_O_HOST这样简单的变量可以指向目标地址。因此,整体工作量会复杂很多。
- 某些特殊情况是指什么?
比如说,机器上还有其他服务开启了某些端口。解决方案,首先设置端口时,就应当尽量避开常见的服务所在端口,避免冲突。如果怀疑端口被其他服务占用,可以关闭jupyter后再次检查端口是否开启.
- 我记不住ip地址的话,能不能不用服务器ip访问
jupyter服务?
可以,只要在本地机器上,用类似的方案开一条正向隧道,将本地端口port指向管理节点的服务端口,然后即可用https://127.0.0.1:port或https://localhost:port访问到`jupyter`服务。