本地深度学习服务器远程访问——FRP内网穿透(在window深度学习服务器)
本地深度学习服务器远程访问——FRP内网穿透(在window深度学习服务器)
本人使用的配置为:
客户端:联想Y7000笔记本,主要用于调试深度学习代码以及测试深度学习目标检测训练后模型训练结果。
内网服务器:戴尔深度学习服务器,系统为win10,CPU为I7-10700,GPU为RTX3060,主要用于训练深度学习模型。
外网服务器:一台具有公网IP的服务器,系统为Linux RED HAT ,主要用于中转客户端与内网服务器的之间命令传输,实现跨网段的深度学习服务调用。
其基本原理如下:
我们拥有内网服务器C
,能够藉由公网IPiG
访问公网服务器。我们用frp
建立内网穿透,希望通过公网服务器的某个端口xyz
就能进入内网服务器,即通过ssh -p xyz usr@iG
进入内网服务器。在公网服务器中,我们放入程序frps
,并创建一个名字为frps.ini
的空配置文件。在内网服务器中,我们同样放入程序frpc
,并创建名字为frpc.ini
的空配置文件,然后我们分别配置两台服务器:
1,下载代理软件frp
github上下载相应的服务器端与客户程序https://github.com/fatedier/frp/releases
图中两个红框分别对应了linux64位与window64位程序。
2,公网服务器设置:
首先用sftp协议或其他协议将frp_0.41.0_linux_amd64.tar.gz上传到公网服务器的根目录下的/opt下,并解压
cd /opt
tar -zxvf frp_0.41.0_linux_amd64.tar.gz
cd frp_0.41.0_linux_amd64
其中有几个注意的文件:
- frps
- frps.ini
- frpc
- frpc.ini
前两个文件(s结尾代表server)分别是服务端程序和服务端配置文件,后两个文件(c结尾代表client)分别是客户端程序和客户端配置文件。此时,我们在配置公网服务器,因此执行下面的指令:
vi frps.ini
在公网服务器中,我们在配置文件frps.ini
中写入
[common]
bind_port = 7000
dashboard_port = 7500
token = abcoqie!@#32
dashboard_user = admin
dashboard_pwd = admin123456
vhost_http_port = 9085
vhost_https_port = 11080
这里7000
是一个让内网服务器与公网服务器进行流量交换的端口,服从tcp文件传输协议,理论上可以取任意服务器允许的端口。
“bind_port”表示用于客户端和服务端连接的端口,这个端口号我们之后在配置客户端的时候要用到。
“dashboard_port”是服务端仪表板的端口,若使用7500端口,在配置完成服务启动后可以通过浏览器访问 x.x.x.x:7500 (其中x.x.x.x为公网服务器的IP)查看frp服务运行信息。
“token”是用于客户端和服务端连接的口令,请自行设置并记录,稍后会用到。
“dashboard_user”和“dashboard_pwd”表示打开仪表板页面登录的用户名和密码,自行设置即可。
“vhost_http_port”和“vhost_https_port”用于反向代理HTTP主机时使用,本文不涉及HTTP协议,因而照抄或者删除这两条均可。
然后我们在公网服务器上用下面的代码运行frps
服务:
./frps -c frps.ini
这表示公网服务器开了一个端口7000,允许内网服务器进行流量上传与下载。此时访问 x.x.x.x:7500 并使用自己设置的用户名密码登录,即可看到仪表板界面:
至此,我们的服务端仅运行在前台,如果Ctrl+C停止或者关闭SSH窗口后,frps均会停止运行,因而我们使用 nohup命令将其运行在后台。
修改外网服务器的全局环境变量,输入vi /etc/profile
后,在文件下写下
export PATH=$PATH:/opt/frp_0.41.0_linux_amd64
之后生效环境变量,输入source /etc/profile
,接着再输入
nohup frps -c /opt/frp_0.41.0_linux_amd64/frps.ini &
输出如下内容即表示正常运行,并且进程号为23843
[1] 23843
[root@localhost frp_0.41.0_linux_amd64]# nohup: ignoring input and appending output to `nohup.out'
此时可先使用Ctrl+C关闭nohup,frps依然会在后台运行,使用jobs命令查看后台运行的程序
jobs
在结果中我们可以看到frps正在后台正常运行
[1]+ Running nohup /opt/frp_0.41.0_linux_amd64/frps -c frps.ini &
若想结束相关进程,执行以下指令:
kill -9 23843 #kill -9 端口号
此时访问 x.x.x.x:7500 依然可以打开仪表板界面,至此,服务端即设置完成,你可以关闭SSH窗口了。
3,内网服务器设置:
接下来,我们让内网服务器通过这个端口,转发ssh服务。在内网服务器中,我们在配置文件frpc.ini
中写入
[common]
server_addr = xxx.xxx.xxx.xxx
server_port = 7000
token = abcoqie!@#32
[SSH]
type = tcp
local_ip =127.0.0.1
local_port = 22
remote_port = 2486
- “server_addr”为服务端IP地址,填入即可。
- “server_port”为服务器端口,填入你设置的端口号即可,如果未改变就是7000
- “token”是你在服务器上设置的连接口令,原样填入即可。
与公网服务器用该端口进行捆绑后,我们命名一个叫SSH
的ssh服务,转发ssh
服务到公网服务器的2486端口。因为ssh
服务本质上是一个文本传输服务tcp
,因此我们给ssh
的传输类型也是tcp
。之后在win10内网服务器下运行frp的frpc.exe即可。若是linux,在frp解压的文件夹下执行以下命令即可:
./frpc -c frpc.ini
然后,我们通过访问公网服务器2486端口,就能访问到内网服务器,示例如下
ssh -p 2486 username@x.x.x.x
注意,以上方法可以设置一台公网服务器对多台内网服务器的绑定,只需要对不同的服务器设置不同的7001端口,并给出不同的服务命名(即ssh
)即可。username为客户端登录电脑的用户名,window10下可以通过控制面板下的用户帐户下更改帐户类型查看,x.x.x.x为公网服务IP地址。
注意,需要在window10上开启ssh服务器功能,才可以连接上,否则会出现以下错误,当然,如果内网服务器为linux,则配置ssh服务简单的多,可以自行百度。
[SSH] connect to local service [127.0.0.1:22] error: dial tcp 127.0.0.1:22: connectex: No connection could be made because the target machine actively refused it.
对于安装OPENSSH,需要在
设置 -> 管理可选功能 -> 添加功能 -> [OpenSSH 客户端] [OpenSSH 服务器] 启用
之后启动服务并设置自动启动:
计算机右键->管理->服务->OPENSSH SERVER&&OPENSSH Authentication
全部右键启动并设置为自动
开启免密登录
先在本地(客户端)生成公钥,当然,本地也是需要安装openssh的,在cmd下执行下面的命令。
ssh-keygen -t rsa
一路回车即可
切换到路径:C:\Users\your_userName\.ssh
,找到公钥文件id_rsa.pub。
复制公钥文件内容,到服务端C:\Users\your_userName\.ssh
下创建文件authorized_keys(没有文件后缀),粘贴公钥,保存退出。(即将客户机的公钥上传至深度学习服务器)
服务端切换到C:\ProgramData\ssh\下(首次启动sshd后会生成该文件夹),打开sshd_config文件,
修改文件(以下是重点):
修改文件(以下是重点):
找到 #StrictModes yes 改成 StrictModes no
确保以下3条没有被注释
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication yes #表示开启密码登录
这里注意,如果用私钥登录则注释掉前一句的PasswordAuthentication yes
确保以下2条有注释掉
#Match Group administrators
# AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
其余不做修改,基本都是已注释不启用。
重启服务器:在管理员终端下执行
net stop sshd
net start sshd
测试登录:
ssh -p 2486 username@x.x.x.x
回到客户端 ssh -p 2486 username@x.x.x.x,username为客户端登录电脑的用户名,window10下可以通过控制面板下的用户帐户下更改帐户类型查看,x.x.x.x为公网服务IP地址。此时无需密码直接登录说明正常(首次登陆可能会提示是否加入known_hosts,yes即可)。如果是多台win10主机互相免密,操作同上,不过需要每台主机的authorized_keys都需要包含其他主机公钥,即包含多条公钥记录。
注意,由于这是在我自己的内网服务器上测试是否可以ssh登录内网服务器,因此,ssh的公私钥都保存在内网服务器上,如果在第三方客户端连接内网服务器,需要新生成一个公私钥保存于内网服务器中,并将公钥上传至远程深度学习服务器,具体操作参考(MobaXterm设置ssh免密码登录服务器教程)
4,windows下内网客户端后台运行及开机设置:
frpc运行时始终有一个命令行窗口运行在前台,影响美观,我们可以使用一个批处理文件来将其运行在后台,而且可以双击执行,每次打开frpc不用再自己输命令了。
在任何一个目录下新建一个文本文件并将其重命名为“frpc.bat”,编辑,粘贴如下内容并保存
cd C:\Users\username\Desktop\frp_0.41.0_windows_amd64
frpc -c frpc.ini
exit
将cd后的路径更改为你的frpc实际存放的目录,这里建议最好放C盘下某一个文件夹下。
之后直接运行这个 .bat 文件即可启动frpc。
若想开机启动隐藏窗口(可在任务管理器中退出),则在开机自启文件夹下新建一个名为frp的vbs文件,下面写上:):
Set shell = Wscript.createobject("wscript.shell")
a = shell.run ("之前frpc.bat的存放路径",0)
至此,客户端配置完成,之后就是你自己根据需要在frpc.ini后追加规则即可。
强烈建议你在使用frp直接测试内网穿透前,先在局域网内测试好相关功能的正常使用,并配置好可能会影响的Windows防火墙等内容,在内网调试通过后再使用frp进行内网穿透测试。
5,win10内网服务器安装SFTP服务器
很多时候,我们需要在远程服务器中进行文件的上传与下载操作,因此要在内网服务器中设置一个可供上传与下载的服务器程序,这里我们选用sftp服务器,在内网服务器搭建SFTP服务器,对于linux搭建并开启SFTP比较简单,这里不详细介绍,有需要的话,自行百度即可,这里主要介绍如何在window10下开启SFTP:
参考步骤3中内网服务器设置,在配置文件frpc.ini
中追加写入以下内容并按照步骤3的过程开启SSH服务
[SFTP]
type = tcp
local_ip =127.0.0.1
local_port = 22
remote_port = 7002
并在某一个盘符中创建一个sftp的根文件夹:
D:\SFTPRoot
更改C:\ProgramData\ssh\sshd_config文件,在文件最后添加
ChrootDirectory D:\SFTPRoot
注:以上两步操作设置CHroot监狱,可以将用户登陆后需要将其锁定在特定的目录下,防止用户浏览其他重要数据,推荐在SSH中使用此命令。
保存后,重启ssh服务即可。
再重启frpc,执行
frpc -c frpc.ini
之后回到客户端执行,注意不同的sftp的指定参数不一定一致,在win下是-P,在linux下可能是-oPort,如下,请注意区别:
sftp -P 7002 username@x.x.x.x #类似于ssh登录
由于配置的公钥,因此此时无需密码直接登录说明正常,以下是一些sftp的简单命令
cd 指定目录
get -r DSSD.pdf C:\Users\username\Desktop 下载DSSD.pdf文件
put -r C:\Users\username\Desktop\zboot.img \ 上传zboot.img文件到根目录下
exit 退出
7,win10内网服务器配置Jupyter notebook
在自己电脑上写深度学习程序时,jupyter notebook
是很好的辅助工具。我们希望在服务器上开一个类似的web服务,使其可以在自己的浏览器中访问,从而实现远程调试,在线编程。jupyter notebook
是一种可以调用服务器的python解释器后端处理网页上输入的工具,我们要让它可以随处访问,需要一些额外配置,包括配置访问ip,访问密码,文件存储目录以及本机http服务端口,配置好以后,我们就可以开始在线编程了。
首先在终端下打开tensorflow环境,在win10下开启终端cmd,执行以下指令:
#首先进入ssh环境下的终端目录
ssh -p 2486 username@x.x.x.x
activate #等效于运行anconda prompt指令
conda activate tensorflow24gpu
先执行以下指令安装jupyter notebook,安装过了就不用安装了,直接跳过下一步
conda install jupyter notebook
安装好以后,我们在命令行输入
jupyter notebook --generate-config
就会在当前用户下生成一个名为.jupyter
的文件夹,这里我的地址是Writing default config to: D:\Cadence\Cadence\SPB_Data.jupyter\jupyter_notebook_config.py,注意按照正确的路径寻找文件。
里面有配置文件jupyter_notebook_config.py
。我们通过修改它即可进行jupyter notebook
的服务器端配置。首先我们对ip
与port
进行配置,即打开jupyter notebook服务后,可以通过哪个内网地址访问。我们的建议是将ip
设置为本机的内网ip,port
随意设置为支持http
服务的端口,下面是一个例子:
先在tensorflow环境下依次执行以下代码,之后会提示输入两次自定义远程登陆的密码,最后会出现一串 sha1xxxxxx的字符串,需要记录下来。
ipython
from notebook.auth import passwd
passwd()
这里密码我设置为123456,而依据我设置的密码123456生成的哈希码则为
'sha1:50448bb2f790:13197a0de39411a0e8fe49d5e6ceec06472b27f8'
之后打开之前一步生成的py格式的Jupyter notebook的配置文件,在最后添加
c.NotebookApp.ip = "127.0.0.1" #设置ip为内网IP
c.NotebookApp.port = "8888" #可自行指定一个端口, 访问时使用该端口
c.NotebookApp.password_required = True
c.NotebookApp.password = u'sha1:50448bb2f790:13197a0de39411a0e8fe49d5e6ceec06472b27f8' #别忘了前面有一个u
c.NotebookApp.open_browser = False #含义为是每次启动命令时是否打开浏览器,由于我们用的时候直接输入URL即可,所以这里不需要打开浏览器
c.NotebookApp.notebook_dir = r'C:\Users\username\Desktop' #设置文件存储目录,这样所有在jupyter notebook中的文件与notebook都会存储于C:\Users\username\Desktop这个文件夹下,文件夹需要提前创建,不然会报错,推荐此设置
c.NotebookApp.allow_remote_access = True #如果是比较老的jupyter notebook版本才有这一项,允许远程连接
然后,我们在SSH远程登录下的tensorflow环境下命令行运行
jupyter notebook
并输入命令后就可以在内网中通过http:内网ip:端口
访问到服务器开启的jupyter notebook服务了。
用公网中介访问内网web服务
如上文所述,我们可以用服务器在内网开启好用的在线编程服务。那么,内网的web服务如何通过公网访问呢?如果我们已经如第一章所述设置了对应的ssh
, 那么可以用反向端口映射来访问。
方法1:
以jupyter notebook为例,如果我们已经设置了公网的ssh穿透,那么我们需要访问内网的web服务,只需要在自己机器的命令行中输入
ssh -p 2486 -NfL 12345:127.0.0.1:8888 username@x.x.x.x
意义为本地端口通过跳板映射到其他机器,在本地机器启动一个 12345 端口,通过 x.x.x.x公网服务器将远程深度学习服务器127.0.0.1:8888的服务转发到本地机器上,这时在本地机器访问 127.0.0.1:12345 相当于访问 深度学习服务器的127.0.0.1:8888。
就可以把内网的web服务端口转发到本机,然后通过访问http:localhost:12345就可以访问服务器的web服务。但是这种操作较为繁琐,需要多次重复输入。如果前面的jupyter notebook的配置文件.config未能配置正确,则当我们在远程访问jupyter时,在本地打开jupyter的web服务时需要输入jupyter生成的token,而cmd终端下打开jupyter时生成网址后的token,如下所示:
如果正确配置了.config文件,则在打开时会正常显示输入密码,如同本地登录。
方法2:
另外一个更好的解决方案是通过frp
服务,联合二级域名来进行访问。我们用frp
将内网的web服务端口
转发到公网服务器的端口,通过访问公网服务器的某端口即可访问到内网。但是,往往公网服务器允许的http服务端口只有1个,因此,我们可以通过二级域名的方式转发内网服务器的多个web服务,而这就需要用到一个域名。
但是,本人并未成功操作成功,不是403错误,就是其他新的错误拒绝连接,因此后面本人就不再继续方法2了。
7,win10内网服务器配置Tensorboard
TensorBoard是一个可视化工具,它可以用来展示网络图、张量的指标变化、张量的分布情况等。特别是在训练网络的时候,我们可以设置不同的参数(比如:权重W、偏置B、卷积层数、全连接层数等),使用TensorBoader可以很直观的帮我们进行参数的选择。
因此,在远程访问中,Tensorboard与Jupyter是两个主要的深度学习训练用的可视化工具,在远程访问时,不能打开这两个工具是痛苦的,下面我将示范如何在内网服务器中配置tensorboard供外网访问者访问。
1,这里主要用的技术是SSH远程代理,因此外部的客户机中必须安装SSH服务器并配置好与内网服务器的SSH与之连接,这步骤在上文步骤2中有详细的描述,便不再细说,下面则是在内网服务器中如何配置tensorboard:
在内网服务上新建一个用于保存tensorboard的目录:
SSH登录内网服务器执行以下命令:
ssh -p 2486 username@x.x.x.x #登录内网服务器
activate
conda activate tensorflow24gpu #进入tensorflow虚拟环境
#tensorboard运行
tensorboard --logdir='D:\TensorflowModels\tensorflowboard_log' --port =6006
在客户机上新打开一个终端,并继续在客户机上执行以下命令:
ssh -p 2486 -NfL 16006:127.0.0.1:6006 username@x.x.x.x
之后在本地客户机打开网页127.0.0.1:16006,即可查看远程的tensorboard
下面示范如何操作tensorboard
首先准备一个 Mnist数据集,并执行以下python boardtest.py代码生成一个供tensorboard使用的文件,环境是tensorflow1.13版本,代码如下:
#boardtest.py
import os
import tensorflow as tf
LOGDIR = 'D:\\TensorflowModels\\mnist_dataset\\' #Minist数据集路径
mnist = tf.contrib.learn.datasets.mnist.read_data_sets(train_dir=LOGDIR, one_hot=True)
def conv_layer(input, size_in, size_out):
w = tf.Variable(tf.truncated_normal([5, 5, size_in, size_out], stddev=0.1))
b = tf.Variable(tf.constant(0.1, shape=[size_out]))
conv = tf.nn.conv2d(input, w, strides=[1, 1, 1, 1], padding='SAME')
act = tf.nn.relu(conv + b)
return tf.nn.max_pool(act, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
def fc_layer(input, size_in, size_out):
w = tf.Variable(tf.truncated_normal([size_in, size_out], stddev=0.1))
b = tf.Variable(tf.constant(0.1, shape=[size_out]))
act = tf.nn.relu(tf.matmul(input, w) + b)
return act
def mnist_model(learning_rate, use_two_conv, use_two_fc, hparam):
tf.reset_default_graph()
sess = tf.Session()
# setup placeholders, and reshape the data
x = tf.placeholder(tf.float32, shape=[None, 784])
x_image = tf.reshape(x, [-1, 28, 28, 1])
y = tf.placeholder(tf.float32, shape=[None, 10])
if use_two_conv:
conv1 = conv_layer(x_image, 1, 32)
conv_out = conv_layer(conv1, 32, 64)
else:
conv1 = conv_layer(x_image, 1, 64)
conv_out = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
flattened = tf.reshape(conv_out, [-1, 7 * 7 * 64])
if use_two_fc:
fc1 = fc_layer(flattened, 7 * 7 * 64, 1024)
embedding_input = fc1
embedding_size = 1024
logits = fc_layer(fc1, 1024, 10)
else:
embedding_input = flattened
embedding_size = 7 * 7 * 64
logits = fc_layer(flattened, 7 * 7 * 64, 10)
xent = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))
train_step = tf.train.AdamOptimizer(learning_rate).minimize(xent)
correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
emdedding = tf.Variable(tf.zeros([1024, embedding_size]))
assignment = emdedding.assign(embedding_input)
sess.run(tf.global_variables_initializer())
# 保存路径,刚刚tensorflowboard_log下新建一个test文件夹保存board文件
tenboard_dir = 'D:\\TensorflowModels\\tensorflowboard_log\\testa\\'
# 指定一个文件用来保存图
writer = tf.summary.FileWriter(tenboard_dir + hparam)
# 把图add进去
writer.add_graph(sess.graph)
for i in range(2001):
batch = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x: batch[0], y: batch[1]})
def make_hparam_string(learning_rate, use_two_fc, use_two_conv):
conv_param = 'conv=2' if use_two_conv else 'conv=1'
fc_param = 'fc=2' if use_two_fc else 'fc=1'
return 'lr_%.0E,%s,%s' % (learning_rate, conv_param, fc_param)
def main():
# You can try adding some more learning rates
for learning_rate in [1E-4]:
# Include 'False' as a value to try different model architectures.
for use_two_fc in [True]:
for use_two_conv in [True]:
# Construct a hyperparameter string for each one(example: 'lr_1E-3,fc=2,conv=2')
hparam = make_hparam_string(learning_rate, use_two_fc, use_two_conv)
print('Starting run for %s' % hparam)
# Actually run with the new settings
mnist_model(learning_rate, use_two_fc, use_two_conv, hparam)
if __name__ == '__main__':
main()
注意,以上代码为tensorflow1版本的代码,使用时需要注意,如果用tensorflow2版本,需要小小修改一下。
执行conda命令进入tensorflow1的环境并在boardtest.py代码的目录下,输入以下指令:
python boardtest.py
最终成功执行后,将在指定的目录下生成一个文件如下图所示:
之后再执行以下命令后,出现下面的图片即为成功运行
注意此处,在终端下需要进入testa文件夹的的上一层目录下,即在D:\TensorflowModels\tensorflowboard_log路径下执行以下程序:
tensorboard --logdir=./testa --port=6006
再新开一个终端,执行以下命令:
ssh -p 2486 -NfL 16006:127.0.0.1:6006 username@x.x.x.x
之后在本地客户机打开网页127.0.0.1:16006,即可查看远程的tensorboard
总结:
至此,Tensorflow的远程服务配置至此基本完成,主要重点为frp服务器,SSH服务器的安装配置及tensorflow下工具Jupyter notebook与tensorboard的web服务的端口映射。后续若有其他新的服务追加,基本原理和配置步骤与以上介绍应该大同小异,这里便不再赘述。
注:若需要frp映射不同局域网内主机,则在公网服务中,复制frps.ini为frps1.ini等,将其中bind_port的值改为不同,分别启动,比如frps1.ini的文件可以设置为以下形式。
[common]
bind_port = 8000
dashboard_port = 8500
token = a187@#%#!#&%RWG524
dashboard_user = admin
dashboard_pwd = admin123456
vhost_http_port = 11100
vhost_https_port = 11200
#公网服务器下执行命令如下所示
nohup frps -c /opt/frp_0.41.0_linux_amd64/frps1.ini &
参考博客:
https://zhuanlan.zhihu.com/p/442046879