Visual Studio Code 远程开发探秘

版权声明:所有博客文章除特殊声明外均为原创,允许转载,但要求注明出处。

在以前的文章 有趣的项目 - 在浏览器中运行 Visual Studio Code, 我介绍过 Coder 开发团队将 Visual Studio Code 搬到浏览器里的尝试。这是一个有趣的项目,不过没有想到的是,这之后不久微软官方就推出了 VSCode 的远程开发扩展,这简直是官方逼死同人的节奏。从 Coder 官网 的信息来看,他们似乎已将精力主要放到企业版本,这应该算一个生不逢时的产品吧。今天我们来介绍一下微软自己基于 VSCode 的远程开发平台。

工作原理

VSCode Remote 工作原理

从原理上讲,VSCode 远程开发扩展相当于把开发者自己机器上的 VSCode 原样拷贝到作为目标机器(Remote Host)上,以服务的形式运行,而本地的 VSCode 作为客户端,两者之间通过远程通讯协议彼此协调合作,实际上的开发工作主要是在服务端完成的。这个架构特别之处在于,我们日常所使用的扩展也被分成两个阵营:和界面定制相关的部分,主要包括样式、主题、图标等等在客户端运行;而与开发相关的大部分扩展则在服务端运行。后面在实际操作的部分,我们会看到界面上相应的变化。

目前,VSCode 远程开发支持下列三种主要模式:

  • Remote SSH:通过 SSH 连接到 Linux 服务器;
  • Remote Container:连接到 Docker 容器;
  • Remote WSL:连接到已安装的 WSL 环境。

本文主要介绍第一种,基于 SSH 的方式。容器方式除了初始化配置方面有一些区别以外,具体使用上基本相同。至于 WSL,按照目前的发展方向,可以认为它和从虚拟机运行 Linux 没什么差别,所以我不会特别关注它。想要使用该方式的同学可以参考 官方文档

先决条件

为了使用 VSCode Remote SSH,首先请确认你了解它的一些限制与前提条件。

  • Remote SSH 只支持 Linux 作为服务器,且必须是 64 位版本。这可能是因为 Linux 才有完整的 SSH 服务器支持,而 Windows 或 MacOS 都需要一些额外的工作。鉴于大部分生产服务器都应该是 Linux,相信这个限制对大多数同学不成问题;
  • 对于 Linux 的具体类型,官方明确支持的包括 Debian、RHEL/CentOS 与 Ubuntu 三大发行版。其他很多 Linux 版本也应该可以工作,但并不保证,也有一些比较少见的版本不受支持(主要是因为 glibc 等基础环境的支持问题)。另外,CentOS 最好是 7.x 以上,6.x 版本通常需要一定的调整才能工作(参考: Updating glibc and libstdc++ on RHEL / CentOS 6);
  • 当然,要使用 Container 或 WSL 方式,在机器上必须有 Docker 或者 WSL 的基础环境;
  • 本地机器上应该有 SSH 命令行客户端。对于 Win10,只要不是补丁太旧的话,应该已经内置了 OpenSSH。Putty 目前是不受支持的。

安装扩展

确认前提条件已满足,接下来应该在自己的 VSCode 中安装远程开发扩展。

VSCode Remote 扩展

远程开发扩展的名称是 Remote Development,它实际上是一个扩展包(Extension Pack),由 Remote-SSHRemote-ContainersRemote-WSL 以及 Python 四个扩展组合而成,除了 Python 主要用于功能支持外,其他三个扩展功能是很明显的。目前该扩展仍然处于预览状态,不过已经可以安装到 VSCode 正式版了(若不能安装的话,请确认 VSCode 版本高于 1.35)。

配置 SSH Key

要通过 SSH 连接服务器,我们可以使用用户名/密码或者 SSH Key。对于日常使用的环境来说,基于 SSH Key 的方式尽管初始配置要麻烦一些,但是一劳永逸。

生成 SSH Key 并分发到远程机器是服务器运维的常规操作,具体过程就不再赘述了。官方文档也有一个比较详细的 步骤指导,需要的同学可以参考。

这里补充说明一点,对于 Windows 客户端,生成的 Key 通常位于 %USERPROFILE%.ssh 目录下。在后续的配置部分我们会用到这个目录。

连接到服务器

配置好 SSH Key 就可以连接到服务器了。最直接的方法是通过命令面板,选择命令 Remote-SSH: Connect to Host,然后按照提示输入格式为 user@host 的服务器地址。

连接到服务器

但是每次打开环境都要手工输入地址显然是很不人道的,于是远程开发扩展为我们提供了保存服务器配置的方式。调用该方式一般也是通过命令面板:Remote-SSH: Open Configuration File

打开 SSH 配置文件

该命令又会进一步提示我们选择哪个配置文件来编辑。

打开 SSH 配置文件

你可以认为上图中两个文件分别代表机器级别和用户级别的配置,通常应该选择用户级别配置。打开以后会看到,它已经为我们提供了一个默认模板。我们按格式添加服务器记录,并且额外提供一个证书文件的位置参数。对于服务器地址和用户名,请按你的实际情况输入:

# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
# Host alias
#    HostName hostname
#    User user

Host test-server
    HostName <192.168.207.130>
    User <user>
    IdentityFile C:/Users/<user>/.ssh/id_rsa

在安装完远程开发扩展之后,我们会注意到在活动栏下边多了一个远程图标,点击该图标会出现远程视图,其中包含了我们已经定义过的服务器。在服务器上右键点击,选择 Connect to Host in Current/New Window,就会在当前窗口或新窗口打开到服务器的连接,让你开始工作。

远程视图

第一次连接到远程服务器时的初始化工作需要消耗一段时间,以后再次打开就会快很多。请耐心等待服务器初始化完成,如果一切正常,你就会看到 VSCode 转变为远程开发模式。

远程开发模式

当工作环境处于远程模式的时候,你会注意到和本地开发的一些不同之处。

首先,状态栏左边会用绿色的文字明确指示当前处于远程模式(使用其他主题的话颜色可能会有所不同):

状态栏

其次,当你使用“打开文件”或“打开目录”命令的时候,也会发现现在显示的已经不是操作系统的本地文件对话框,而是另外一个不同的界面,用于选择远程服务器上的路径:

打开目录

此外,你还应该注意以下扩展视图的变化。

扩展视图

从图中我们可以看到,远程开发扩展以及一些界面主题是保留在本地 VSCode 的,而用于开发的扩展则在本地被禁用了。可能是出于性能的考虑,这些扩展并没有自动安装到远程服务器上,要在远端开启这些扩展的话,需要在图中对特定的扩展选择 Install on SSH <server> 命令。对于已经安装到远端的扩展,则会显示提示信息 Extension is enabled on SSH <server> and disabled locally

接下来的操作和普通的本地开发就没什么差别了。你可以打开目录、编辑文件、执行程序,等等。但需要注意的是,现在几乎所有操作幕后都是在服务器上完成的,如果你还下意识地以为是本地操作的话,有时候就难免有点混乱,所以还是应该坚持一段时间来适应。

还有一点补充建议:如果服务端是 Linux 而客户端是 Windows,并且你将要打开的是一个 Git 仓库的话,请考虑在 Git 中配置 autocrlf = false,,以避免不同平台对换行的处理差异导致莫名其妙的变更问题。

设置

最近我录制了一个课程 Visual Studio Code 全景学习,其中对设置的结构有专门的介绍。VSCode 的设置是一个非常灵活、但又相当复杂的层次结构,在远程开发的背景下,又多了一个远程设置的来源,所以结构是更加复杂了。

设置结构

在默认情况下,本地的 VSCode 用户配置会自动应用到远程服务器环境,不需要我们做额外的工作。但客户端和服务器通常是不同的操作系统,它们之间难免有一些差异,所以有时候还是要对远程环境单独作一些配置。为此,VSCode 提供了一个命令 Open Remote Settings 专门用来编辑远程配置。和其他命令一样,你可以从命令面板(Command Palette)调用它。

另外,远程开发也注册了一些自己特有的配置信息。其中最主要的可能是 remote.extensionKind。我们在本文前面的原理部分讲述过,为了支持远程开发模式,VSCode 会把扩展分为本地和远程两种运行类型。一般来说,VSCode 会自动判断扩展应该放在哪个位置,但也有一些情况可能不太好判断,所以 VSCode 允许我们自行配置它。

{
    "remote.extensionKind": {
        "ext1": "ui",
        "ext2": "workspace"
    }
}

对于每个扩展,我们可以把它设置为 ui 或者 workspace,分别代表在本地/服务器上启用。这样,VSCode 在启动远程模式时会对扩展做出合适的处理。如果还觉得有点糊涂的话,建议回头看看文章开头的架构图。

一些技术内部

在远程模式下工作时,几乎所有开发相关的操作都是在远程服务器上完成的。这也包括了终端(Terminal)。你可以尝试在终端输入一些命令,从提示和结果都能发现,这并不是客户端的 Windows Cmd,而是一个真实的 Linux Terminal。另外,我们还会发现 VSCode 会在远程服务器的用户目录下建立一个 .vscode-server 目录,该目录实际上就是一个完整的 VSCode 程序(那个很长的中缀猜想是用来区分不同的session,但是没有具体验证过)。所有在服务端开启的开发扩展也会自动拷贝到相应的子目录下。

终端

如果你希望了解远程模式的一些工作内幕,那么输出面板有一个 Remote-SSH 视图能为你提供一些信息。这个输出显示的内容还是比较有限,但是也能看到启动服务和调用命令的一些细节。此外,输出视图的 Log (Remote Server)Log (Remote Extension Host) 也会显示服务器相关的一些日志记录。

输出面板

我个人非常希望从源代码级别了解远程开发工作的一些细节,但很可惜,目前微软官方的代码库中只有一些文档和问题模板,并未开放远程开发扩展的源码。其实仔细研究远程开发的一些细节可以认识到,远程开发在很多方面是需要和 VSCode 的核心架构深度绑定的,因此有很大可能,该扩展会在功能逐渐稳定以后合并到 VSCode 的主体代码,不再作为单独的扩展出现。当然这是我个人的一家之言,不妨姑妄听之。

需要注意的问题

VSCode 远程开发目前还在预览状态,并且它对 VSCode 内部的一些架构变动也比较大,可能仍然存在不少 bug,对于第三方扩展也可能会有一些兼容性问题。如果你在使用中发现有问题的话,可以到 远程开发 Issue 查找或报告,或者参考官方文档中的 Troubleshooting 以解决一些常见问题。

如果你自己是扩展开发者的话,需要注意的是在远程模式下过去的某些做法可能是会出问题的,特别是一些直接访问本地功能的原生 nodejs 库。微软也列出了容易导致问题的一些常见场景,以及建议的解决方法,请参考阅读:Supporting Remote Development

个人感想

按照微软官方的设想,以及一些开发者的使用经验,VSCode 远程开发主要用于跨平台开发、统一开发环境、沙盒模拟等场景。对于一般性个人开发,我的感觉是通过 SSH 管理比本地开发还是反应略慢,失去了流畅的感觉,并且我个人对于上述场景没有特别强烈的要求,因此远程开发对我来说,至少在目前意义并不算大。但需要承认的是,这种方式带来了很大的想象空间,也很有可能在未来会看到其他更加有用的玩法,所以还是一个值得关注的方向。

然而从架构的角度讲,我对这个扩展是有一些担心的。主要的问题在于复杂性。我看到的主要问题包括:

  • 目前 VSCode 的设置层次已经相当复杂了,并且从官方 Issue 可以感受到,由于这种架构分支太多、难于管理,某些问题处理起来应该是比较棘手,甚至微软的开发者也无法给出明确的回答。而远程开发模式还会让这个结构更加复杂,可谓雪上加霜;
  • 对于扩展的本地/远程分类,也给扩展管理带来了额外的复杂性,并且不够直观;
  • 它也给扩展开发者带来了额外的负担,一些过去习以为常的用法在远程模式下可能彻底无法工作了,且需要这些开发者去了解一些琐碎的技术细节。提高扩展开发者的门槛对于 VSCode 繁荣的生态可能是不利的。

从长远来说,远程开发功能是不是独立成一个单独的产品更好呢?呃——其实我也不知道。