ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

VSCode 连接 Ubuntu:WSL 与 Remote-SSH 配置排错

VSCode 连接 Ubuntu:WSL 与 Remote-SSH 配置排错 1. 先把连接这个词拆开三种形态选错了后面全是白费劲很多人张口就是VSCode 连 Ubuntu但这句话在实践里至少对应三种完全不同的形态选错分支之后后面所有配置都会变成在错误的路上使劲。我自己第一次折腾的时候就因为在虚拟机上乱挂共享目录导致 CMake 每次全量重扫要等两分钟换成 Remote-SSH 之后同一份工程增量构建从 90 秒掉到 6 秒那一刻才真正明白选对连接方式比调参优化重要得多。第一种是WSLUbuntu 以子系统形式跑在 Windows 上和宿主共用内核与内存。装完之后在 Ubuntu 终端里敲code .VSCode 会自动以已连接 WSL的形式打开本质上是 VSCode 在 Windows 上跑界面、在 WSL 里跑服务端。它最大的好处是零网络配置、文件系统直通、可以用 Windows 的显卡和输入法。代价是它不是一台真正的机器内核模块、systemd 的部分能力、底层网络行为都和独立系统有差异。第二种是Remote-SSHUbuntu 是一台独立的系统——可以是局域网里的物理机、可以是 VMware/VirtualBox 里的虚拟机、也可以是云上的实例。VSCode 通过 SSH 通道把一个小服务端推过去之后编辑、搜索、终端、调试全部在 Ubuntu 那一侧执行Windows 只负责画界面。这种方式最贴近真实开发机的体验也是团队协作里最通用的做法。第三种是共享目录式虚拟机上配个共享文件夹Windows 侧用 VSCode 打开那个目录。严格说这不算连接只是把文件映射过来了。它的致命问题在于跨文件系统的 IO 放大——Windows 打开一次文件虚拟机侧可能产生几十次元数据查询node_modules或者大型 C 工程在这种模式下编辑器索引会慢到让人怀疑人生。1.1 三种形态到底差在哪维度WSLRemote-SSH共享目录交互延迟极低取决于网络局域网内几乎无感低大项目文件IO快放在 Linux 文件系统内快很慢是否需要网络配置不需要需要IP/端口/防火墙都要通不需要环境独立性与宿主共用内核完全独立完全独立能否跑底层内核相关工具部分受限完全支持完全支持适合场景日常脚本、Python、Web 前端编译型语言、嵌入式、需要真机环境临时看一眼文件这张表不是让你背而是让你在动手前先问自己一个问题我要跑的东西依赖内核模块吗依赖真实硬件吗如果答案是否定的WSL 是成本最低的选择如果答案是肯定的直接上 Remote-SSH不要试图用共享目录绕过去。1.2 一个容易踩的认知坑双系统连不了我装了双系统和我用 VSCode 连 Ubuntu这两件事存在天然冲突。双系统的意思是同一时刻只有一套系统在运行Ubuntu 没启动的时候SSH 服务根本不存在自然连不上。所以如果你的目标是让 Windows 上的 VSCode 随时连过去写代码正确做法是虚拟机常驻或者另有一台常开的机器而不是双系统。提示虚拟机场景下网络模式选桥接最省心虚拟机会像一台独立设备一样拿到局域网 IP选 NAT 的话必须在虚拟机软件里做端口转发多一层配置就多一个出错点。2. Ubuntu 这一端要先把门打开从网络拓扑到 sshd 可用VSCode 连不上九成的问题不在 VSCode而在 Ubuntu 侧的门没开或者开错了位置。我在帮同事排查这类问题时几乎从来不先看 VSCode 的报错窗口而是先在 Ubuntu 上执行三条命令把问题范围一刀切开。2.1 先在 Ubuntu 上确认我在哪、我叫什么打开 Ubuntu 的终端依次执行ip -4 addr show | grep inet hostname -I第一条会列出所有网卡的 IPv4 地址。你要关注的是那个看起来像192.168.x.x或10.x.x.x的地址127.0.0.1是你自己别人连不进来。第二条只打印地址输出更干净适合直接复制。如果是虚拟机还要额外确认一件事虚拟机的网络模式和你拿到的 IP 段是否自洽。桥接模式下虚拟机拿到的地址应该和宿主机在同一个网段NAT 模式下虚拟机通常拿到192.168.56.x这类内部段宿主机访问它需要端口转发规则而不是直接连 IP。2.2 安装并检查 SSH 服务Ubuntu 桌面版默认不装SSH 服务端这一点非常容易忽略——很多人以为是防火墙问题其实根本没有服务在监听。sudo apt update sudo apt install -y openssh-server systemctl status ssh看到active (running)才算起步。如果状态是failed先看journalctl -u ssh -n 50的尾部输出常见原因是 22 端口被别的进程占用或者配置文件里写了非法指令。确认监听端口ss -tlnp | grep sshd输出里应该能看到0.0.0.0:22或者*:22。如果只看到127.0.0.1:22说明配置里绑定了回环地址外部连接一定会被拒需要去/etc/ssh/sshd_config检查ListenAddress这一项。防火墙方面Ubuntu 默认的 ufw 一般是关闭状态如果你手动开过记得放行sudo ufw status sudo ufw allow 22/tcp2.3 把密码登录换成密钥登录密码登录能用但每次连接都要输、脚本里不好自动化、而且密码强度一旦不够就是隐患。密钥登录配一次后面几年都省事。在 Windows 侧用 PowerShell 或 Git Bash执行ssh-keygen -t ed25519 -C devworkstation一路回车默认生成在C:\Users\你的用户名\.ssh\下得到id_ed25519私钥和id_ed25519.pub公钥。私钥不要发给任何人也不要复制到 Ubuntu 上这是最容易搞混的一点。把公钥送过去Ubuntu 侧只需要一条命令mkdir -p ~/.ssh chmod 700 ~/.ssh echo 把公钥内容粘到这里 ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keyschmod这两条不是可选项SSH 对权限极其敏感~/.ssh是 755 或者authorized_keys是 644 的时候服务端会直接忽略这个文件表现就是公钥明明放进去了却还要密码。这个坑我见过太多次检查权限应该是排查密钥失效的第一步。确认密钥可用之后可以关掉密码登录# /etc/ssh/sshd_config PasswordAuthentication no PubkeyAuthentication yes PermitRootLogin no改完执行sudo systemctl restart ssh。注意改配置之前务必先开另一个终端验证密钥能登录否则一旦配置写错你可能连回去改的机会都没有。2.4 开发环境还需要提前做的三件小事第一件是提高文件监听上限。VSCode 的文件监听、热重载、很多构建工具都依赖 inotify默认值在稍大的工程里会直接爆掉cat /proc/sys/fs/inotify/max_user_watches # 偏小的话 echo fs.inotify.max_user_watches524288 | sudo tee -a /etc/sysctl.conf sudo sysctl -p第二件是给用户加到需要的组里。比如要用 Docker 却不加组每次都得 sudo而 VSCode 的远程终端里 sudo 交互很别扭sudo usermod -aG docker $USER加完组必须重新登录关掉所有 SSH 会话重连否则组信息不会刷新很多人改完发现没生效就是这一步漏了。第三件是看一眼磁盘空间。df -h如果根分区快满了VSCode 的服务端解压会失败报错却往往显示成连接超时方向完全跑偏。留出至少 2~3 GB 比较保险。3. VSCode 这一端插件装哪些、config 怎么写、第一次握手发生了什么Ubuntu 那边门开了接下来才是 VSCode 的活儿。这一章的重点不是点哪个按钮而是理解 VSCode 远程架构的分层理解之后很多诡异现象自己就能解释。3.1 插件清单装三个就够多装反而是负担必装的是Remote - SSH连独立机器、WSL连子系统。微软有个Remote Development扩展包把 SSH、WSL、容器三种能力打包在一起图省事可以直接装它代价是拖进来几个你可能永远用不到的东西。其他插件不要在本地一股脑装完再去连远程。原因在 3.4 会讲清楚。有一个例外中文语言包。它属于 UI 层插件装在本地就能让界面变中文。但如果你想让远程的终端提示、任务输出也是中文需要在远程侧再装一次对应的语言包两边是独立的。3.2 把 ~/.ssh/config 写对后面的路会顺很多大部分人第一次连是直接敲ssh user192.168.1.100然后每次都要回忆 IP、用户名、端口。正确做法是在 Windows 侧C:\Users\你的用户名\.ssh\config里定义别名Host dev-ubuntu HostName 192.168.1.100 User yourname Port 22 IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 30 ServerAliveCountMax 6 ControlMaster auto ControlPath ~/.ssh/cm-%r%h:%p ControlPersist 10m几个关键项值得单独说。ServerAliveInterval 30表示每 30 秒发一次心跳配合ServerAliveCountMax 6可以在网络抖动或者路由表刷新时让会话多撑几分钟而不是立刻断。ControlMaster这一段是连接复用VSCode 连远程时会开多条 SSH 通道终端、文件同步、端口转发各自独立不复用的话你可能看到十几个 ssh 进程复用了之后底层只有一条 TCP重连和断线恢复都明显更快。写完 config 之后验证一步ssh dev-ubuntu能免密进去说明网络、密钥、配置三层都通了。这一步没通之前不要打开 VSCode否则你会在 VSCode 的一堆日志里找问题效率极低。3.3 第一次连接时VSCode 在后台干了什么点连接到主机之后会依次发生这些事建立 SSH 通道 → 检测远程架构x86_64/aarch64→ 检查~/.vscode-server是否存在 → 不存在则把服务端压缩包传过去解压 → 启动远程扩展宿主进程 → 加载工作区。理解这个流程的价值在于卡在不同的阶段问题是完全不同的。卡在正在下载 VS Code 服务器通常是网络或磁盘问题卡在正在打开远程连接多半是服务端进程起不来连上之后窗口一片空白往往是远程扩展宿主崩溃或者用户主目录配额满了。服务端落在~/.vscode-server/下面里面按 commit id 分目录。这个目录会随着 VSCode 升级不断累积旧版本用一段时间后可以清理掉不在用的那些能腾出不少空间。3.4 本地插件和远程插件是两套这是最容易踩的坑VSCode 在远程模式下把扩展分成两类UI 扩展跑在本地工作区扩展跑在远程。你装了一个 C/C 插件它需要在远程侧运行才能解析远程的include路径你装了一个主题插件它跑在本地就够了。于是就有了那个经典现象明明装了插件为什么功能不生效。答案通常是插件装在了本地而当前窗口是远程窗口VSCode 会把它标记为需要在远程安装。正确做法是打开扩展面板切到对应分类看一眼需要远程的就在远程装。这里的实用建议是能装远程就装远程。原因是插件在本地运行、要频繁访问远程文件时每一次操作都要走 SSH 通道延迟叠加起来非常难受。远程安装则是在服务端本地读文件快得不是一个量级。4. 连上只是开始编译、调试、输入法三件套怎么配窗口右下角显示SSH: dev-ubuntu的那一刻很多人以为大功告成其实真正的配置工作才刚开始。远程窗口里跑的是 Ubuntu 的环境而 Ubuntu 上有什么工具链取决于你刚才装了多少东西。4.1 工具链一次性补齐别边写边装基础组合我一般这么装sudo apt install -y build-essential gdb cmake ninja-build pkg-config git curlbuild-essential会把gcc、g、make、libc头文件一起带上。单独装gcc有时会遇到安装失败或者装完编译还报找不到头文件就是因为缺了libc6-dev这一类依赖直接装build-essential省事得多。如果apt install报错按这个顺序看先看是不是有另一个 apt 进程在跑ps aux | grep apt再sudo dpkg --configure -a修一下中断的安装最后看df -h磁盘是不是满了。这三个原因能覆盖绝大多数安装失败。CMake 版本是另一个高频问题。Ubuntu 仓库里的 CMake 往往偏旧而新一点的项目会要求 3.20 以上。检查版本cmake --version升级有两条路一是用 pip 装适合个人开发环境pip install --user cmake二是加 Kitware 的官方源。pip 那条路更干净出问题直接删掉包目录就行不会污染系统。4.2 C/C 的远程调试三个文件决定成败远程 C 调试需要.vscode/下的三个文件配合c_cpp_properties.json管代码跳转和补全tasks.json管怎么编译launch.json管怎么启动调试器。很多人的问题出在这三个文件里写的路径是 Windows 风格的——C:\Users\...在远程 Linux 上根本不存在。c_cpp_properties.json关键在compilerPath要指向远程的编译器{ configurations: [ { name: Linux, compilerPath: /usr/bin/g, cStandard: c17, cppStandard: c17, intelliSenseMode: linux-gcc-x64, compileCommands: ${workspaceFolder}/build/compile_commands.json } ] }最省事的办法其实是最后那行compileCommands让 CMake 生成compile_commands.json插件直接读它头文件路径、宏定义、编译选项全都自动同步不用手写一堆includePath。CMake 生成这个文件只要在CMakeLists.txt里加一句set(CMAKE_EXPORT_COMPILE_COMMANDS ON)调试用的launch.json里program路径必须是 Linux 路径通常是${workspaceFolder}/build/你的可执行文件MIMode设为gdbmiDebuggerPath写/usr/bin/gdb。如果启动时报找不到 gdb说明远程没装回去执行 4.1 那条 apt 命令。注意调试器的路径一定要用which gdb的输出确认别凭记忆写。Ubuntu 里 gdb 装在/usr/bin/gdb是常态但用 snap 或者自己编译过的情况就未必。4.3 Python 解释器选错是最隐蔽的坑Python 项目在远程模式下最容易出现的问题是终端里pip list有某个包但 VSCode 里导入还是标红。原因通常是解释器不一致——VSCode 用了系统 python终端里用的却是 conda 或者是虚拟环境。处理办法是显式指定。打开命令面板选Python: Select Interpreter然后手动填路径不要只在列表里挑名字。虚拟环境的解释器路径长这样/home/yourname/projects/demo/.venv/bin/python判断当前 VSCode 用的是哪个解释器看一眼状态栏左下角就行。装依赖的时候也用这个解释器的绝对路径去装/home/yourname/projects/demo/.venv/bin/pip install -r requirements.txt这样能保证装的位置和用的位置永远是同一个避免我明明装了的扯皮。4.4 中文输入和界面语言远程窗口里能不能打中文取决于两个层面输入法框架装在 Ubuntu 上输入法候选框由本地绘制。Ubuntu 侧装 fcitx5 加拼音输入法sudo apt install -y fcitx5 fcitx5-chinese-addons装完需要配置环境变量让 GTK 和 Qt 程序知道用哪个输入法框架。这几个变量写在~/.profile或者~/.pam_environment里GTK_IM_MODULEfcitx QT_IM_MODULEfcitx XMODIFIERSimfcitx改完注销重新登录生效。这里有个细节环境变量写在~/.bashrc里对图形程序无效。~/.bashrc只在交互式 shell 里被读图形会话启动的程序根本不会加载它。这就是终端里echo $GTK_IM_MODULE有值但输入法还是不好用的根本原因。界面语言方面装中文语言包之后如果没生效检查一个设置项Configure Display Language是否选了zh-cn。这个设置是按窗口类型分开的本地窗口和远程窗口各自记一份所以可能出现本地是中文、远程是英文的情况两个窗口各设一次就好。4.5 环境变量在 VSCode 里不生效问题出在哪这是远程开发里最让人抓狂的一类问题终端里echo $PATH明明包含了~/bin可是 VSCode 的任务、调试器却找不到那个可执行文件。根因在于VSCode 的远程服务端不是由交互式 shell 启动的。它通过 SSH 的非交互通道启动只加载~/.profile或者 shell 的非交互配置不读~/.bashrc。所以你在.bashrc里加的那些export对终端面板有效终端会启动交互式 shell对任务和调试进程无效。最干净的解法是把路径类的配置放进~/.profile然后用一条兼容写法兜底# ~/.profile export PATH$HOME/.local/bin:$HOME/bin:$PATH如果你不想动系统文件也可以在项目的.vscode/settings.json里给终端显式注入{ terminal.integrated.env.linux: { PATH: /home/yourname/.local/bin:${env:PATH} } }两种方式我都在用前者适合全局工具后者适合只对某个项目生效的临时配置。5. 连不上的完整排查链路从超时到服务端起不来这一章我按真实排查顺序写你可以照着一步步走。原则是分层验证先证明网络通再证明 SSH 通最后才怀疑 VSCode。5.1 第一层网络到底通不通从 Windows 侧执行ping 192.168.1.100如果 ping 不通先别碰 SSH。检查虚拟机网络模式、IP 是否变了DHCP 租约到期换 IP 是很常见的、Windows 防火墙是否把出站拦了。虚拟机上如果用的是 NATping 本来就不一定通这时直接跳到下一层用端口测试。# Windows PowerShell Test-NetConnection 192.168.1.100 -Port 22TcpTestSucceeded : True说明端口可达。这一步比 ping 更有说服力因为 ICMP 经常被策略拦掉但 TCP 22 通不通才是真正决定能不能连的。5.2 第二层SSH 本身能不能过ssh -vvv dev-ubuntu-vvv会打印完整的握手过程。看三个关键位置Connecting to ...后面有没有往下走说明 TCP 建起来了Authentications that can continue列出的是什么能看出服务端允许哪些认证方式Offering public key之后有没有Server accepts key能看出密钥是否被接受。如果卡在Offering public key然后失败八成是authorized_keys权限不对回到 2.3 节检查。5.3 第三层VSCode 特有的问题到这里 SSH 已经能通了但 VSCode 还是连不上那就只剩服务端的事。打开命令面板执行Remote-SSH: Show Log看远程连接日志的尾部。症状可能原因处理方式卡在正在下载 VS Code 服务器目标机访问不了下载源或磁盘不足确认磁盘空间必要时手动放置服务端包报Failed to parse remote port远程 shell 输出了额外内容干扰解析检查~/.bashrc里有没有echo打印连上后扩展全部需要重装服务端 commit 目录变了属正常现象重新安装即可终端能开但窗口空白远程扩展宿主崩溃删除对应版本的~/.vscode-server目录重连频繁掉线网络空闲被回收加ServerAliveInterval第三行那个shell 输出干扰值得展开说一句如果你在~/.bashrc里写了echo 欢迎登录或者跑了个会打印 banner 的脚本VSCode 解析远程主机信息时会把这些输出当成数据直接解析失败。所有在非交互场景下会打印内容的配置都该用case $- in *i*)包起来只在交互式 shell 里执行。5.4 内网和离线环境怎么处理有些开发机在隔离网络里出不去外网VSCode 服务端下载会一直转圈。思路是在能上网的机器上拿到服务端包再传进目标机。VSCode 的远程服务端包可以从官方发布的地址按 commit id 下载你的 VSCode 版本对应的 commit id 可以在帮助 → 关于里看到。拿到包之后在目标机上解压到~/.vscode-server/bin/commit-id/下面确保目录结构里直接是bin、node、out这些内容而不是多套了一层目录。放好之后重连VSCode 会在本地校验通常就直接跳过了下载步骤。另一种更省事的做法是在离线环境里预先准备好一台模板机把~/.vscode-server和工具链都装好之后克隆虚拟机或者拷用户目录即可比每次重新走一遍下载流程靠谱得多。6. 用久了才明白的几个细节能省下大量来回折腾的时间前面五章是从零到连上这一章是连上之后怎么用得舒服。这些点很少有人一开始就讲但每一个都能在长期使用里省下不少时间。6.1 端口转发本地浏览器直接看远程服务远程跑了个 Web 服务监听 8080你没必在 Ubuntu 上开浏览器。VSCode 的端口面板可以自动发现监听端口也可以手动添加转发。添加之后本地浏览器访问localhost:8080就直达远程服务。命令行侧不想开面板的话直接在 config 里预置Host dev-ubuntu ... LocalForward 8080 127.0.0.1:8080这条规则在连接建立时就生效适合那些每次都要用的固定服务。6.2 工作区文件与多窗口频繁切换的项目建议用.code-workspace工作区文件把多个目录打包在一起打开比如同时打开后端仓库、前端仓库和一份文档目录。远程模式下工作区文件也保存在远程换台电脑连过去配置跟着走。多窗口方面要注意一点同一个远程主机可以开多个窗口但每个窗口会各起一套扩展宿主进程内存占用是按窗口叠加的。机器内存不大的话同时开三四个远程窗口再加编译很容易把内存吃满表现出来就是编辑器莫名其妙变卡。6.3 长任务的断线处理编译、跑测试这类长任务最怕断线。两个实用做法一是把重要任务放到tmux或者nohup里跑VSCode 断不断都不影响二是把 SSH 的心跳参数配好3.2 节里的ServerAliveInterval大部分短暂抖动都能扛过去。任务跑完再重连的情况下用tmux attach回到会话里看完整输出比在 VSCode 终端里翻历史记录方便得多。我现在的习惯是超过五分钟的任务一律进 tmuxVSCode 终端只用来做交互式操作。最后分享一个我自己踩出来的经验远程开发环境的配置文件.ssh/config、~/.vscode-server的清理脚本、工具链的安装脚本都值得单独存一份用 Git 管理起来。换机器或者重装系统的时候把这些脚本跑一遍二十分钟就能恢复到熟悉的状态靠记忆一条条敲往往要折腾一整天还容易漏掉某个当时觉得以后再说的细节。
返回列表