Vasily's Blog

一个记录学习经历的站点

0%

VS Code Remote-SSH 老服务器兼容:完整踩坑记录与技术方案

VS Code Remote-SSH 老服务器兼容:完整踩坑记录与技术方案

问题

VS Code 从 1.86 开始,远程 vscode-server 中的 Node.js 要求 glibc >= 2.28。CentOS 7 默认 glibc 2.17,低于此要求。现象:

  1. SSH 隧道能通,code-<commit> CLI binary 可能启动成功
  2. server 进程(code-server + node)启动即崩溃
  3. VS Code 认为"server 未安装",联网下载 → 服务器不能上网 → 404
  4. 无限循环重试,自动删除并重建 server 目录

目标是不降级 VS Code,在 glibc 2.17 的老服务器上跑通 Remote-SSH。

Remote-SSH 远程架构的演进

要理解修复思路,必须先搞清楚 vscode-server 在远程的目录结构。

经典旧架构(VS Code < 1.86)

1
2
3
4
5
6
~/.vscode-server/
├── bin/<commit-hash>/
│ ├── node ← ELF 64-bit
│ ├── out/server-main.js
│ └── extensions/
└── code-<commit> ← CLI binary(启动器)

启动链路: VS Code 客户端 → SSH 隧道 → CLI binary 启动 → 调用 bin/<commit>/bin/code-server shell 脚本 → 脚本调用 node out/server-main.js

关键发现:旧版 bin/code-server 是一个 shell 脚本(约 249 字节),不涉及 glibc,只是调用 node:

1
2
3
#!/usr/bin/env sh
ROOT="$(dirname "$(dirname "$(readlink -f "$0")")")"
"$ROOT/node" ${INSPECT:-} "$ROOT/out/server-main.js" "$@"

glibc 负担完全在 node 这个 ELF 二进制上。

新 CLI 架构(VS Code 1.86~1.98,过渡期)

1
2
3
4
5
6
7
8
9
~/.vscode-server/
├── cli/
│ └── servers/
│ └── Stable-<commit>/
│ └── server/
│ ├── bin/code-server ← 仍是 shell 脚本
│ ├── node
│ └── out/
└── code-<commit>

server 从 bin/<commit>/ 移到了 cli/servers/Stable-<commit>/server/,内部结构大体相同。

全新架构(VS Code 1.126,当前版本)

1
2
3
4
5
6
7
8
9
10
11
~/.vscode-server/
├── cli/
│ └── servers/
│ └── Stable-<commit>/
│ └── server/
│ ├── bin/
│ │ ├── code-server/ ← **变成目录**(内含 ELF 二进制)!
│ │ └── ...
│ ├── node ← 96MB ELF,可能是目录
│ └── out/
└── code-<commit> ← 32MB 静态链接 ELF

关键变化: bin/code-server 从 shell 脚本变成了自包含 ELF 二进制,同样需要 glibc 2.28+。这是新版 VS Code 在旧服务器上完全不能用的根本原因——两个地方都卡 glibc

各版本修复需求

VS Code 版本 需要修 node 需要修 code-server server 路径
< 1.86 ❌ (shell 脚本) bin/<commit>/
1.86~1.98 ❌ (shell 脚本) cli/servers/Stable-/server/
1.99~1.110 ❌? cli/servers/Stable-/server/
1.126+ ✅ (注意可能是目录) ✅ (变成了 ELF 目录) cli/servers/Stable-/server/

三轮尝试

❌ 第一轮:替换 CLI binary + 伪装旧 server 目录

直接把旧版 server 目录 cp -r 改名成新版 commit hash,再用旧版 CLI binary 覆盖新版。

结果: VS Code 连上了,server 进程启动成功,但立刻报 Client refused: version mismatch。旧 server 的 JS 代码报告的是旧版本号(1.98.2),VS Code 1.126 客户端不认。

❌ 第二轮:下载新版 server 替换 JS 代码

在本机下载新版 server,scp 传上去,用新版 out/package.jsonproduct.json 覆盖旧目录。

结果: 仍然连不上。新版 server 的 bin/code-server 变成了 ELF 目录,在 glibc 2.17 上启动即崩溃。VS Code 进入无限重试循环。

✅ 第三轮:旧版 code-server + 新版 JS,混合部署

核心洞察: bin/code-server 在 1.98 中是 shell 脚本,在 1.126 中是 ELF 目录。用旧版的 shell 脚本覆盖新版目录,就可以绕过 ELF binary 的 glibc 需求。同理,node 也用旧版的。

操作步骤:

在服务器上:

1
2
3
4
5
6
7
8
9
10
11
# 1. 从旧版重建新版目录
cp -r ~/.vscode-server/cli/servers/Stable-<old-commit> \
~/.vscode-server/cli/servers/Stable-<new-commit>

# 2. 确认旧版 code-server 是 shell 脚本
file ~/.vscode-server/cli/servers/Stable-<new-commit>/server/bin/code-server
# 输出应为: "a /usr/bin/env sh script, ASCII text executable"

# 3. 确认 node 能跑
~/.vscode-server/cli/servers/Stable-<new-commit>/server/node --version
# 输出: v20.18.2

在本机 scp 新版 JS 代码覆盖:

1
2
3
scp -J jump-user@jump-host -r C:\path\to\vscode-server-linux-x64\out user@target:~/.../server/
scp -J jump-user@jump-host C:\path\to\vscode-server-linux-x64\package.json user@target:~/.../server/
scp -J jump-user@jump-host C:\path\to\vscode-server-linux-x64\product.json user@target:~/.../server/

关键细节: 旧版 bin/code-server 和旧版 node 的执行权限在新版目录中可能没有保留,务必补上:

1
2
chmod +x ~/.vscode-server/cli/servers/Stable-<new-commit>/server/node
chmod +x ~/.vscode-server/cli/servers/Stable-<new-commit>/server/bin/code-server

最终文件结构:

1
2
3
4
5
6
7
8
9
server/
├── bin/
│ └── code-server ← 旧版 shell 脚本(不是目录!)
├── extensions/ ← 新版(scp 覆盖)
├── node ← 旧版 v20.18.2
├── node_modules/ ← 新版
├── out/ ← 新版 JS 代码
├── package.json ← 新版
└── product.json ← 新版

VS Code 的无限重试机制(最坑的地方)

每次连接失败后,日志显示 VS Code 在反复循环:

1
2
3
4
[server] Installing and setting up...
[server] Error installing server: ... 404 Not Found
[server] Checking ... log.txt and pid.txt ...
[server] Installing and setting up...

同时服务器上 cli/servers/ 目录里的 Stable-<commit>自动删除并重建为空的 .staging 目录。

根本原因: VS Code 检测到 server 进程启动失败后触发自动修复机制:

  1. CLI binary 上报「server 在端口 X 上监听」(实际是通信端口,不是真 server)
  2. exec server 发现真正的 VS Code Server 没有启动
  3. 触发 server 安装流程 → 联网下载 → 404 → 重试

解决方法:必须完全关闭 VS Code 窗口。 只断开远程连接不够——后台 exec server 仍在持续重试。正确流程:

  1. 关掉整个 VS Code(File → Exit)
  2. 在服务器上重建 server 目录
  3. 清理锁文件:rm -f ~/.vscode-server/.cli.*.lock
  4. 重启 VS Code
  5. 立即连接

关键决策点

方案 需要旧 server 需要本机上网 需要 root 一次施工管多久
旧 binary 替换法(本文) ✅(只需一次) 直到 VS Code 更新
patchelf 方案 ✅(只需一次) 直到 VS Code 更新
升级服务器 glibc 永久的
降级 VS Code 直到你受不了旧版
  • 有旧版 server 残留 → 用本文的方案,最简单
  • 没有任何旧版 server → 考虑 patchelf 方案
  • 能 root 且不怕风险 → 升级 glibc,一劳永逸

只要 VS Code 1.126 的 protocol 不变,这条链路就一直有效。VS Code 更新后 commit hash 会变——那时需要重新下载新版 server、重新 scp、重新替换 binary。


附录:完整诊断命令速查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看所有 server 目录
ls -la ~/.vscode-server/cli/servers/

# 检查 node 能否运行
~/.vscode-server/cli/servers/Stable-<commit>/server/node --version

# 检查 code-server 类型(文件 vs 目录/ELF)
file ~/.vscode-server/cli/servers/Stable-<commit>/server/bin/code-server

# 查看 server 日志
cat ~/.vscode-server/cli/servers/Stable-<commit>/log.txt

# 清理锁文件
rm -f ~/.vscode-server/.cli.*.lock

# 查看所有 CLI binary 版本
for f in ~/.vscode-server/code-*; do echo "=== $f ===" && $f --version 2>&1; done