适用场景:本地 WSL 编辑代码,远程 HPC/集群运行计算,通过 sshfs 把集群上的源码目录和数据目录像本地磁盘一样使用。 本教程只讲目录挂载这件事是怎么做到的。
一句话概括
sshfs(SSH Filesystem)通过 SSH 协议把远程服务器上的一个目录映射成本地文件系统里的普通目录。挂载后,ls、cd、vim、code . 等操作与本地无异,改动实时同步到远程。
前置条件
1 本地环境
- WSL2 / Linux / macOS 终端
- 已安装
ssh、sshfs、tmux、fusermount
检查命令:
ssh -V
sshfs -V
tmux -V
which fusermount
安装:
sudo apt update
sudo apt install -y ssh sshfs tmux
2 远程集群侧
- 已开通账号,已知用户名和登录节点地址
- 已上传 SSH 公钥到集群
- 集群开启动态二次验证(如 Google Authenticator)
3 目录信息
| 远程目录 | 本地挂载点 | 用途 |
|---|---|---|
/public/home/your_username |
~/shenma/cluster_home |
源码、个人配置 |
/data/share/your_username |
~/shenma/cluster_data |
计算结果、大文件 |
核心原理
本地程序
│
▼
~/shenma/cluster_home
│ FUSE / sshfs
▼
SSH 隧道
│
▼
cluster:/public/home/your_username
- sshfs:把本地挂载点的读写请求翻译成 SFTP 命令,通过 SSH 发到远程。
- ControlMaster:第一条 SSH 完成认证后创建控制套接字,后续连接复用,免再次认证。
- tmux:在后台保持交互式终端会话,让动态验证码登录长期有效。
配置 SSH
编辑 ~/.ssh/config:
Host cluster
HostName cluster.example.com
User your_username
Port 22
IdentityFile ~/.ssh/id_ed25519_cluster
ControlMaster auto
ControlPath ~/.ssh/controlmasters/
ControlPersist 8h
ForwardX11 yes
ForwardX11Trusted yes
ServerAliveInterval 30
ServerAliveCountMax 3
创建控制套接字目录:
mkdir -p ~/.ssh/controlmasters
chmod 700 ~/.ssh/controlmasters
测试:
ssh cluster
建立 SSH 主连接
创建挂载点:
mkdir -p ~/shenma/cluster_home
mkdir -p ~/shenma/cluster_data
在 tmux 中启动 SSH:
tmux new-session -d -s cluster-master "ssh -Y cluster"
进入 tmux 输入验证码:
tmux attach -t cluster-master
输入 Google Authenticator 验证码后,按 Ctrl+B 再按 D 分离会话。
验证主连接:
ssh -O check cluster
应看到 Master running (pid=...)。
挂载远程目录
sshfs cluster:/public/home/your_username ~/shenma/cluster_home
sshfs cluster:/data/share/your_username ~/shenma/cluster_data
查看挂载:
df -h | grep sshfs
验证挂载
mountpoint -q ~/shenma/cluster_home && echo "cluster_home 已挂载"
ls -la ~/shenma/cluster_home
测试写入:
echo "hello" > ~/shenma/cluster_home/sshfs_test.txt
cat ~/shenma/cluster_home/sshfs_test.txt
rm ~/shenma/cluster_home/sshfs_test.txt
使用与卸载
使用挂载目录:
cd ~/shenma/cluster_home/your_project
vim some_file.cpp
ls ~/shenma/cluster_data/your_case
卸载:
fusermount -u ~/shenma/cluster_home
fusermount -u ~/shenma/cluster_data
ssh -O exit cluster
tmux kill-session -t cluster-master
一键管理脚本
下面脚本把上面的所有操作封装成菜单,保存为 ~/shenma/scripts/cluster-mount.sh:
#!/usr/bin/env bash
# cluster-mount.sh — 检查/建立/关闭 sshfs 挂载与 SSH ControlMaster
#
# 使用前修改:SSH_HOST、SSHFS_HOME_REMOTE、SSHFS_DATA_REMOTE
set -euo pipefail
SSH_HOST="${SSH_HOST:-cluster}"
TMUX_SESSION="${TMUX_SESSION:-cluster-master}"
SSHFS_HOME_LOCAL="${SSHFS_HOME_LOCAL:-$HOME/shenma/cluster_home}"
SSHFS_HOME_REMOTE="${SSHFS_HOME_REMOTE:-/public/home/your_username}"
SSHFS_DATA_LOCAL="${SSHFS_DATA_LOCAL:-$HOME/shenma/cluster_data}"
SSHFS_DATA_REMOTE="${SSHFS_DATA_REMOTE:-/data/share/your_username}"
R='\033[0;31m'
G='\033[0;32m'
Y='\033[1;33m'
B='\033[0;34m'
N='\033[0m'
function menu() {
echo -e "\n${B}sshfs 集群目录挂载管理${N}"
echo -e " 1) 检查状态并修复"
echo -e " 2) 断开连接"
echo -e " 3) 退出"
read -rp "请选择 [1-3]: " choice
}
function check_ssh_master() {
if ssh -O check "$SSH_HOST" 2>/dev/null; then
echo -e "${G}✓ SSH master 已连接${N}"
return 0
else
echo -e "${Y}✗ SSH master 未连接${N}"
return 1
fi
}
function check_mount() {
local local_path="$1" name="$2"
if mountpoint -q "$local_path" 2>/dev/null; then
echo -e "${G}✓ $name 已挂载 ($local_path)${N}"
return 0
else
echo -e "${Y}✗ $name 未挂载 ($local_path)${N}"
return 1
fi
}
function repair_mount() {
local local_path="$1" remote_path="$2" name="$3"
if mountpoint -q "$local_path" 2>/dev/null; then return 0; fi
echo -e "${Y}→ 重新挂载 $name${N}"
mkdir -p "$local_path"
if sshfs "$SSH_HOST:$remote_path" "$local_path"; then
echo -e "${G}✓ $name 挂载成功${N}"
else
echo -e "${R}✗ $name 挂载失败${N}"
return 1
fi
}
function rebuild_ssh_master() {
echo -e "\n${Y}SSH master 断开,需要重建。${N}"
echo -e "${Y}请在 tmux 窗口中输入 Google Authenticator 验证码,然后按 Ctrl+B 再按 D 离开。${N}"
read -n1 -rsp "按任意键继续 ..." tmp
echo
ssh -O exit "$SSH_HOST" 2>/dev/null || true
tmux kill-session -t "$TMUX_SESSION" 2>/dev/null || true
tmux new-session -d -s "$TMUX_SESSION" "ssh -Y $SSH_HOST"
tmux attach -t "$TMUX_SESSION"
}
function do_check() {
echo -e "\n${B}==> 当前状态${N}"
local ssh_ok=true mount1_ok=true mount2_ok=true
check_ssh_master || ssh_ok=false
check_mount "$SSHFS_HOME_LOCAL" "home 目录" || mount1_ok=false
check_mount "$SSHFS_DATA_LOCAL" "数据目录" || mount2_ok=false
if [[ "$ssh_ok" == true && "$mount1_ok" == true && "$mount2_ok" == true ]]; then
echo -e "\n${G}所有状态正常。${N}"
return 0
fi
read -rp "状态异常,是否修复? [Y/n]: " confirm
if [[ "$confirm" == [nN]* ]]; then
echo -e "${Y}已取消修复。${N}"
return 0
fi
do_repair
}
function do_repair() {
echo -e "\n${B}==> 开始修复${N}"
if ! check_ssh_master; then
rebuild_ssh_master
if ! check_ssh_master; then
echo -e "${R}✗ SSH master 重建失败。${N}"
return 1
fi
fi
repair_mount "$SSHFS_HOME_LOCAL" "$SSHFS_HOME_REMOTE" "home 目录"
repair_mount "$SSHFS_DATA_LOCAL" "$SSHFS_DATA_REMOTE" "数据目录"
echo -e "\n${G}修复完成。${N}"
}
function do_shutdown() {
echo -e "\n${B}==> 关闭连接${N}"
for mp in "$SSHFS_HOME_LOCAL" "$SSHFS_DATA_LOCAL"; do
if mountpoint -q "$mp" 2>/dev/null; then
fusermount -u "$mp" && echo -e "${G}✓ 已卸载 $mp${N}" || echo -e "${R}✗ 卸载失败 $mp${N}"
else
echo -e "${Y} 未挂载 $mp${N}"
fi
done
if ssh -O check "$SSH_HOST" 2>/dev/null; then
ssh -O exit "$SSH_HOST" && echo -e "${G}✓ SSH master 已关闭${N}" || echo -e "${R}✗ 关闭失败${N}"
else
echo -e "${Y} SSH master 未运行${N}"
fi
if tmux has-session -t "$TMUX_SESSION" 2>/dev/null; then
tmux kill-session -t "$TMUX_SESSION" && echo -e "${G}✓ tmux 会话已关闭${N}" || echo -e "${R}✗ 关闭失败${N}"
else
echo -e "${Y} tmux 会话不存在${N}"
fi
echo -e "\n${G}连接已清理。${N}"
}
function main() {
while true; do
menu
case "$choice" in
1) do_check ;;
2)
read -rp "确定断开连接? [y/N]: " confirm
if [[ "$confirm" == [yY]* ]]; then
do_shutdown
else
echo -e "${Y}已取消。${N}"
fi
;;
3)
echo -e "${B}再见。${N}"
exit 0
;;
*) echo -e "${R}无效选择,请输入 1-3。${N}" ;;
esac
done
}
main "$@"
用法:
chmod +x ~/shenma/scripts/cluster-mount.sh
bash ~/shenma/scripts/cluster-mount.sh
菜单选项:
- 检查状态并修复:自动检查 SSH master 和挂载,缺失时提示输入验证码并修复。
- 断开连接:卸载、关闭 SSH master、关闭 tmux 会话。
- 退出:退出脚本。
故障排查
1 命令找不到
sudo apt update
sudo apt install -y sshfs
2 `read: Connection reset by peer`
SSH master 断开,重新执行:
tmux new-session -d -s cluster-master "ssh -Y cluster"
3 挂载点显示为空
检查主连接是否仍在:
ssh -O check cluster
4 卸载时提示 busy
先关闭占用挂载点的进程:
lsof ~/shenma/cluster_home
fusermount -uz ~/shenma/cluster_home
5 验证码反复失败
确认手机时间与服务器时间同步。动态验证码窗口通常只有 30 秒。
6 ControlMaster 自动断开
调小 ServerAliveInterval 或增大 ControlPersist。
FAQ
1 sshfs 和 NFS 有什么区别?
sshfs 走 SSH 协议,不需要 NFS 服务或管理员权限,适合个人工作站临时挂载。NFS 性能更高,但需要服务端配置。
2 为什么不用密码登录?
集群安全策略通常禁用密码静态登录,必须密钥 + 二次验证。
3 可以不挂 tmux 吗?
如果你不需要二次验证,或集群支持免交互认证,可以直接 sshfs 挂载。需要动态验证码时,tmux 是最稳定的方案。
4 挂载后编辑大文件慢怎么办?
sshfs 适合源码、小文件和查看结果。频繁读写大文件建议用 rsync 同步或直接在集群上操作。
5 如何在 macOS 上使用?
安装 macFUSE 和 sshfs:
brew install macfuse sshfs tmux
然后按本教程相同步骤配置即可。
