如果你最近也打算把日常开发环境从Windows物理机迁到一台AlmaLinux虚拟机上,然后用VSCode通过SSH远程连接来做代码编写和调试,那你大概率会遇到我这一周踩过的那些坑。VSCode Remote-SSH本身是个成熟方案,但一旦和虚拟机、AlmaLinux这两个变量叠加在一起,网络不通、端口未监听、主机密钥不匹配、vscode-server装不上,哪一个都能让连接卡死在“正在加密远程连接”上,折腾几个小时找不到方向。
这篇文章不是官方文档的翻译,也不是什么高深原理剖析,就是我实际连接AlmaLinux虚拟机时的一套排查路径。我尽量按照从下往上、从网络层到应用层的顺序来讲,哪些是启动服务前就该配好的,哪些是连不上的时候该查的,哪些是连上之后还会反复掉线的,我都会写出来。如果你正在对着VSCode报错一头雾水,可以直接按章节对号入座。
1. 我为什么偏要用虚拟机跑AlmaLinux来做远程开发
先交代一下背景。之前我的开发环境一直是Windows物理机加WSL2,WSL2用起来虽然方便,但有些编译依赖、系统库的坑和真实Linux服务器还是存在差异。我手头有几台老旧的物理服务器,又不想直接重装成Linux主机,于是决定在VMware Workstation里开一台AlmaLinux虚拟机,作为远程开发的目标机。
1.1 CentOS停更之后,AlmaLinux成了最省心的替代
选AlmaLinux而不是其他发行版,纯粹是因为兼容性。AlmaLinux是RHEL的二进制兼容发行版,CentOS停更之后,很多生产环境都切到了它,9.x的软件源和企业生态很完整。用它模拟服务器环境,后面部署到生产机器上的心态会稳很多。你也可以用Rocky Linux,操作上几乎没差别,但我在实际使用中感觉AlmaLinux的社区文档和云镜像更全,特别是和VMware结合时,open-vm-tools的兼容性明显更好。
从远程开发的角度说,AlmaLinux默认的sshd配置、SELinux策略、firewalld规则和CentOS一脉相承,网上关于CentOS的排错经验大多数可以照搬。如果你之前折腾过CentOS,那AlmaLinux几乎不需要学习成本。
1.2 虚拟机的网络模式决定了你后面少踩一半的坑
这一步特别重要,但很多人一开始就忽略了。VMware虚拟机有三种常见网络模式:桥接模式、NAT模式、仅主机模式。做VSCode远程开发,我强烈建议优先使用桥接模式,让虚拟机和宿主机处在同一个局域网网段,直接从路由器拿IP。
如果用NAT模式,虚拟机和外网通信没问题,但宿主机访问虚拟机要走VMnet8网段的地址,虽然也能通,但配置端口转发、多台机器切换、防火墙辅助策略时会多出不少事。我最开始图省事选了NAT,结果虚拟机重启后IP经常变,VSCode配置里的Host地址跟着改,连接稳定性大打折扣。后来改成桥接,固定IP之后,Remote-SSH的配置基本不用再动。
安装AlmaLinux过程中,网络默认可能是DHCP,装完系统后我建议第一时间把IP改成静态,用的网卡名称可能是ens160或ens192,可以通过nmcli或者修改/etc/NetworkManager/system-connections/里的配置文件来设置。改完之后记得重启网络服务,或者用nmcli connection reload让配置生效。
2. 环境准备:把这几个基础项打牢,后面才不折腾
远程连接VSCode,本质上是VSCode把客户端和服务端通过SSH连接起来,服务端会运行一个vscode-server,所有编辑、索引、终端操作都由它来处理。所以虚拟机侧的基础环境没搭好,VSCode再聪明也没用。
2.1 虚拟机里必须开启的基础服务
AlmaLinux最小化安装默认不会装图形界面,也不会主动开启SSH服务。这一点和Ubuntu的桌面版不太一样,Ubuntu安装时能勾选“OpenSSH server”,AlmaLinux在字符交互的安装界面里也有一个“Server with GUI”或“Minimal Install”的选项,但不少人选完最小化后就把这事忘了。
连接之前先确认三样东西:openssh-server、ssh服务状态、防火墙放行规则。
# 安装openssh-server sudo dnf install -y openssh-server # 启动并设置开机自启 sudo systemctl enable --now sshd # 查看ssh服务状态 sudo systemctl status sshd如果状态显示active,说明sshd在运行。接着确认防火墙:
sudo systemctl status firewalld sudo firewall-cmd --add-service=ssh --permanent sudo firewall-cmd --reloadfirewalld是AlmaLinux默认防火墙,哪怕你在安装时没启用它,也建议确认一下,因为有些镜像模板里防火墙是开着但没放行22端口。你可以在宿主机上用telnet或者PowerShell的Test-NetConnection来测一下端口,见后面排查章节。
2.2 sshd配置里那些容易默认值坑人的参数
正常情况下,/etc/ssh/sshd_config的默认配置能直接用,但有几个参数在远程连接场景下特别容易出问题。
PasswordAuthentication:默认是yes,如果你在安装系统时设置了root密码,并且想用密码登录,没问题。但如果远程连接一直报“Permission denied”,先检查这里。PermitRootLogin:默认RHEL系是prohibit-password,意思是不允许通过密码直接root登录,只允许密钥登录。如果你用root加密码想连,会被拒绝。要么改为yes,要么建一个普通用户再用sudo。AllowUsers:有些安全加固的镜像会加上这个限制参数,如果远程用户不在列表里,连端口都握手不上,日志里显示“Connection closed by ... preauth”。UsePAM:默认开启。如果你改了密码后一直认证失败,PAM那边可能会出现服务错误,可以看journalctl -u sshd的日志。
我实际遇到过一次,虚拟机刚装完,用Windows上的VSCode连接时反复要密码,输入后直接提示Permission denied, please try again。后来查日志发现是PermitRootLogin默认禁止root密码登录,而我又一直用的是root账号。切换成普通用户,或者修改配置后重启sshd,问题才解决。
2.3 宿主机VSCode侧的准备
宿主机上需要安装Remote-SSH扩展,这一步没有太多技术含量。但有一个地方容易被忽略:VSCode会调用系统的ssh命令。在Windows上,如果你的VSCode是便携版或者没有安装OpenSSH客户端,Remote-SSH可能找不到可用的SSH命令。
Windows 10以上一般自带OpenSSH Client,可以在设置里确认一下。如果没有,去“Windows功能”里勾选“OpenSSH客户端”,或者用Git自带的ssh路径。装了某些“绿色版VSCode”的朋友需要注意,Remote-SSH插件会去找ssh.exe的路径,环境变量PATH里必须有。
确认方式很简单,打开cmd输入:
ssh -V能看到OpenSSH版本号就行。如果这个命令都找不到,VSCode远程连接基本必挂。
3. 连接失败的逐层排查:从ping不通到无法建立连接
VSCode远程连接的过程可以拆成几步:网络连通 -> 端口可达 -> SSH握手 -> 服务端启动vscode-server -> 客户端与服务端建立通信。任何一步失败,表现都不一样。我习惯自下而上排查。
3.1 第一层:网络通不通
先在物理机上ping虚拟机的IP。如果ping不通,看一下虚拟机是不是在桥接网络下没拿到IP,或者宿主机防火墙拦了ICMP(很少见)。最常见的还是IP地址不在同一网段。
# 在虚拟机上查看自己的IP ip addr show如果虚拟机和宿主机都在192.168.1.x网段,一般直接能通。如果虚拟机是NAT模式的192.168.x.x,宿主机是物理网络的192.168.1.x,那我建议先确认VMware虚拟网卡是否处于启用状态。
还有一个小技巧:从虚拟机ping宿主机,能通说明虚拟机网络出得去;从宿主机ping虚拟机,能通则说明双向都通。VSCode Remote-SSH只需要宿主机到虚拟机方向能通,所以后一个方向才是重点。
3.2 第二层:端口在不在监听
网络通了之后,测试22端口是否开放。Windows PowerShell里可以用:
Test-NetConnection 192.168.1.10 -Port 22如果TcpTestSucceeded为False,基本可以断定要么ssh服务没起,要么防火墙拦截,要么端口被改过。在虚拟机上执行ss -tlnp | grep 22看看监听状态,再确认firewalld里是否真的放行了。有一类坑是:你用了firewall-cmd --add-port=22/tcp,但AlmaLinux默认zone可能不是public,规则加到了别的zone,结果没生效。可以加--zone=public显式指定,或者在防火墙设置图形工具里确认。
3.3 第三层:加密协商与主机密钥
端口通了,但连接时提示“远程主机标识已更改”或者“Host key verification failed”,这是主机密钥问题。常见于虚拟机重装过系统,但VSCode的known_hosts里还存着旧指纹。
解决方法很简单:打开C:\Users\你的用户名\.ssh\known_hosts,找到虚拟机IP对应的行删掉,重新连接时会再次询问是否信任,选择yes即可。
另外,如果虚拟机的SSH版本比较老,而Windows端的ssh为了安全默认禁用了一些加密算法,会提示“no matching key exchange method found”。解决方案有两个:升级虚拟机里的openssh-server,或者在~/.ssh/config里为这个Host额外指定算法。后一种方法我一般不推荐,治标不治本,但还是给个示例:
Host alma HostName 192.168.1.10 User dev KexAlgorithms +diffie-hellman-group14-sha256注意这行是临时规避,最好还是用dnf update openssh-server升级到新版本。
3.4 第四层:VSCode的Remote-SSH日志怎么看
前三层都正常,VSCode仍然连接失败,那就要看VSCode自己的日志。千万别凭感觉瞎猜。日志打开方式:点击VSCode左下角的远程连接图标,选择“Remote-SSH: Open SSH Host”;或在命令面板里执行Remote-SSH: Show Log,选择“Session”或“Remote”标签。
日志里一般会打印SSH连接的详细过程和vscode-server的启动进度。很多人卡在“正在加密远程连接”这五个字,问题往往就藏在日志的尾部。
如果日志显示连接成功后马上断开,且远端返回process exited with code 137这种,那基本是虚拟机内存不足,vscode-server被杀掉了。如果是Permission denied,就是认证问题。如果是Connection reset by peer,可能是sshd配置里的ClientAliveInterval太短,或者防火墙规则在踢连接。
4. 最隐蔽的故障:卡在“正在加密远程连接”不动
这是整个过程中最让人抓狂的一个现象。SSH密码认证已经通过,输出窗口里还提示“下载VSCode服务端”,或者说“正在加密远程连接”,然后就一直停在那里,过一会儿报错“Failed to connect to the remote extension host server”。
4.1 故障现象
我那次故障表现为:连接按钮转圈大概十秒,窗口左下角显示“正在加密远程连接”,然后窗口日志停在某一行,过几十秒后弹出一个错误提示框。重新连接也一样。当时我以为是网络问题,反复重启虚拟机也没用。
4.2 排查过程
打开Remote-SSH日志,看到最后几行:
[debug] Remote server is listening on port ... [debug] ~/.vscode-server/.../server.sh ... [error] Error: Permission denied. open /home/user/.cache/...这就很明确了,不是加密协商失败,而是远程服务器的vscode-server在安装或启动时没有足够权限去访问缓存目录和临时目录。
继续查虚拟机上的磁盘和目录权限:
df -h发现根分区用了97%,vscode-server解压时需要大量临时空间,写到/tmp时直接失败。因为/tmp分区几乎满了,SSH虽然连上了,但服务端始终无法初始化,卡在加密阶段其实是前端没有及时把错误反馈出来。
还有一种类似情况是~/.vscode-server目录的所有者不是当前用户。如果以前用root启动过一次vscode-server,之后切换普通用户连接,目录权限就会冲突,表现为“Permission denied”。这时需要把旧的vscode-server目录删掉,或者把owner改成当前用户:
rm -rf ~/.vscode-server4.3 根因和修复方案
磁盘空间不足的修复很简单:清理/var/log下面的旧日志、dnf clean all、删除不必要的包。我最后从97%清到43%,VSCode立刻就能连上了。如果空间非常紧张但不想删数据,可以让vscode-server使用其他位置的目录,在~/.ssh/config里给目标Host加一行:
Host alma RemoteCommand code-server或者设置环境变量:
SetEnv VSCODE_SERVER_DATA_DIR=/home/dev/.vscode-data意思是让远程扩展宿主的数据目录放到其他位置,避免/tmp或家目录所在分区空间不足。但注意SetEnv需要sshd的AcceptEnv支持,AlmaLinux默认不一定允许所有变量,最好还是直接把磁盘清理干净。
事后我的反思是:既然要做远程开发,虚拟机的磁盘至少预留30GB,分区分成逻辑卷,别把空间全压在一个卷里。/home、/tmp、/var这些目录如果空间分配不合理,vscode-server这种频繁读写缓存的应用最敏感。
5. config文件与免密登录的坑:权限、编码和known_hosts
远程连接的下一步就是配置免密登录和别名,这能省掉每天输密码的麻烦。但这里面的坑非常多,而且几乎每个都是Windows和Linux的“文化差异”导致的。
5.1 一个能用的ssh config到底长什么样
示例:
Host alma HostName 192.168.1.10 User dev Port 22 IdentityFile C:\Users\dev\.ssh\id_ed25519 StrictHostKeyChecking noStrictHostKeyChecking no不建议长期使用,有安全隐患,但初次调试时能避免因known_hosts问题中断连接。正式使用还是建议保留默认的ask。
配置路径在Windows上是C:\Users\你的用户名\.ssh\config。注意这个文件名没有扩展名,不要保存成config.txt,VSCode的Remote-SSH只会认config。
5.2 私钥权限为什么那么严格
Windows上如果你把私钥放在当前用户的.ssh目录下,SSH并不会严格要求权限。但如果你是从别的目录复制过去的,或者从项目管理器直接引用了共享盘下的密钥,OpenSSH可能报“UNPROTECTED PRIVATE KEY FILE”错误。
解决办法是用PowerShell修复权限,而不是右键属性设置:
icacls C:\Users\dev\.ssh\id_ed25519 /inheritance:r /grant:r "$($env:USERNAME):R"这样只有当前用户有读取权限。如果私钥在Linux虚拟机里生成,然后拷贝到Windows,还要注意文件里的换行符和编码。私钥必须保持纯文本,不能在Windows上用记事本另存为带BOM的UTF-8。
5.3 Windows宿主机上的换行符陷阱
我用VSCode编辑config文件时,默认的CRLF换行会导致SSH解析出错,报一些莫名其妙的错误,比如“Bad configuration option”或“Bad owner or permissions”。解决办法是让VSCode把config文件的换行符改成LF。操作方式:点击状态栏右下角的“CRLF”字样,改为“LF”,或者安装EditorConfig插件统一管理。
同理,~/.ssh/known_hosts文件如果被无意义地加了CRLF,也可能导致“Host key verification failed”。这部分在图形界面上看不到,只能通过日志发现。一旦遇到SSH命令行能连接但VSCode连接失败,优先怀疑这些琐碎因素。
6. 连接稳定性和开发体验优化
等你能顺畅连上AlmaLinux虚拟机了,接下来就是如何保持连接稳定,以及让远程开发体验更接近本地。
6.1 防止VSCode Remote-SSH频繁掉线
很多人在虚拟机上开发到一半,VSCode突然断连或者提示“Could not establish connection to ...”。原因之一是虚拟机休眠或网络被DHCP重新分配了IP。用桥接并配置静态IP能解决大部分问题。
另一个原因是SSH会话空闲超时。默认情况下,sshd不会主动断开空闲连接,但某些虚拟机模板或网络设备会。可以在sshd_config里增加:
ClientAliveInterval 60 ClientAliveCountMax 10意思是每60秒向客户端发一个保活包,10次没回应才断开。改完重启sshd。
还有,如果虚拟机里运行的任务特别耗CPU,vscode-server可能因为过载而响应变慢,表面的症状是VSCode一直转圈,其实远端在忙。可以用htop看看负载,别急着折腾网络。
6.2 加速远程文件同步和搜索
VSCode Remote-SSH不是把整个项目同步到本地,而是通过网络实时读写,所以第一次打开大项目时会比较慢。可以在虚拟机上禁用前端的实时文件监控来减少CPU占用,或者把files.watcherExclude配置好,把node_modules、.git、编译输出目录全部排除:
"files.watcherExclude": { "**/node_modules/**": true, "**/.git/**": true, "**/build/**": true, "**/target/**": true }搜索文件时,Remote-SSH默认在远端搜索,速度取决于磁盘IO。如果虚拟机是机械硬盘,建议至少用SSD。AlmaLinux的vm.swappiness对交互响应也有影响,可以适当降低这个值,不过这个优化不是必须的,我通常保持默认。
6.3 远程开发时值得装的扩展组合
VSCode连接远程后,本地端的很多扩展不会自动传到远端,需要在扩展面板里选择“在SSH: 虚拟机名上安装”。我常用的组合:
- Remote-SSH,远程开发的基础。
- Python或C/C++扩展,取决于开发语言。
- GitLens,查看代码历史和blame信息。
- ESLint、Prettier,在远端生效,统一代码风格。
- Live Server或Debugger,需要远程端口转发时,VSCode会自动做端口映射,非常方便。
注意:有些扩展在远程场景下渲染起来很慢,比如字体图标类插件、大型UI主题,会增加Remote-SSH的通信负载。建议精简扩展数量,只保留工作必需的,这样每次连接时在远端安装扩展列表的时间都会短很多。
7. 我自己留着的快速自检清单
把这一周踩的坑浓缩成一份清单,每次连接失败的时候按顺序过一遍,基本十分钟内能定位问题。
7.1 连接前必查项
- 虚拟机IP是否正确,宿主机能ping通。
- 22端口从宿主机能访问,
Test-NetConnection返回True。 - sshd服务在运行,且没有
PermitRootLogin no挡住账号。 - firewalld放行了ssh,且规则落到了对的zone。
- VSCode的Remote-SSH日志已经打开,能看到console输出。
7.2 连接失败时的快速判定
- ping不通 -> 先查网络模式和静态IP。
- 端口不通 -> 查sshd服务和防火墙。
- 能连上但提示身份或密钥错误 -> 查
PermitRootLogin、公钥、私钥权限。 - 能连上但卡在“正在加密远程连接” -> 立刻去虚拟机上看磁盘空间,
df -h,再看~/.vscode-server目录权限。 - 连上后频繁掉线 -> 配静态IP,加保活参数,检查虚拟机负载。
这份清单我在两台机器上都贴了一份,每次连不上就直接对着查。后来我甚至把它写成了一页Markdown,放到.home目录下,配合VSCode的Remote-SSH命令,基本不再为连接问题熬夜。
最后再分享一个习惯:每次给虚拟机升级内核或安装系统更新后,我都会手动重启一次sshd,再确认journalctl -u sshd里没有新增的异常。AlmaLinux更新后可能会触发SELinux策略的微小变化,极少情况下会影响远程连接。只要养成“先看日志再动手”的习惯,这类问题都会变得很具体,不再玄学。希望这篇总结能帮你少走点弯路。