“他山之石,可以攻玉”,站在巨人的肩膀才能看得更高,走得更远。在科研的道路上,更需借助东风才能更快前行,让我们一起乘风破浪,奋勇向前。
本文来源:https://zhuanlan.zhihu.com/p/442046879
作者:捡到一束光
更多干货,请关注公众号(DataPlanet)
笔者常常要对实验室的深度学习服务器进行管理与维护,本文是对服务器管理过程中公网对内网访问的典型需求的解决方案。深度学习服务器主要由若干CPU(通常是2块CPU,共48-64进程)与若干GPU(2-8块GPU,通常为1080Ti,2080Ti,3090,TITAN xp,Tesla V100等)组成,可以用于深度学习模型的训练。这些服务器往往放置于内网中,在外网无法直接ssh,往往需要通过麻烦的VPN验证进入内网方能进行访问。笔者还遇到的特殊问题是,管理的四台服务器位于不同的内网,换机登陆极其麻烦。此外,深度学习任务中还需要用到很多基于web服务的工具,如jupyter notebook,tensorboard等工具,这些web服务的配置也需要一些技巧。在本文中,我采用开源内网穿透工具frp对上述问题进行解决,从而实现内网服务器的公网ssh操作与公网访问。
01 用Frp实现内网穿透
运行服务器的典型场景是这样的:在内网A中有一台服务器,它只有内网ip而没有公网ip,因此在内网外无法访问。但是该服务器又可以访问公网资源,能够进行上传与下载行为。我们想在公网直接ssh到内网服务器上,并使用服务器的一些web服务。Frp的思路是构造一台具有公网ip的服务器,然后将内网服务器与这台我们能够访问的,具有公网ip的服务器进行“捆绑操作”,我们向公网服务器发送的请求全部变成数据包传给内网服务器,内网服务器再返回给公网服务器,并交由公网服务器展示给我们。基于这种思路,我们可以购买阿里云服务器作为中转,采用frp作为转发工具,从而实现内网穿透。
在云服务器方面,我购买的是阿里云学生优惠下的轻量应用服务器,每个月限1000G流量,一年113,峰值带宽5Mbps,方便下载与上传。frp的使用很简单,我们首先下载它最重要的两个部分,分别是frps(frp-server)与frpc(frp-client)。其中,frps控制公网服务器上接收内网服务器流量的端口,以及公网服务器打开内网web服务的端口,而frpc则控制内网服务器发送流量的ip地址,发送流量的端口,以及自己需要转发的服务在远程服务器上的对应端口. 一个例子如下:
我们拥有公网服务器G,其ip为iG,系统为ubuntu。我们拥有内网服务器C,能够藉由公网IPiG访问公网服务器。我们用frp建立内网穿透,希望通过公网服务器的某个端口xyz就能进入内网服务器,即通过ssh -p xyz usr@iG 进入内网服务器。在公网服务器中,我们放入程序frps,并创建一个名字为frps.ini的空配置文件。在内网服务器中,我们同样放入程序frpc,并创建名字为frpc.ini的空配置文件,然后我们分别配置两台服务器:
在公网服务器中,我们在配置文件frps.ini中写入
[common]
bind_port = 7000
这里7000是一个让内网服务器与公网服务器进行流量交换的端口,服从tcp文件传输协议,理论上可以取任意服务器允许的端口。然后我们在公网服务器上用下面的代码运行frps服务:
frps -c frps.ini
这表示公网服务器开了一个端口7000,允许内网服务器进行流量上传与下载。接下来,我们让内网服务器通过这个端口,转发ssh服务。在内网服务器中,我们在配置文件frpc.ini中写入
[common]
server_addr = iG
server_port = 7000
[ssh-C]
type = tcp
local_ip =127.0.0.1
local_port = 22
remote_port = xyz
这里server_addr是公网服务器的公网ip,server_port就是公网服务器开启的捆绑端口bind_port。与公网服务器用该端口进行捆绑后,我们命名一个叫ssh-C的ssh服务,转发ssh服务到公网服务器的xyz端口。因为ssh服务本质上是一个文本传输服务tcp,因此我们给ssh的传输类型也是tcp。然后我们在内网服务器上用下面的代码运行frpc服务:
frpc -c frpc.ini
然后,我们通过访问公网服务器xyz端口,就能访问到内网服务器,示例如下
ssh -p xyz username@iG
注意,以上方法可以设置一台公网服务器对多台内网服务器的绑定,只需要对不同的服务器设置不同的xyz端口,并给出不同的服务命名(即ssh-C)即可。
02 服务器在线编程——Jupyter Notebook
在自己电脑上写程序的时候,jupyter notebook是很好的辅助工具。我们希望在服务器上开一个类似的web服务,使其可以在自己的浏览器中访问,从而实现远程调试,在线编程。jupyter notebook是一种可以调用服务器的python解释器后端处理网页上输入的工具,我们要让它可以随处访问,需要一些额外配置,包括配置访问ip,访问密码,文件存储目录以及本机http服务端口,配置好以后,我们就可以开始在线编程了。
首先,我们先在本地服务器上配置jupyter notebook,先采用Anaconda进行安装:
conda install jupyterlab
安装好以后,我们在命令行输入
jupyter notebook --generate-config
就会在当前用户下生成一个名为.jupyter的文件夹,里面有配置文件jupyter_notebook_config.py。我们通过修改它即可进行jupyter notebook的服务器端配置。首先我们对ip与port进行配置,即打开jupyter notebook服务后,可以通过哪个内网地址访问。我们的建议是将ip设置为本机的内网ip,port随意设置为支持http服务的端口,下面是一个例子:
c.NotebookApp.ip = "内网ip"
c.NotebookApp.port = "端口"
然后,我们设置登陆该jupyter notebook所需要的密码。配置文件通过256位SHA密钥生成,我们把密码转换为密钥方能填写。在服务器上打开python,运行
from notebook.auth import passwd
passwd()
则会要求你输入密码,输入密码后将返回密码对应的密钥。如输入123456则会返回
'sha1:778d23c18ddb:d9afd76551b9aebc33ee628b792460aa7763b870'
我们将其如下粘贴到配置文件中
c.NotebookApp.password_required = True
c.NotebookApp.password = 'sha1:778d23c18ddb:d9afd76551b9aebc33ee628b792460aa7763b870'
最后,我们设置文件存储目录
c.NotebookApp.notebook_dir = "/home/user/mydir"
这样所有在jupyter notebook中的文件与notebook都会存储于/home/user/mydir这个文件夹下,文件夹需要提前创建,不然会报错。注意到服务器是没有浏览器的,因此我们还需设置浏览器默认选项为False
c.NotebookApp.open_browser = False
然后,我们在命令行运行
jupyter notebook
就可以在内网中通过http:内网ip:端口访问到服务器开启的jupyter notebook服务了。
如上文所述,我们可以用服务器在内网开启好用的在线编程服务。那么,内网的web服务如何通过公网访问呢?如果我们已经如第一章所述设置了对应的ssh, 那么可以用反向端口映射来访问。以jupyter notebook为例,如果我们已经设置了公网的ssh穿透,那么我们需要访问内网的web服务,只需要在自己机器的命令行中输入
ssh -p xyz -NfL localhost:本地端口:内网ip:jupyter端口 username@iG
就可以把内网的web服务端口转发到本机,然后通过访问http:localhost:端口就可以访问服务器的web服务。但是这种操作较为繁琐,需要多次重复输入。更优雅的解决方案是通过frp服务,联合二级域名来进行访问。我们用frp将内网的web服务端口转发到公网服务器的端口,通过访问公网服务器的某端口即可访问到内网。但是,往往公网服务器允许的http服务端口只有1个,因此,我们可以通过二级域名的方式转发内网服务器的多个web服务,而这就需要用到一个域名。
域名注册
域名注册可以用腾讯云或阿里云服务器注册,在上面购买一个便宜的域名即可,国内购买的域名需要做实名认证,并等待10-12小时,域名就能被DNS服务器所记录,这样我们就可以用这个域名来进行定向了。腾讯云与阿里云都提供域名解析服务,我们点击解析,将域名与我们的服务器进行绑定,一个示例如下
主机记录:* 记录类型:A 记录值:公网ip
添加这个解析条目后,我们ping任何前缀.域名都会直接ping到我们的公网ip上,这样就把域名和公网ip进行了绑定。我们利用该域名,对不同的web服务分配不同的前缀,然后在公网服务器上开一个支持http协议的端口,就能实现内网web服务的公网访问。假设我们在公网上开设的端口为8080,那么我们就可以进行如下配置,以jupyter notebook为例:
Frp配置
首先,我们在服务器端的frps.ini文件中新增http端口与对应的域名如下:
[common]
bind_port = 7000
vhost_http_port = 8080
subdomain_host = web.域名
这样我们告诉了frps可以使用8080端口来进行web访问,然后我们将内网的所有web服务将转发到公网服务器的8080端口,并用一个前缀域名进行定位(这里是web.域名)。以转发上文开启的jupyter notebook服务为例,我们对内网服务器进行如下配置“在frpc.ini文件中新增基于http协议的条目为
[common]
server_addr = iG
server_port = 7000
[ssh-C]
type = tcp
local_ip =127.0.0.1
local_port = 22
remote_port = xyz
[jupyterlab-C]
type = http
local_ip = 内网ip
local_port = 内网port
subdomain = jupyter
然后在公网和内网分别重新开启frps和frpc服务
frps -c frps.ini
frpc -c frpc.ini
我们就可以通过在浏览器访问http:jupyter.web.域名:8080转到内网服务器的web服务中,一切与在内网中访问http:内网ip:内网port完全一致,同样可以设置tensorboard。
Notes:解决麻烦的备案问题与安全问题
注意我们的服务器和域名都是在大陆购买的,因此在公网开web服务其实是需要备案的。实名很简单,但是备案需要填写很多网站资料,还要贴网站的审核号,流程有点繁琐。一个比较简单的方法是,把域名都改成域名.,这里不再细说。
将内网暴露在公网会增加安全风险,出于安全性我们给出三点建议:
服务器采用白名单模式进行登陆
jupyter notebook的密码设计得复杂一些
tensorboard用完就关闭
本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。