本地深度學習服務器遠程訪問——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