有一台性能較好的台式機(以下簡稱 H(ome)),希望在離開它的幾天內依舊可以繼續在其上的工作。這台 H 沒有公網 IP,於是購買一台雲服務器(以下簡稱 C(loud)),獲得公網 IP。使用 ssh forwarding 將 H 的 ssh 22 端口映射到的 C 的 2222 端口。之后進行開發部署。
另外鏈接 C 的筆記本電腦(L(aptop))。
以下介紹部署過程,備忘。
1. 雲服務器選購
因為雲服務器只是跳板機,只需要良好的連通性,幾乎無 CPU 和內存需求。本着合(節)適(約)的原則,選擇最便宜配置的服務器,選擇阿里雲 ECS,在購買界面選擇“一鍵購買”。選擇最便宜的1 vCPU 1 GiB
的機器。因為當前應用只是個人使用,網絡傳輸量小,也沒有穩定長時間的流量,所以帶寬處選擇按使用流量
(固定的配置費用減少 40%)。可以調整帶寬到 100Mbps,流量價格與 1Mbps 一致,僅需¥0.800/GB
。
這個選擇與 H 相同的鏡像,Ubuntu 18.04。
購買成功后,第一次用密碼登錄,ssh-keygen 生成密鑰對。將 H 與 L 的公鑰放置到 C 的 ~/.ssh/authorized_keys 文件中。
同時將 C 的公鑰放置到 H 的相同位置,方便 C 登錄 H。
在 C 的 ~/.ssh/config 中配置 H 的別名,在 L 的 ~/.ssh/config 中配置 H 的別名。各種需要的別名都配置一下,方便后續配置。
~/.ssh/config 的配置如下。
Host H
Hostname localhost
Port 2222
User jingetu
2. SSH forwarding
在 H 上進行 ssh forwarding 將端口映射到 C 上。命令如下。
# 將 H 的 22 端口映射到 C 的 2222 端口
nohup ssh -NR 2222:localhost:22 -o ExitOnForwardFailure=yes -o ServerAliveInterval=10 C &
這個 forwarding 不穩定,后面命令中的 -o ExitOnForwardFailure=yes -o ServerAliveInterval=10
就是用來保活用的。
為了進一步優化,想到用 crontab 按時檢查該進程是否存在。所以寫了這么一個 python 腳本 auto_ssh.py,檢查是否有該進程,如果沒有就再執行一次啟動。
#!/usr/bin/python
import subprocess
import os
out = subprocess.check_output("ps -ef | grep NR | grep ssh", shell=True)
if "2222:localhost:22" not in out_str:
os.system("nohup ssh -NR 2222:localhost:22 -o ExitOnForwardFailure=yes -o ServerAliveInterval=10 C &")
這個 ssh 命令應當是非 root 用戶執行的,因為是從 C 以正常用戶身份登錄 H。
crontab 實現定時運行以上腳本,以保活。以正常用戶身份輸入 crontab -e
進行編輯,添加行 * * * * * python /home/jingetu/auto_ssh.py
,表示每分鍾執行一次。(關於 crontab 的語法問題,可以通過網站 https://crontab.guru/ 測試,完全不需要記憶 crontab 語法規則。)之后可以使用 crontab -l
查看當前用戶的定時任務。
crontab 還有另外一個好處,就是重啟機器,crontab 的任務設定依舊存在。所以遠程 reboot H,僅需耐心等待 1 - 2 分鍾,H 便可自動 forwarding 到 C。
對於重啟,還有一些需要設置。1. 在 Settings -> Users 中將當前用戶設置成 Automatic Login,重啟進入系統無需輸出密碼;2. H 是雙系統,安裝一個 Ubuntu 軟件 Grub Customizer,調整在 grub 時 Ubuntu 是默認選項。
現在不是很確定以上操作的穩定性。所以還有一種想法是 */30 * * * * killall ssh
,每 30 分鍾自動斷開一次 forwarding,下一分鍾會有 auto_ssh.py 腳本自動啟動 forwarding。這么做犧牲體驗。
3. 開發環境配置
已知 VSCode 有服務器開發功能,可以通過 ssh 修改服務器 C 上的代碼。但實際的編譯過程應當在 H 上進行,所以就需要做一個代碼同步的工作。
3.1. 代碼同步
將 H 上的代碼整理到 C 的特定位置。安裝軟件 lsyncd 進行單方向 C -> H 的同步,只要 C 上的目錄下有任何變化就會同步到 H 上的目錄下。
配置文件 /etc/lsyncd.conf 如下:
settings {
logfile = "/tmp/lsyncd.log",
statusFile = "/tmp/lsyncd.status",
nodaemon = true,
statusInterval = 3
}
sync {
default.rsync,
source="/root/code_dir",
target="H:/home/jingetu/code_dir",
delay=1,
exclude={'*.swp', '*.swx', '*~'},
rsync = {
archive = true,
compress = false,
_extra = {"--omit-dir-times","-e ssh -i /root/.ssh/id_rsa"}
}
}
delay=1
表示每隔 1 秒同步一次。C 上的代碼目錄是 /root/code_dir
,H 上的代碼目錄是 /home/jingetu/code_dir
。
執行 lsyncd -nodaemon /etc/lsyncd.conf
就可以開啟同步。
3.2. VSCode 遠程開發
在 L 上,安裝 vscode,參考 https://code.visualstudio.com/docs/remote/ssh ,進行配置。也就是安裝 vscode 的 extension Remote Development。
按操作鏈接 C,會打開一個新的窗口,按指示打開 C 上的目錄 /root/code_dir
。
然后在 vscode 的底部開啟兩個 terminal(C 的 terminal).第一個 terminal 執行 lsyncd -nodaemon /etc/lsyncd.conf
,前台運行,可以查看 C 到 H 的同步日志。第二個 terminal,執行 ssh H
,登錄到 H,進行正常的代碼修改后的編譯、運行工作。
注意,不要在 lsyncd 沒有啟動的時候,修改服務器上的代碼。因為同步是按事件觸發的,修改的事件沒有被 lsyncd 記錄下來,沒有同步會造成兩處代碼不同步。
執行完以上操作就可以開始在 L 的 vscode 窗口寫代碼。
4. 保活
H 上設置以下腳本 crontab,自動連接 WiFi。第二次檢查 ifconfig,如果沒有鏈接上,有可能是網卡沒有掛載上,重啟一般能掛載上。
#!/usr/bin/python3
import subprocess
import os
out = subprocess.check_output("ifconfig", shell=True)
out_str = out.decode('utf-8')
if "wlo1" not in out_str:
os.system("nmcli c up id WiFi_NAME_XXX")
out = subprocess.check_output("ifconfig", shell=True)
out_str = out.decode('utf-8')
if "wlo1" not in out_str:
os.system("sudo /sbin/shutdown --reboot now")
有些時候可以將 H 進入到睡眠狀態,在睡眠狀態無 WiFi 鏈接,所以 C 就不能控制 H 從睡眠狀態中恢復回來。可以在睡眠之前先設定恢復時間,這個恢復由 BIOS 控制,所以時間是 BIOS 時鍾(Real Time Clock)的時間。以下是睡眠 3600 秒之后恢復。
sudo rtcwake -m disk -s 3600
睡眠命令如下。(記得這個命令需要謹慎使用,不要一睡不醒,可以可以查看一下 rtcwake 每天固定時間恢復,這樣就可以保證最多睡 24 小時。)
systemctl suspend
在 C 上清理使用 2222 端口的進程(有些時候會重復鏈接)。
kill $(lsof -t -i tcp:2222)
以上的代碼同步並不是一個很好的方法,可以直接用 sshfs 在 C 上掛載 H 的目錄。再從 L 上掛載 C 的目錄。(macOS 安裝 sshfs 有點問題,所以對於代碼目錄,可以直接掛載在 C 上,再從 L 上選擇 VSCode 遠程開發
。)
mkdir ~workspace && sshfs -o allow_other,default_permissions H:workspace ~/workspace