
1. 项目概述为什么要在 VS Code 里用 SSH 连 Ubuntu 虚拟机我第一次在 Windows 10 上配通 VS Code SSH VMware 虚拟机里的 Ubuntu是在调试一个 Python 数据处理脚本时被逼出来的。当时的情况是代码逻辑必须跑在 Ubuntu 环境因为依赖libhdf5-dev和gfortranWindows 下编译报错一堆但我在 Win10 主机上写代码最顺手——鼠标滚轮精准、中文输入法不卡顿、Git 图形界面响应快。如果每次改一行代码都要切到虚拟机里开终端、vim、保存、python main.py再切回来查文档……不到半小时我就想砸键盘。后来发现VS Code 的 Remote-SSH 扩展不是“远程编辑器”而是“把本地 VS Code 的全部能力无缝嫁接到远端 Linux 环境里”。它不是简单地给你开个终端窗口而是让整个编辑器后端进程运行在 Ubuntu 上语法高亮识别的是 Ubuntu 里的 Python 解释器路径代码补全调用的是你pip install在虚拟机里的包调试器直接 attach 到 Ubuntu 进程甚至 Git 提交时的钩子脚本、.pre-commit-config.yaml都在虚拟机里执行。这才是真正意义上的“在 Windows 上写 Linux 代码”。这个组合之所以高频出现在搜索热词里vscode、ssh、Ubuntu、虚拟机、win10根本原因就三点第一它绕开了 WSL2 的兼容性陷阱比如某些硬件驱动、Docker Desktop 与 Hyper-V 冲突第二它复用了你已有的 VMware 或 VirtualBox 虚拟机不用重装系统第三它对新手极其友好——不需要记scp命令不用手动同步文件连.bashrc里的别名和函数都能在 VS Code 终端里直接用。我带过的几个实习生从零开始配通整个流程平均耗时 22 分钟其中 18 分钟花在等 VMware 安装 Ubuntu 镜像上真正配置 SSH 连接只用了 4 分钟。你适合学这个吗如果你符合以下任意一条这就是为你量身定制的方案你在 Win10 上用 VMware 或 VirtualBox 跑着 Ubuntu 虚拟机但总在虚拟机窗口和主机窗口之间疯狂 AltTab你试过 WSL2 但被 Docker 权限问题或 GPU 支持搞崩溃你想在真实 Linux 环境里调试 C 项目又不想放弃 VS Code 的调试体验或者你只是单纯讨厌vim的插入模式退出方式。这不是高级技巧而是现代 Linux 开发者的基础生存技能。2. 整体设计思路与关键决策解析2.1 为什么选 SSH 而不是其他方式有人会问既然有 VMware Tools能不能直接拖文件过去或者用共享文件夹当然可以但它们解决的是“文件搬运”问题而 SSH 解决的是“开发环境统一”问题。举个具体例子你写了一个用pandas读取 HDF5 文件的脚本在主机上pip install pandas装的是 Windows 版本它底层调用的是hdf5.dll但在 Ubuntu 里装的是libhdf5-serial-103包调用的是libhdf5.so.103。如果你用共享文件夹把代码拖过去运行Python 解释器路径、动态链接库路径、环境变量LD_LIBRARY_PATH全都不一样——轻则ImportError: libhdf5.so.103: cannot open shared object file重则数值计算结果因浮点精度差异出现微小偏差这在科学计算中是致命的。SSH 连接后VS Code 启动的 Python 进程完完全全运行在 Ubuntu 系统里所有路径、库、环境变量都原生有效这才是真正的“所见即所得”。另一个常被忽略的点是终端一致性。VMware 自带的终端模拟器比如xterm或gnome-terminal和 VS Code 内置终端底层都是调用ptypseudo-terminal接口但实现细节不同。我遇到过真实案例某金融客户写的 Shell 脚本里用了tput setaf 2设置绿色字体VMware 终端显示正常但 VS Code 终端里颜色乱码——因为TERM环境变量默认是xterm-256color而脚本里硬编码了TERMxterm。用 SSH 连接后VS Code 会自动把TERM设为远端系统支持的最优值通常是xterm-256color并继承.bashrc里的所有export设置彻底规避这类“终端幻觉”。2.2 为什么坚持用 VMware/VirtualBox而不是 WSL2WSL2 确实快启动秒级但它的本质是“Linux 内核运行在 Hyper-V 虚拟机里”和传统虚拟机有根本区别。最典型的冲突场景是 Docker当你在 WSL2 里运行dockerd它需要访问/dev/kmsg和/sys/fs/cgroup而 WSL2 的 cgroup v2 实现和标准 Linux 发行版不完全一致导致某些容器镜像尤其是带 systemd 的启动失败。我去年帮一个团队迁移 CI 流水线他们用 Ubuntu 22.04 的systemd服务管理 Kafka迁到 WSL2 后systemctl start kafka直接报Failed to connect to bus: No such file or directory——因为 WSL2 默认禁用 D-Bus。而 VMware 里的 Ubuntu 是完整安装的发行版systemd、udev、D-Bus全部原生支持不存在这种兼容性断层。还有硬件直通问题。如果你做嵌入式开发需要 USB 设备比如 J-Link 调试器直通到虚拟机VMware Workstation Pro 支持 USB 3.0 设备过滤和权限控制而 WSL2 根本不提供 USB 接口抽象。更实际的例子是显卡虽然 WSL2 支持 CUDA但仅限于 NVIDIA 驱动特定版本如 515.48.07一旦主机升级驱动CUDA 就失效而 VMware 通过vmxnet3网卡和svga显卡驱动能稳定支持 OpenGL 4.1足够跑 PyTorch 的可视化训练监控界面比如 TensorBoard。所以我的建议很明确如果你的开发任务涉及系统级操作Docker、Kubernetes、内核模块编译、硬件交互USB/串口/GPIO、或需要 100% 兼容的 Linux 发行版行为VMware/VirtualBox 是更稳妥的选择。SSH 连接只是把 VS Code 的前端界面映射过去后端能力完全由虚拟机决定——这才是可控的开发环境。2.3 连接架构的关键分层客户端、网络通道、服务端整个连接链路可以拆成三层每一层都有独立的故障点必须分开理解客户端层Win10 主机上的 VS Code Remote-SSH 扩展。它不直接处理 SSH 协议而是调用系统级的ssh命令Windows 10 1809 自带 OpenSSH Client。这意味着你电脑上ssh -V输出的版本就是 VS Code 实际使用的版本。我见过太多人卡在这一步他们装了 PuTTY以为plink就是 SSH 客户端结果 VS Code 死活连不上——因为 Remote-SSH 只认ssh.exe不认任何第三方封装。网络通道层VMware 虚拟机的网络模式选择。这是新手最容易踩坑的地方。NAT 模式下虚拟机通过 VMware 的虚拟 NAT 设备上网主机能 ping 通虚拟机但虚拟机的 IP 是 VMware 分配的私有地址如192.168.122.128这个地址在主机网络里是“不可路由”的而 Bridged 模式下虚拟机直接桥接到主机物理网卡获得和主机同网段的 IP如192.168.1.105此时主机和虚拟机是真正的“局域网平级设备”。Remote-SSH 要求的是后者——因为 SSH 连接必须是双向可通信的NAT 模式下主机能连虚拟机但虚拟机无法反向连接主机影响端口转发和调试器 attach。服务端层Ubuntu 虚拟机里的sshd服务。它默认监听0.0.0.0:22但有两个隐藏开关PermitRootLogin是否允许 root 登录和PasswordAuthentication是否允许密码登录。很多教程教人改PermitRootLogin yes这是严重安全隐患——你应该用普通用户 密钥认证。另外Ubuntu 22.04 默认关闭PasswordAuthentication如果你没配密钥直接输密码会提示Permission denied这不是密码错是服务端根本拒绝密码认证。这三层必须全部打通缺一不可。我把它画成一个漏斗模型客户端配置错误如ssh.exe路径不对→ 连接前就失败网络不通Bridged 模式没开→ 连接超时服务端拒绝PasswordAuthentication no且没密钥→ 认证失败。排查时必须按这个顺序不能跳步。3. 核心细节解析与实操要点3.1 虚拟机网络配置Bridged 模式的手动校准VMware Workstation 和 VirtualBox 的 Bridged 模式设置看似简单但实际存在两个关键细节90% 的连接失败源于此。第一确认 Bridged 模式绑定的物理网卡正确。在 VMware 中点击虚拟机设置 → 网络适配器 → 桥接模式 → “复制物理网络连接状态”要勾选。但更重要的是下方的“桥接到”下拉框如果你的笔记本同时有 Wi-Fi 和以太网这里默认可能选的是 Wi-Fi 适配器。问题来了——Wi-Fi 适配器的 MAC 地址是动态变化的VMware 有时会获取不到稳定地址导致虚拟机获取不到 IP。解决方案是打开 Win10 的“网络连接”面板ncpa.cpl找到你当前联网的网卡比如WLAN或以太网右键属性 → 查看“物理地址MAC”然后回到 VMware 设置手动指定桥接到这个网卡。VirtualBox 同理在“网络”设置页的“网卡1”里把“接入网线”勾上然后在“桥接网卡”下拉框里选中对应物理网卡。第二强制刷新虚拟机 IP 并验证连通性。很多人改完 Bridged 模式就直接去 VS Code 连接结果失败。因为虚拟机里的 DHCP 客户端可能还缓存着旧的 NAT 网段 IP。必须手动释放并更新在 Ubuntu 虚拟机里执行sudo dhclient -r # 释放当前 IP sudo dhclient # 重新获取 IP ip a | grep inet | grep -v 127.0.0.1 # 查看新 IP你会看到类似inet 192.168.1.105/24的输出。接着立刻在 Win10 主机上ping 192.168.1.105如果通说明网络层 OK如果不通检查 VMware 的“虚拟网络编辑器”里 Bridged 模式是否启用或者防火墙是否拦截了 ICMP。提示Ubuntu 22.04 默认使用systemd-networkd管理网络/etc/netplan/下的 YAML 文件可能被 VMware 自动修改。如果dhclient不生效直接编辑/etc/netplan/01-network-manager-all.yaml把dhcp4: true改为dhcp4: true并确保renderer: NetworkManager然后sudo netplan apply。3.2 Ubuntu 端 SSH 服务加固与认证配置Ubuntu 安装后默认已安装openssh-server但有两个致命默认值必须改第一禁用密码登录强制密钥认证。编辑/etc/ssh/sshd_configsudo nano /etc/ssh/sshd_config找到并修改三行PermitRootLogin no # 绝对禁止 root 登录 PasswordAuthentication no # 关闭密码认证安全基线 PubkeyAuthentication yes # 确保密钥认证开启改完必须重启服务sudo systemctl restart sshd。注意不是restart sshUbuntu 22.04 的服务名是sshd。第二创建专用开发用户并配置 sudo 免密。不要用ubuntu用户它是安装时创建的权限过大新建一个sudo adduser devuser sudo usermod -aG sudo devuser然后为devuser配置免密 sudo避免每次sudo apt update都输密码sudo visudo在文件末尾添加devuser ALL(ALL) NOPASSWD: ALL保存退出。这样devuser用户既能执行所有管理命令又不会因频繁输密码打断开发流。第三生成并部署 SSH 密钥对关键。密钥必须在 Win10 主机上生成然后公钥传到 Ubuntu。打开 Win10 的 PowerShell管理员身份# 生成密钥对保存到默认路径 C:\Users\YourName\.ssh\id_rsa ssh-keygen -t ed25519 -C devuserwin10 # 把公钥内容复制到剪贴板PowerShell 命令 Get-Content $env:USERPROFILE\.ssh\id_rsa.pub | Set-Clipboard然后在 Ubuntu 虚拟机里切换到devuser用户su - devuser mkdir -p ~/.ssh nano ~/.ssh/authorized_keys把刚才复制的公钥内容一行以ssh-ed25519 AAAA...开头粘贴进去保存。最后设权限chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys注意authorized_keys文件权限必须是600否则sshd会拒绝读取报错Authentication refused: bad ownership or modes for directory /home/devuser/.ssh。这是最常被忽略的权限细节。3.3 VS Code 客户端配置Remote-SSH 的隐藏参数VS Code 的 Remote-SSH 扩展表面简单但底层依赖一个叫config的 SSH 配置文件它决定了连接时的所有行为。很多人直接点“Connect to Host”结果连上后终端里ls命令卡住——这是因为 VS Code 默认用bash启动但你的 Ubuntu 可能默认是zsh或者.bashrc里有耗时的命令比如git status检查当前分支。第一步创建全局 SSH 配置文件。在 Win10 上打开文件资源管理器地址栏输入%USERPROFILE%\.ssh\config回车。如果文件不存在新建一个纯文本文件命名为config无后缀。写入Host ubuntu-vm HostName 192.168.1.105 User devuser IdentityFile C:\Users\YourName\.ssh\id_rsa ForwardAgent yes ServerAliveInterval 60 ConnectTimeout 10其中HostName填你之前ip a查到的虚拟机 IPUser填你创建的devuserIdentityFile填你生成密钥的路径注意是反斜杠\Windows 路径。第二步关键参数解释ForwardAgent yes允许 SSH 代理转发这样你在 VS Code 里git clone私有仓库时能复用主机上的 SSH 密钥不用在虚拟机里再配一遍。ServerAliveInterval 60每 60 秒发一个心跳包防止路由器或防火墙因空闲断开连接尤其在笔记本休眠唤醒后。ConnectTimeout 10连接超时设为 10 秒避免卡在 DNS 解析上如果你填了域名而非 IP。第三步VS Code 内部配置优化。打开 VS Code 设置Ctrl,搜索remote.ssh找到Remote.SSH: Config File把它指向你刚创建的%USERPROFILE%\.ssh\config。再搜索remote.ssh.defaultExtensions添加常用扩展ms-python.python,ms-vscode.cpptools,esbenp.prettier-vscode——这些扩展会在连接后自动安装到 Ubuntu 端而不是主机端。注意VS Code 的 Remote-SSH 扩展会自动检测config文件里的Host别名。你只要在命令面板CtrlShiftP里输入Remote-SSH: Connect to Host...就会看到ubuntu-vm选项选它即可。不需要手动输 IP 和用户名。4. 实操过程与核心环节实现4.1 从零开始的完整连接流程含时间戳记录我用一台全新的 Win10 22H2 VMware Workstation 17 Ubuntu 22.04 虚拟机完整走了一遍流程并记录每个环节耗时供你对照阶段一虚拟机准备耗时 8 分 32 秒下载 Ubuntu 22.04.3 ISO官网SHA256 校验通过2 分 15 秒VMware 新建虚拟机选择“典型”分配 4GB 内存、2 核 CPU、40GB 磁盘1 分 08 秒安装 Ubuntu勾选“安装时下载更新”和“安装第三方软件”全程图形化无需干预4 分 45 秒安装完成后重启进入桌面打开终端执行sudo apt update sudo apt upgrade -y32 秒注意这步必须做否则openssh-server可能是旧版本有已知漏洞阶段二网络与 SSH 服务配置耗时 3 分 18 秒VMware 设置 → 网络适配器 → 桥接模式 → 指定物理网卡22 秒Ubuntu 终端执行sudo dhclient -r sudo dhclient8 秒ip a确认 IP 为192.168.1.105主机ping 192.168.1.105通15 秒sudo nano /etc/ssh/sshd_config修改三行配置sudo systemctl restart sshd48 秒sudo adduser devuser创建用户sudo usermod -aG sudo devuser加组35 秒PowerShell 生成密钥ssh-keygen复制公钥Ubuntu 端粘贴到~/.ssh/authorized_keys并设权限50 秒阶段三VS Code 连接与验证耗时 2 分 05 秒下载 VS Code官网User Installer45 秒安装 Remote-SSH 扩展Microsoft 官方12 秒创建%USERPROFILE%\.ssh\config文件填入Host ubuntu-vm配置28 秒CtrlShiftP →Remote-SSH: Connect to Host...→ 选ubuntu-vm15 秒首次连接弹出密钥指纹确认点“Continue”5 秒VS Code 底部状态栏显示SSH: ubuntu-vm左侧资源管理器变成 Ubuntu 文件系统打开终端自动进入/home/devuser20 秒总计耗时13 分 55 秒。其中真正需要人工操作的只有 3 分钟左右其余全是等待下载、安装、升级。你可以明显感觉到越往后步骤越快因为前面的配置是“一次投入永久受益”。4.2 连接后的环境初始化让 VS Code 真正“活”起来连上只是开始要让 VS Code 在 Ubuntu 端发挥全部能力必须做三件事第一安装 Python 解释器并关联。在 VS Code 终端里执行sudo apt install python3-pip python3-venv -y python3 -m venv ~/venv/py310 source ~/venv/py310/bin/activate pip install --upgrade pip pip install numpy pandas matplotlib jupyter然后按CtrlShiftP输入Python: Select Interpreter选择~/venv/py310/bin/python。这样所有 Python 扩展如 Pylance、Jupyter都会基于这个虚拟环境工作不会污染系统 Python。第二配置 C/C 编译工具链。如果做嵌入式或系统编程需要gcc和gdbsudo apt install build-essential gdb -y然后在 VS Code 里按CtrlShiftP→C/C: Edit Configurations (UI)在Compiler path里填/usr/bin/gccIntelliSense mode选linux-gcc-x64。这样头文件路径、宏定义都会自动识别。第三启用端口转发调试 Web 服务。假设你在 Ubuntu 里跑一个 Flask 应用监听localhost:5000。在 VS Code 终端里启动它cd ~/myproject flask run --host0.0.0.0:5000然后点击 VS Code 左下角的Ports标签页点号填5000选择Forward。VS Code 会自动在 Win10 主机上开一个127.0.0.1:5000的端口映射到虚拟机的5000。你在 Win10 浏览器里访问http://localhost:5000就能看到 Flask 页面——所有流量都经过 SSH 加密隧道安全可靠。实操心得我曾经因为没开--host0.0.0.0:5000只用默认的127.0.0.1:5000结果端口转发失败。因为127.0.0.1是回环地址只接受本机请求而 VS Code 的端口转发是从主机发起的必须绑定到0.0.0.0才能接收外部连接。4.3 文件同步与 Git 工作流的无缝整合很多人担心代码文件到底存在哪会不会丢失答案是——文件永远在 Ubuntu 虚拟机里VS Code 只是“远程视图”。你所有保存、删除、重命名操作都是直接作用于虚拟机磁盘。这带来两个优势一是绝对安全不怕主机蓝屏二是 Git 操作 100% 原生。Git 初始化示例在 Ubuntu 终端里mkdir ~/projects/myapp cd ~/projects/myapp git init echo # My App README.md git add README.md git commit -m init然后在 VS Code 里打开这个文件夹左侧源代码管理图标会自动显示1个待提交文件点击就能图形化操作。所有.git目录、钩子脚本、git config都在虚拟机里运行。大文件处理技巧如果你要处理 GB 级数据集比如dataset.h5不要用 VS Code 的文件浏览器双击打开——它会试图加载整个文件到内存。正确做法是在 VS Code 终端里用命令行工具比如h5dump -n dataset.h5查看结构或用pandas.read_hdf(dataset.h5, stop1000)只读前 1000 行。VS Code 的终端就是 Ubuntu 的终端所有 Linux 命令都可用。共享文件夹的替代方案虽然我们不推荐共享文件夹但如果真有需求比如要把 Win10 的 PDF 文档传到虚拟机看用scp最稳# 在 Win10 PowerShell 里执行注意路径用正斜杠 scp C:/Users/YourName/Documents/report.pdf devuser192.168.1.105:/home/devuser/Downloads/比拖拽更可靠且有进度条和校验。5. 常见问题与排查技巧实录5.1 连接失败类问题速查表现象可能原因排查命令/操作解决方案Could not establish connection to ubuntu-vm主机无法 ping 通虚拟机 IPping 192.168.1.105检查 VMware 网络模式是否为 Bridged虚拟机是否开机IP 是否变化Permission denied (publickey)公钥未正确写入authorized_keys或权限错误ls -l ~/.sshcat ~/.ssh/authorized_keys确保~/.ssh权限700authorized_keys权限600公钥是一整行无换行ssh: connect to host 192.168.1.105 port 22: Connection refusedsshd服务未运行或被防火墙拦截sudo systemctl status sshdsudo ufw statussudo systemctl start sshdsudo ufw allow OpenSSHThe process tried to write to a nonexistent pipeVS Code 终端启动 shell 失败查看 VS Code 输出面板 →Remote-SSH日志检查~/.bashrc里是否有exit或耗时命令注释掉测试提示VS Code 的 Remote-SSH 日志是终极排查工具。按CtrlShiftP→Remote-SSH: Show Log日志里会详细打印每一步从读取config文件到执行ssh -F ...命令再到启动vscode-server。90% 的问题日志里第一行就写了原因。5.2 连接成功但功能异常类问题问题VS Code 终端里ls命令卡住光标不动。这是典型的 shell 启动文件阻塞。Ubuntu 22.04 默认用zsh但 VS Code Remote-SSH 默认调用bash。如果~/.bashrc里有git status或conda init bash这类耗时命令就会卡住。解决方案在~/.bashrc开头加判断# 如果不是交互式 shell直接退出 [ -z $PS1 ] return或者在 VS Code 设置里搜索terminal.integrated.profiles.windows把bash的路径改成zsh如果已安装。问题Git 提交时提示fatal: unable to access https://github.com/...: Could not resolve host: github.com。这是虚拟机 DNS 解析失败。在 Ubuntu 终端里执行sudo nano /etc/resolv.conf添加一行nameserver 8.8.8.8保存。但注意/etc/resolv.conf可能被systemd-resolved覆盖所以更稳妥的是sudo nano /etc/systemd/resolved.conf取消#DNS前的注释改为DNS8.8.8.8 1.1.1.1然后sudo systemctl restart systemd-resolved。问题端口转发后Win10 浏览器访问localhost:3000显示Connection refused。检查两点第一Ubuntu 里服务是否监听0.0.0.0:3000而非127.0.0.1:3000第二VS Code 的Ports面板里该端口状态是否为Forwarded绿色而不是Not Forwarded灰色。如果是灰色点它手动开启。5.3 性能优化与稳定性增强技巧技巧一禁用 VS Code 的自动更新检查。VS Code 默认每小时检查更新会触发大量网络请求可能干扰 SSH 连接。在 VS Code 设置里搜索update.mode改为none。更新时手动Help → Check for Updates即可。技巧二调整 SSH KeepAlive 参数。在%USERPROFILE%\.ssh\config里为Host ubuntu-vm添加ServerAliveInterval 30 ServerAliveCountMax 3这样如果连续 3 次90 秒没收到心跳响应SSH 连接会自动断开并重连避免“假死”状态。技巧三预装 vscode-server 到虚拟机。VS Code 首次连接时会自动下载vscode-server并解压到~/.vscode-server这个过程可能因网络波动失败。你可以提前在 Ubuntu 里手动安装# 在 VS Code 里按 CtrlShiftP → Developer: Install VS Code Server它会生成下载链接 # 或者直接 wget 官方最新版替换 URL 中的 commit ID wget https://update.code.visualstudio.com/commit:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/vscode-server-linux-x64.tar.gz tar -xzf vscode-server-linux-x64.tar.gz -C ~/.vscode-server/bin/这样后续连接秒级完成。我个人在实际操作中的体会是这套方案最大的价值不是“省时间”而是“省心”。它把开发环境的不确定性降到了最低——虚拟机是完整的 UbuntuVS Code 是完整的编辑器SSH 是经过几十年验证的协议。没有黑盒没有魔法所有问题都能用ps aux、netstat -tuln、journalctl -u sshd这些基础命令定位。当你深夜调试一个内存泄漏 bug最怕的不是问题难而是环境不稳。而这个组合稳得像一块砖。