Devpod 远程开发实践:如何提升开发者生产力

在大规模软件工程体系中,单体仓库、复杂构建流程和本地
开发环境维护,往往会成为影响开发者生产力的重要因素。某海外大型科技公司通过
Devpod 远程开发环境,将代码、工具链和构建资源迁移到云端,在提升构建速度、降低环境配置成本和增强安全性方面取得了明显成效。

早期痛点

2017 年的代码库状况

2017 年,某海外大型科技公司的代码体系高度分散:代码分布在数千个仓库中,覆盖 10 多种编程语言、4000 多个服务、500 多个 Web 应用,同时还使用着 9 种以上构建工具和 6 种以上配置工具。几乎每个微服务和应用都有自己独立的代码仓库。

这种分散的代码组织方式给开发者带来了诸多问题,包括依赖管理困难、生产环境中的库版本碎片化、构建工具不统一、各类工具和框架难以获得充分支持、团队协作成本高,以及代码共享不便等。

从多仓库迁移到单体仓库

图 1:迁移到单体仓库

为解决这些问题,公司内部成立了一个跨组织工作组,负责制定新的仓库与构建策略。该工作组建议将所有代码迁移到一个统一的单体仓库中,采用基于主干的开发模式,并确保每个第三方库在公司内部只保留一个统一版本。

迁移到单体仓库后,公司获得了几项重要收益:

  • 依赖管理更加清晰;
  • 生产环境中的库版本更加一致;
  • 形成了统一的集中式构建平台;
  • 标准化工具和流程更易推广与维护;
  • 代码可见性、团队协作效率和代码复用能力显著提升。

单体仓库带来的开发挑战

迁移到单体仓库为公司的长期工程体系奠定了基础,但也让开发者在笔记本电脑上完成日常的“编辑—构建—运行”循环变得更加困难:

  • 构建规模更大,耗时更长;
  • 开发者需要将数 GB 且频繁变化的构建产物下载到本地,或直接在本地完成构建;
  • 当开发者远离办公室高速企业网络时,开发体验会明显下降。有时,远程克隆一个新项目并从零开始完成环境搭建需要数小时;
  • 本地开发环境的维护也始终是难题,例如工具链更新不具备原子性、执行步骤不透明、不同开发者之间环境差异较大等。

图 2:Devpod 概览 – Uber 的远程开发环境

Devpod:面向大规模工程的远程开发方案

为了给开发者提供更快、更便捷、更安全的体验,团队开始探索远程开发方案。

这个思路很有吸引力:将构建任务放到云端性能更强的机器上执行,让开发环境在几秒钟内启动,并把代码库和工具链统一保存在安全、可控的环境中。Devpod 正是在这样的背景下诞生的。

Devpod 是该公司内部的远程开发环境,目标是让开发者摆脱本地机器性能、网络和环境配置的限制,在云端获得稳定、高效且一致的开发体验。

什么是 Devpod 远程开发环境?

Devpod 是一个面向内部工程师设计的远程开发环境。它以容器形式运行在 Kubernetes 集群上,内置开发所需的工具链、配置和计算资源,并支持与主流 IDE 集成。

借助 Devpod,开发者可以在大规模单体仓库中顺畅完成代码编辑、构建、调试和运行等工作,而无需在本地笔记本电脑上维护复杂的开发环境。

Devpod 的核心优势

团队认为,远程开发环境的核心价值主要体现在三个方面:性能提升、接近零配置,以及更高的安全性。其中,性能和低配置成本是提升开发者生产力的关键。

在安全性方面,Devpod 也带来了明显改善。由于所有用户都运行在统一且受控的环境中,团队可以确保开发环境始终保持在最新稳定版本。存在漏洞的应用和工具可以在非工作时间完成修补和更新。

同时,远程开发环境更易监控。团队可以更方便地观察环境运行状态、检测异常行为,并在非工作时间扫描磁盘,以发现潜在的恶意痕迹或异常活动。由于 Devpod 运行在云端 Kubernetes Pod 中,不受笔记本电脑电量限制,这类安全检查也更容易自动化执行。

通过远程开发提升性能和开发效率

Devpod 利用云端高性能计算资源,为每个开发环境提供最高 48 个核心和 96 GB 内存。同时,团队还围绕 Git、构建系统和 IDE 做了一系列优化,从而显著改善开发体验。

IDE 体验优化

  • 安装并配置由中央开发者体验团队支持的最新 IDE 版本;
  • 预加载缓存后的 IDE 索引;
  • 预安装常用工具、扩展和内部插件;
  • 为开发者提供开箱即用的统一配置。

Git 性能优化

  • 优化 Git 配置;
  • 利用 Linux 文件系统相较普通笔记本电脑文件系统的性能优势;
  • 结合单体仓库场景,对常见 Git 操作进行专项优化。

构建性能优化

  • 预加载构建所需的开发工件;
  • 提供更充足的计算资源;
  • 就近接入远程构建缓存;
  • 减少开发者本地下载和重复构建的成本。

除了构建和代码操作速度提升之外,远程开发环境还带来了其他体验改善:

  • 笔记本电脑不再频繁卡死或过热;
  • 笔记本电脑电池续航时间得到改善;
  • 每位用户可以同时拥有多个远程开发环境;
  • 远程开发任务不会受到本地机器上其他进程的干扰。

接近零配置的云端开发环境

Devpod 预置了一整套工具和配置,使开发者能够快速进入主要单体仓库并开始工作:

  • 配置简单:通过一条客户端命令,或在 Web 管理界面中点击几下,即可按需创建新环境;
  • IDE 开箱即用:预配置主流代码编辑器和商业 IDE,并预加载索引;
  • 几分钟内即可获得一致的开发环境;
  • 每个单体仓库所需工具都已完成预安装、预配置和测试;
  • 操作系统、存储层和库与生产环境保持相似;
  • 代码仓库已提前克隆,减少初次启动等待时间。

更安全的远程开发环境

使用 Devpod 后,开发者无需再手动维护本地环境更新。系统会在夜间自动应用最新工具版本和安全补丁。

主要安全收益包括:

  • 受控工具链:开发环境基于预构建镜像,内置经过加固的工具链;
  • 配置变更无缝生效;
  • 支持快速自动更新;
  • 镜像在提升为稳定版本之前会经过安全扫描。

Devpod 也是持久化的远程环境。工程师无需担心个人设置、文件或代码变更丢失,也可以在不同设备之间无缝继续工作。在特定场景下,多位工程师还可以进入同一个环境进行协作。

全球多区域部署,降低远程开发延迟

对远程开发而言,低延迟至关重要。尤其是在 IDE 和终端交互场景中,如果输入反馈存在明显延迟,开发体验会迅速恶化。

为此,团队在全球多个区域部署了 Devpod,以便让不同地区的工程师都能获得较低延迟的访问体验。目前,Devpod 已部署在以下区域:

  • 美国俄勒冈州;
  • 美国弗吉尼亚州;
  • 欧盟荷兰;
  • 印度孟买;
  • 巴西圣保罗。

Devpod 类型

Devpod 的“类型”指的是面向特定工程师群体定制的 Docker 镜像。每种类型都包含对应开发场景所需的工具,并预加载默认设置与配置。

目前支持的类型包括:

图 3:Devpod 的各种版本——开箱即用的配置

Devpod Central:Web 管理界面

为了改善用户首次使用 Devpod 的体验,并提升故障排查效率,团队为 Devpod 构建了一个简洁的 Web 管理界面 Devpod Central。

通过 Devpod Central,用户可以更直观地创建、查看和管理自己的远程开发环境。团队后续还计划持续扩展该平台能力,提高资源使用和消耗情况的可见性。

图 4:Devpod 中心 – 管理门户

Devpod 架构设计

图 5:Devpod 架构

Devpod 镜像

将所有工具和配置打包到 Docker 镜像中,是一个自然且合理的选择。Docker 镜像易于分发和部署,许多工程师也熟悉其使用方式,因此更容易参与贡献和维护。

每个 Docker 镜像都需要一个基础镜像。经过讨论,团队认为采用与生产环境操作系统一致的基础镜像最为合理。这样一来,团队可以复用现有基础设施来构建和分发内部软件包,也可以复用生产环境中已经验证过的关键软件配置。

图 6:Devpod 镜像层级结构

使用 Kubernetes 作为底层平台

从本地笔记本电脑迁移到云端后,团队可以使用拥有 90 多个核心、数百 GB 内存的高性能机器。最终,团队选择 Kubernetes 作为底层平台,因为它提供了 Devpod 所需的关键基础能力:

  • 在高性能硬件上托管容器;
  • 为容器提供网络连接能力;
  • 通过持久卷在环境重启后保留工程师的工作内容。

Devpod Operator

当团队开始使用 Kubernetes 运行 Devpod 时,采用自定义资源定义,也就是 CRD,是顺理成章的选择。CRD 是扩展 Kubernetes 的一种方式,它可以在控制平面中保存自定义数据结构。这样,团队无需再为 Devpod 元数据单独搭建内部数据库。

通过自定义控制器,团队可以使用kubectl创建和访问 Devpod。

Kubernetes 自定义资源定义的一大优势在于,它允许开发者编写一段软件来响应自定义资源的变化。这被称为 Operator 模式。借助这一模式,团队将自定义资源与小型自定义控制器结合起来,把高层级的 Devpod 描述转换为标准 Kubernetes 原语,再交由标准 Kubernetes 控制器处理。

最初,这个自定义控制器非常轻量。团队甚至开玩笑说,它几乎可以被改写成 Bash 脚本。幸运的是,最终团队没有这么做。

后来,团队扩展了该自定义控制器,使其能够响应VolumeSnapshotContentsPersistentVolumeClaim,从而在 Devpod 未使用时降低成本。

图 7:Devpod 操作员

Devpod 部署策略

团队希望将 Devpod 运行在生产数据中心之外的 Kubernetes 集群中,主要有两个原因。

首先,将开发者 shell 服务部署在生产数据中心之外,可以带来更强的隔离性。

其次,公司需要服务亚太以及欧洲、中东和非洲等地区不断增长的工程师群体。通过将 Kubernetes 集群部署在靠近开发中心的位置,团队可以为开发者提供更流畅的体验。即便远程开发已经在一定程度上降低了对延迟的敏感性,例如使用本地文件、避免每次击键都产生网络往返,低延迟对于良好的开发体验仍然非常关键。

借助 Kubernetes 作为平台,团队可以把精力集中在真正具有战略意义的事情上:为开发者提供快速、便捷、安全的开发体验。

构建过程本质上高度依赖文件系统。因此,将高速存储连接到 Kubernetes 集群,对于运行 Devpod 至关重要。虽然也可以使用由对象存储支持的临时文件系统,但这种方式实现成本较高,使用体验也不够理想。团队仍计划在未来继续探索这一方向。

远程开发落地过程中的挑战

打造理想的工程师开发环境并不容易。Devpod 在落地过程中面临了不少挑战,包括:

  • 如何在性能和成本之间取得平衡;
  • 如何提供自动升级能力;
  • 如何确保开发者工作不中断;
  • 如何为不同用户预配置合适的 IDE 设置;
  • 如何在统一环境和个性化需求之间找到平衡。

IDE 体验

工程师每天都要使用 IDE。因此,如果 IDE 体验不够好,远程开发环境就很难真正成功。

在 Devpod 的发展过程中,团队先后提供了多种 IDE 连接方式:

  • 通过远程模式使用主流代码编辑器;
  • 通过 Web 使用主流代码编辑器;
  • 通过 Web 使用某主流商业 IDE,该方案后来被弃用;
  • 通过 SSH 访问远程商业 IDE;
  • 通过终端使用 Vim 和 Emacs。

图 8:Devpod IDE 选项

除了提供多种 IDE 选项,团队还重点从以下方面优化使用体验:

  • 预加载索引;
  • 预配置设置;
  • 预安装工具、扩展和自定义插件。

凭借强大的云端实例规格,Devpod 能够提供接近本地开发的流畅体验。然而,IDE 体验从一开始就是团队面临的最大挑战之一。早期流行的 Web IDE 存在较明显的延迟问题,后续又暴露出一些稳定性问题,这些都影响了 Devpod 的推进速度。

主流代码编辑器的远程版和 Web 版在远程工作负载方面表现较好,但在语言理解等智能能力上仍有提升空间。语言服务器协议 LSP 是一项重要进步,但很多语言服务器实现仍需进一步完善,才能满足大型工程场景的需求。

例如,如果语言服务器能够将索引保存到磁盘,并在后续从磁盘重新加载,将会非常实用。但目前许多语言服务器还不支持这一能力。幸运的是,LSP 是开放协议,许多语言服务器也是开源项目,因此团队也有机会向社区贡献改进。

保持开发环境持续更新

工程师的时间非常宝贵,因此提供一个无需手动维护的开发环境至关重要。为此,团队会在非工作时间自动升级 Devpod 环境,使其包含最新工具和安全更新。

为了满足不同工程师的需求,Devpod 提供了四种发布节奏:

  • 稳定版:默认版本;
  • RC 版:下一个稳定版本的候选发布版本;
  • 开发版:每晚更新到最新成功构建版本;
  • 无自动更新:完全关闭自动更新。

无论工程师希望使用最稳定的环境,体验最新功能,还是完全不希望自动升级,Devpod 都能提供对应选择。

后来,团队进一步改进了自动更新机制,支持新版本逐步发布。这样做是为了在某个问题漏过自动化测试和候选版本测试流程时,尽可能缩小影响范围。

远程开发环境的成本效益

在推进 Devpod 的过程中,团队经常需要回答一个问题:自建是否比采购现成方案更合理?

为了证明自建方案的价值,团队持续跟踪并优化每个 Devpod 的成本,确保 Devpod 的性价比优于现成替代方案。

由于 Devpod 并不是用完即销毁的短生命周期环境,团队必须确保计算和存储资源得到充分利用。因此,团队实施了多项优化措施。

关闭不活跃环境

为了节省计算资源,自动化任务会定期检查每个环境最近是否被使用。一旦检测到环境长期不活跃,系统就会删除其容器,从而释放计算资源。

虚拟机重新平衡

多个 Devpod 环境会部署在同一台大型虚拟机上,而这些虚拟机都会产生费用。因此,充分利用每台虚拟机的资源非常重要。

如果两台虚拟机都只使用了一半资源,系统会将负载迁移到其中一台虚拟机上,并关闭另一台虚拟机。

对已关闭环境的磁盘进行快照

Devpod 运行时需要容器和磁盘。当容器关闭后,磁盘不再处于活跃使用状态。此时,系统会将磁盘转换为低成本存储形式,直到环境重新启动。

通过这些优化,团队成功将 Devpod 成本控制在远低于现成方案的水平,同时仍实现了显著的性能提升。

跟踪 SLO 指标,保障开发者体验

该公司的开发者平台服务于约 5000 名核心软件工程师,帮助他们高效、大规模地构建、部署和管理高质量软件。团队深知,为开发者提供稳定体验是一项重要责任。

因此,Devpod 团队会持续跟踪服务级别指标 SLI,以确保达成服务级别目标 SLO。

目前,关键指标包括以下几类。

系统可用性

对于大多数工程师日常高度依赖的系统而言,可用性至关重要。团队希望在可用性 SLO 中尽可能提供更多“9”。不过,实际可用性也会受到云服务提供商可用性上限的限制。

开发环境就绪状态

团队会密切监控“预期运行的开发环境数量”和“实际可用的开发环境数量”之间的差异。因为每一个不可用环境都对应着一位受影响的工程师。

如果某个环境启动时间超过预期,团队会收到告警并排查根本原因。

连接健康状况

为了让 Devpod 尽可能复现本地开发能力,团队必须确保基础设施与公司内部服务之间保持稳定连接。因此,团队会持续探测多个端点,以掌握整体连接状态。

在可用性指标方面,团队曾遇到一个问题:维护窗口期间,即使系统不需要处于完全运行状态,也会触发关键告警。这给团队带来了额外负担,有时甚至会在夜间打断值班人员。

因此,团队开始跟踪维护窗口,并将该时间段从告警逻辑中排除。可观测性仪表盘则帮助团队将维护窗口与正常运行时间指标进行对比。

图 9:Devpod 关键 SLO – 正常运行时间

Devpod 的实际效果

数据显示,Devpod 有效降低了本地 Git 命令的执行耗时。以公司最大的单体仓库为例,该仓库包含超过 7000 万行代码。在该仓库中执行 p95git status命令时,Devpod 的耗时表现明显优于本地笔记本电脑。

相关数据表明,Devpod 上的 p95git status耗时始终低于 4 秒。相比之下,随着单体仓库规模扩大,笔记本电脑上的git status性能曾一度持续下降。直到团队在笔记本电脑上引入文件系统监视器和稀疏检出后,本地性能才有所改善。

图 10:Git 状态的改进

构建任务也呈现出类似趋势。对于耗时最长的完整构建任务,Devpod 的表现始终优于笔记本电脑。以 Go 构建任务的 p95 延迟为例,Devpod 在高负载构建场景下表现更稳定。

图 11:Go 构建时间的改进

图 12:Go 构建时间的改进

对于耗时更长、更复杂的构建任务,Devpod 的性能平均比笔记本电脑提升 2.5 倍;对于一般构建任务,也就是 p75 构建任务,Devpod 的性能平均比笔记本电脑提升 1.8 倍。

随着这些改进持续落地,Devpod 在组织内部的采用率自然增长。截至 2022 年 11 月,超过 60% 的软件工程师已经采用 Devpod。

图 13:Devpod 采用情况

开发者认可 Devpod

根据内部开发者 NPS 反馈,Devpod 一直被认为是最能提升工作效率的工具之一。

调查显示,多数开发者喜欢 Devpod 的主要原因,是它显著提升了构建速度。许多开发者表示,使用 Devpod 后,他们的工作效率和生产力都有明显提高。

下一步规划

团队认为,Devpod 代表了该公司工程体系未来的重要方向。长期目标是让工程师获得完全无缝的远程开发体验。

接下来,团队计划重点探索以下几个方向。

按需 Devpod

目前,创建一个新的开发环境大约需要 2 到 3 分钟。团队希望通过预先配置每种产品类型的开发环境,并在分配给请求用户之前完成最终配置,将初始创建时间缩短到 3 秒以内。

临时 Devpod:生命周期更短的开发环境

一旦按需 Devpod 能够实现,并将新环境创建时间缩短到 3 秒以内,团队希望将 Devpod 与开发者日常使用最频繁的工具进行更深入集成。

团队预计,短生命周期 Devpod 可以应用于以下场景:

  • 专用于功能开发的临时环境;
  • 快速调试 CI 失败问题;
  • 开箱即用的代码审查环境;
  • 分析移动端崩溃问题。

非中断式自动升级和维护

目前,系统会在非工作时间设置环境维护窗口。但部分工程师可能需要在非工作时间工作,或在环境中运行耗时较长的任务。

因此,团队计划通过监控活动连接,并在必要时延迟维护任务,进一步降低自动升级和维护对开发者的影响。

通过自动化降低成本并提升性能

团队希望制定更加精细的调度和关停策略,在不中断用户工作流的前提下进一步降低成本,并及时清理闲置 Devpod。

这一方向可以理解为 Devpod 的自动化调度能力:系统根据环境使用情况、资源消耗和用户活动状态,自动做出更合理的资源分配决策。

无缝 IDE 体验

理想状态下,IDE 应该在后台隐藏远程环境的实现细节,让工程师在本地笔记本电脑上使用 IDE 时,几乎感知不到远程环境的存在。

IDE 可以在后台静默连接到远程环境,并在网络状态良好时提供强大的云端计算能力。即便远程环境出现问题,开发者也不应被迫中断工作,而是能够无缝切换并继续在本地完成开发。

面向所有环境的集中式构建集群

团队计划引入一个统一的构建集群,提供远程缓存和远程执行能力,为 CI、笔记本电脑和 Devpod 开发环境共同服务。

该构建集群将运行计算密集型任务,并充分利用空闲计算资源。此外,团队还希望部署一个地理分布式远程缓存,以最大限度降低延迟,并为构建产物提供更快的下载能力。

团队专属 Devpod 配置

团队正在探索如何尽可能简化新团队首次使用 Devpod 的配置流程。

其中一个重要方向,是允许团队自定义 Devpod 配置。这样,新团队成员只需点击一次,就可以获得符合团队需求的一致开发环境,而不必手动完成复杂的环境搭建。

笔记本电脑与 Devpod 之间的无缝文件传输

工程师会使用 Devpod 处理计算密集型任务和长时间运行的构建。在某些情况下,开发者需要在笔记本电脑和 Devpod 之间传输文件,反向传输也同样常见。

团队的目标是将 Devpod 驱动器自动挂载到用户笔记本电脑上,从而实现笔记本电脑与远程开发环境之间的无缝文件传输。

Devpod 对 Android 模拟器的支持

Devpod 能够显著缩短 Android 开发者的构建时间。然而,在远程 IDE 中进行测试和调试时,当前体验仍不够理想。主要原因是远程环境中缺少 Android 模拟器,也缺少可通过 ADB 连接到 Devpod 的虚拟设备。

团队的目标是为使用 Devpod 的 Android 开发者提供无缝调试体验,包括与 Devpod 支持的 IDE 集成的内置插件。

总结

Devpod 的实践表明,对于拥有大规模单体仓库和复杂构建体系的工程组织而言,远程开发环境不仅可以提升构建性能,还能降低本地环境维护成本,增强安全性,并改善整体开发者体验。

不过,远程开发环境解决的主要是“开发执行效率”问题。对于希望进一步提升端到端研发效能的团队,还需要把目标、需求、任务、测试、发布、知识沉淀和数据分析串联起来。比如,借助 PingCode 这类智能化研发管理工具,团队可以将研发过程中的需求流转、项目开发、测试发布、知识积累和工具集成统一起来,让远程开发带来的效率提升进一步沉淀为可管理、可度量、可持续优化的研发体系。

通过云端计算资源、Kubernetes 容器平台、预配置 IDE、远程构建缓存和自动化运维能力,Devpod 为提升开发者生产力提供了一条可行路径。对于正在应对大型代码库、复杂工具链和多地区协作挑战的研发团队来说,这类远程开发模式具有重要参考价值。