如果你也是那种"Windows主力干活,Linux虚拟机专门用来跑环境"的玩家,这个需求你大概率遇到过:Ubuntu装好了,代码想放到Linux里跑,但总不能一直在VMware那个小窗口里写吧。复制粘贴不好使、剪贴板不共享、虚拟机一卡,写代码的心情全没了。VSCode远程连接Ubuntu就是解决这个问题的正经路子,核心工具是SSH。这篇文章我把整个流程拆到最细,从虚拟机网络配置、SSH服务开启、VSCode插件安装,到首次连接、报错排查、免密登录和端口转发,全部按"小白照着做就能成"的标准来写。
我见过太多人卡在中间某一步就放弃了,其实这列车的每一节车厢都有明确的检查点,只要按顺序确认好,一次跑通完全没问题。下面开始。
1. 为什么要折腾远程连接:虚拟机里写代码的三个痛点
先说动机。很多人不理解,虚拟机都打开了,Ubuntu桌面也看到了,直接在虚拟机窗口里写代码不就行了?真不行。我用下来的感受是:VMware窗口里写代码的体验,大概处于"能用,但每天都在找罪受"的水平。三个痛点最明显。
第一,界面体验断层。Windows下我习惯了多显示器、多标签页、快捷键切窗口,而虚拟机窗口被限制在一个固定画布里,分辨率还得手动调。更崩溃的是剪贴板经常不同步,我在Windows复制一段代码,切到虚拟机里粘贴,发现是空的,然后又得回头重新复制。这种零碎的摩擦一天下来消耗大量注意力。
第二,性能和资源的双重损失。Ubuntu桌面版本身就吃内存,再叠加上VMware的图形加速,机器差一点就会卡顿。很多时候你只是想跑一个Python脚本或编译一个C程序,根本不需要看到桌面,却被迫养着一个完整的GNOME图形环境。
第三,文件同步绕圈子。也有人用共享文件夹或者Samba在Windows和Linux之间互传代码,但改完代码还得手动同步,再加上编码格式、行尾符差异,经常莫名其妙出bug。
那为什么最终选 VSCode + Remote-SSH?因为它把"编码体验"和"运行环境"拆开了:你仍然在VSCode这个本地编辑器里写代码,但代码实际存放在Ubuntu上,终端、解释器、编译工具、调试器全部跑在远程Linux环境里。下面是几种常见方案的对比。
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| VMware窗口直接开发 | 零额外配置 | 卡、剪贴板不同步、窗口管理差 | 偶尔用一次 |
| Samba/共享文件夹 | 文件在Windows侧 | 文件同步靠手动,容易冲突 | 传文件 |
| VNC/远程桌面 | 能看到完整桌面 | 网络要求高、延迟明显 | 必须用图形界面 |
| PuTTY + XShell | 轻量稳定 | 纯终端,编辑体验弱 | 运维服务器 |
| VSCode Remote-SSH | 本地编辑体验,远程执行环境 | 首次配置稍复杂 | 日常开发主力 |
结论很直接:如果是正经写代码,Remote-SSH是当前综合成本最低的方案。剩下的问题就是,怎么把它跑通。
2. 动手前把三件事理清楚:网络、SSH服务、客户端
很多人上来就装VSCode插件,然后连不上,再回来查问题。我的经验是,先把底层链路理清楚,最后再碰VSCode。这条链路从上到下依次是:虚拟机网络、Ubuntu的SSH服务、Windows侧的SSH客户端和VSCode插件。每一层都有独立的验证方法。
2.1 虚拟机网络怎么选:NAT是默认最优解
Ubuntu能上网,不代表宿主机能直接连到它。这是新手最容易踩的第一个认知误区。虚拟机有三种常见网络模式,我用VMware和VirtualBox都测过,结论一致。
- NAT模式(VMware里叫NAT,VirtualBox里叫NAT):虚拟机通过宿主机共享IP上网,对外表现为宿主机的一个内部设备。宿主机可以主动访问虚拟机,虚拟机的出站网络也通。适合大多数开发场景。
- 桥接模式(Bridged):虚拟机和宿主机在同一个局域网里,有自己的独立IP,看起来就像另一台实体机。适合需要被局域网其他人访问的场景。
- Host-Only模式:虚拟机只能和宿主机通信,不能上网。只适合做隔离实验。
对小白来说,默认选NAT就对了。理由很简单:NAT不需要关心路由器配置,不用担心同事的电脑IP占用,虚拟机的IP由VMware内部DHCP分配,宿主机访问它是稳定可控的。桥接模式虽然听起来更"真实",但一旦换了Wi-Fi或网线,虚拟机的IP可能变,反而麻烦。
装好Ubuntu后,在虚拟机里打开终端,执行ip addr查看IP地址,类似192.168.xxx.xxx这种。这里有个小坑:很多人用ifconfig,但新版本Ubuntu默认没装net-tools,会提示command not found。直接ip addr就行,这是现代Linux标准的网络查询命令。
提示:判断虚拟机网络是否正常的快速方法是
ping www.baidu.com,能通说明出站网络没问题。但别忘了,出站正常和宿主机能连它,是两码事。
2.2 Ubuntu开启SSH服务
Ubuntu默认只装了SSH客户端,没有服务端。也就是说,它能用ssh去连别人,但别人连不上它。所以必须要装openssh-server。
在Ubuntu终端里按顺序执行:
# 先更新软件源缓存 sudo apt update # 安装SSH服务端 sudo apt install -y openssh-server装完后,用下面三条命令确认状态:
# 查看SSH服务状态 sudo systemctl status ssh # 开机自启 sudo systemctl enable ssh # 确认22端口在监听 ss -tlnp | grep 22如果最后一条命令能看到0.0.0.0:22或[::]:22的监听记录,说明SSH服务已经正常起来了。再看一眼Ubuntu自带的防火墙:
sudo ufw status如果你之前手动开过UFW防火墙,状态是active,记得放行22端口:
sudo ufw allow ssh这一步很多人会忘,但SSH连不上的错误里,有一小半其实是防火墙在拦。
2.3 宿主机安装VSCode和Remote-SSH插件
VSCode安装没什么难点,记住从官方网站下载。Windows下安装时有个选项叫"添加到PATH",建议勾上。安装完后搜索插件Remote - SSH,这是微软官方出的扩展,扩展ID是ms-vscode-remote.remote-ssh,认准这个ID,同名仿冒的插件很多。
装好插件后,VSCode左下角会出现一个绿色或蓝色的><图标。这个图标是Remote开发的入口,后面所有连接操作都从它出发。
顺便提一句,如果你之前按网上的教程装了一堆其他远程插件,比如Remote - SSH: Editing Configuration Files这类,它们是Remote-SSH的辅助扩展,不用单独装,主插件会自动带上。
到这里,底层三件事全部就绪。检查清单是:虚拟机网络通不通、SSH端口在不在监听、VSCode插件装没装好。三件事都确认后,再进入真正的连接环节。
3. 从配置到第一次连接成功:每一步都有产出
配置过程其实就两件事:写SSH配置文件,然后发起连接。但这两件事里的细节决定成败。
3.1 一份不会出错的SSH Config
在VSCode里按F1或Ctrl+Shift+P打开命令面板,输入并执行:
Remote-SSH: Open SSH Configuration File...这时会让你选择配置文件,Windows下默认路径是C:\Users\你的用户名\.ssh\config。如果弹出提示说文件不存在,选择"创建新文件",然后把它保存到这个路径。
把下面这段配置写进去:
Host ubuntu-vm HostName 192.168.xxx.xxx User yourname Port 22解释一下每个字段:
Host:给这个连接起的别名,随便写,方便自己认就行。后面连接时选这个名字。HostName:Ubuntu的IP地址,就是刚才用ip addr查到的那个。User:你登录Ubuntu时用的用户名,不是邮箱,也不是显示名,是类似于yourname的那个短名称。可以通过执行whoami查询。Port:SSH端口,默认就是22,写不写都可以,但写上更清晰。
注意:Windows下如果你之前没建过
.ssh文件夹,直接创建C:\Users\你的用户名\.ssh\config即可。但如果你用的是老版本Windows或公司电脑有安全策略限制,建议把路径放在VSCode提示的默认位置,不要随意改到其他盘。
3.2 发起首次连接,遇到fingerprint怎么选
配置写好后,再按F1,执行:
Remote-SSH: Connect to Host...选择刚才配置的别名ubuntu-vm。VSCode会新开一个窗口,然后开始连接流程。首次连接时,界面会弹出一个对话框,内容是:
The authenticity of host '192.168.xxx.xxx' can't be established. Are you sure you want to continue connecting?这是SSH在问你:这台主机的指纹(fingerprint)不认识,是否信任它并继续连接?输入yes回车即可。原理很简单——SSH防止"中间人攻击",首次连接时记录主机指纹,以后再连会校验。如果你不确定,可以先把Ubuntu上执行ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub得到的指纹拿出来对照一下,确认一致再输yes。
接下来会提示输入密码。这里输入的密码是没有回显的,屏幕上不会显示任何字符,这是正常的,不要以为键盘坏了。输完回车,VSCode会在线安装vscode-server到远程Ubuntu,这个过程视网络情况需要几十秒到几分钟。
连接成功的标志是左下角显示SSH: ubuntu-vm,同时底部状态栏变成绿色。
3.3 验证连接不是"表面成功"
这一步很多人省略,但我觉得必须做,因为"VSCode不报错"和"确实能在远程干活"是两回事。
连接后先做三件事:
- 打开远程文件夹:
File->Open Folder,此时弹出的文件浏览器会让你输密码,然后看到的是远程Linux的文件系统。随便打开一个文件夹,比如/home/你的用户名。 - 打开集成终端:快捷键
Ctrl+`,执行whoami和uname -a。如果输出的是Ubuntu的用户名和Linux内核信息,说明终端确实跑在远程机器上。 - 安装一个远程扩展,比如Python。打开扩展面板,搜索Python,点击
Install in SSH: ubuntu-vm。装好后,在扩展列表里能看到这个插件被归到SSH: ubuntu-vm分类下,而不是本地。
这三步走完,才算是真正跑通了远程开发链路。
4. 一键复现排错现场:连接失败的完整排查链
这一节写给那些"第一次没成"的朋友。我把自己见过的所有失败案例按排查顺序整理成了一条链路,从网络层一路往上到应用层。别跳着看,按照这个顺序找,问题在哪里一目了然。
4.1 连接超时或卡在"Setting up SSH Host"
如果在选择的瞬间就报Could not establish connection to ...或者长时间卡在转圈状态,第一个要怀疑的是网络层,不是VSCode。
先到Windows的PowerShell或CMD里执行:
ping 192.168.xxx.xxx- 如果能ping通,说明宿主机和虚拟机之间网络通着,继续往下排查。
- 如果ping不通,问题在虚拟机网络。检查VMware菜单里的"可移动设备"或"虚拟机设置",确认网络连接是NAT模式且已连接。再回到Ubuntu里看一眼IP是否变了。
- 如果ping能通但SSH连不上,执行
Test-NetConnection 192.168.xxx.xxx -Port 22,Windows下这个命令可以快速判断22端口是否可达。
一个很容易踩的坑:虚拟机休眠或者VMware挂起后再恢复,IP可能变了。每次连不上,先重新
ip addr确认一遍IP,再把config里的HostName改掉。为了从根上解决这个问题,我后面会在进阶部分说固定IP的方法。
4.2 Permission denied (publickey,password):用户名和密码验证
这个报错说明网络和端口都通,但SSH身份验证没过。先看报错里的细节:
Permission denied, please try again.:通常是密码错了,或者用户名写错了。Permission denied (publickey,password).:SSH服务器拒绝了基于密码的登录,只允许密钥登录。
查三处:
- 确认config里的
User字段。注意,不是root就能无障碍登录,很多Ubuntu默认禁止root远程登录,要用普通用户名。 - 确认密码是否正确。Ubuntu安装时设置的用户密码,不是某些工具里显示的动态密码。
- 检查远程的sshd配置。执行:
sudo grep -E "PasswordAuthentication|PermitRootLogin" /etc/ssh/sshd_config正常应该看到PasswordAuthentication yes。如果看到的是no或这一行被注释掉了,就用sudo nano /etc/ssh/sshd_config打开,取消注释并改成yes,然后执行sudo systemctl restart ssh重启服务。
4.3 连接后立刻退出或反复要求密码
这种症状是:密码输对了,连接建立起来了,但两秒后自动断开,或者要求重新输密码。这类问题,九成出在远程家目录的权限上。
SSH服务端对权限非常敏感:如果~/.ssh或~/.ssh/authorized_keys权限过于开放,会被直接拒绝。执行下面两条命令修正:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys如果你压根没建过.ssh目录,则先创建再设置权限:
mkdir -p ~/.ssh chmod 700 ~/.ssh touch ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys另一类常见原因是Windows侧的known_hosts里记录了同一个IP但不同的指纹。比如Ubuntu重装系统后,SSH host key变了,Windows还保留着旧记录,连接就会直接报REMOTE HOST IDENTIFICATION HAS CHANGED。解决办法是找到C:\Users\你的用户名\.ssh\known_hosts,删除对应IP的那一行,重新连接。
4.4 "此扩展在此工作区中被禁用,因为其被定义为在远程扩展主机中运行"
这个提示很搞心态,我初次遇到的时候也懵了。它的本质是:VS Code的扩展分两类,一类是本地UI扩展(比如主题、快捷键增强),一类是需要在远程主机上运行的扩展(比如Python语言服务、VS Code Server端插件)。Remote-SSH会自动决定每个扩展应该装在哪一侧。
当你看到"被定义为在远程扩展主机中运行"的禁用提示,通常是因为你在本地安装了某个扩展,但它的manifest里声明了extensionKind: ["remote"],导致VSCode自动把它标记为"远程扩展",而你又没有连接到远程,于是被禁用。
解决办法很简单:
- 连接远程成功后再安装这个扩展,选择
Install in SSH: ubuntu-vm。 - 已装在本地但被禁用的,在扩展面板里找到它,点击齿轮,选择
设置为远程扩展,或者干脆点禁用,然后重新连接远程再启用。
这个问答在官方issue里出现过很多次,其实不是bug,是设计的扩展归类机制,理解了就不慌。
4.5 其他高频报错速查表
我整理了一个表,覆盖了我试过Remote-SSH以来遇到的其他高频问题。
| 报错或现象 | 主要原因 | 先试的解法 |
|---|---|---|
Could not establish connection to "xxx" | 网络不通或host配置错误 | ping宿主机、验证config文件 |
Port 22: Connection timed out | 防火墙或端口未监听 | 检查ss -tlnp、ufw status |
REMOTE HOST IDENTIFICATION HAS CHANGED | known_hosts记录旧的host key | 删除known_hosts中对应行 |
vscode-server: 下载失败/超时 | 远程~/.vscode-server损坏或网络问题 | 删除~/.vscode-server后重新连接 |
| 远程终端中文乱码 | 远程locale未设置 | 执行export LANG=en_US.UTF-8并写入~/.bashrc |
Bad owner or permissions on ~/.ssh/config | Windows下config权限过大 | 右键文件属性-安全-完全控制只留当前用户 |
| 连接成功但打开目录失败 | 远程目录权限不够 | 使用有权限的用户或chmod |
排查问题的总体原则:从链路底层往上查。网络层不通就别折腾SSH,SSH服务没起来就别怪VSCode,VSCode能连上但扩展报错再去查扩展。按照这个顺序来,绝大多数情况都能快速定位。
5. 连上之后怎么把体验拉满:免密、转发、开发习惯
跑通是第一步,真正好用的是把后续体验优化到位。这一节我选了三个投入产出比最高的优化点。
5.1 SSH密钥免密登录:省掉每次输密码
每次连接都输密码,次数多了就烦。SSH密钥登录是一次配置、永久受益的操作。原理很简单:把本地生成的一把公钥放到远程主机的~/.ssh/authorized_keys里,之后本地发起SSH请求时,远程用公钥验证你的私钥,密码环节就省了。
在Windows的PowerShell或CMD里执行:
ssh-keygen -t ed25519 -C "windows-local"一路回车即可,默认保存到C:\Users\你的用户名\.ssh\id_ed25519。然后执行:
type C:\Users\你的用户名\.ssh\id_ed25519.pub | ssh ubuntu-vm "cat >> ~/.ssh/authorized_keys"这条命令的意思是把本地的公钥内容追加到远程的授权列表中。执行完会要求输入一次密码,之后就永久免密了。验证方式是直接执行ssh ubuntu-vm,不再需要密码。
提示:免密登录后,VSCode的Remote-SSH也会自动继承这个能力,连接时不会再弹密码框,体验接近本地开发。注意备份好私钥文件,它就是你的登录凭证,丢了没法通过远程找回。
5.2 端口转发:在Windows浏览器里打开虚拟机里的服务
连接成功只是开始。你在Ubuntu里跑一个Web服务、Jupyter Notebook,或者调试一个后端API,总不能每次都去看虚拟机IP加端口吧。Remote-SSH内置了端口转发功能,可以直接把远程端口"搬到"本地。
最常见的场景:Ubuntu里启动一个开发服务器,端口是8000,希望在Windows浏览器里直接访问http://localhost:8000。
在已连接的VSCode窗口里,找到"端口"面板(Ports),点击端口输入框,填8000,回车。VSCode会自动创建转发。接着在Windows浏览器打开http://localhost:8000,就能访问到远程服务了。
另一种更持久的方式是写进SSH Config里:
Host ubuntu-vm HostName 192.168.xxx.xxx User yourname Port 22 LocalForward 8000 localhost:8000这样每次连接都会自动把远程8000端口映射到本地8000端口。
5.3 把虚拟机当成开发机的三条建议
真正把Remote-SSH用起来之后,会有种"Windows是我的编辑器,Ubuntu是我的编译器"的感觉。给你三个我用下来觉得很值的配置建议。
第一,先拍快照再乱动。VMware支持虚拟机快照,所有配置操作、系统更新之前,先拍个快照,出问题了秒回。这是远程开发最大的安全感来源。
第二,固定虚拟机IP。DHCP虽然省事,但IP变了就得改config。最简单的方法是在路由器里给虚拟机MAC地址绑定固定IP,或者在Ubuntu里手动配置Netplan。对纯内部开发,我建议直接编辑/etc/netplan/下的yaml文件,把dhcp4: no并配置静态IP,改完sudo netplan apply生效。
第三,日常开发优先走Remote-SSH。很多情况下不需要打开VMware窗口,VSCode远程连接后台虚拟机就能完成开发。VMware窗口可以最小化甚至让虚拟机后台运行,节省宿主机资源。如果仅需要命令行,也可以直接用ssh ubuntu-vm登录终端。
最后再分享一个我自己的使用体会。远程连接受网络、SSH服务、密钥、VSCode四个层面的共同影响,出问题时不要慌,按照"网络层 → 服务层 → 配置层 → 应用层"这个顺序排查,大部分问题十分钟内都能定位。很多人把它想复杂了,其实链路并不长,关键是每一步都要有验证意识。真到了连串行流程都跑通的那天,你会发现,敲代码的最佳姿势就是在Windows里开着VSCode,而所有的运行和调试都在那台安静的Ubuntu虚拟机里完成。这个配置值得花半小时搞定,因为之后每天写代码都在享受它。