GitHub 工程团队已经转移到 Codespaces:实现开发环境的秒级启用
本文最初发布于 GitHub 官方博客,由 InfoQ 中文站翻译并分享。
GitHub.com 的代码库有 14 年的历史了。在 GitHub.com 的第一次提交被推送时,Rails 还只有两岁,AWS 只有 1 岁,而 Azure 和 GCP 还不存在。如果用 COBOL 时间来看这可能不算长,但用互联网时间来看,时间真是不短了。
在这 14 年中,支撑 GitHub.com(github/github)的核心存储库已经有超过 100 万次提交。绝大多数提交都是来自开发人员在 macOS 上的构建和测试工作。
但是,我们的开发平台在发展。在过去的几个月里,我们已经将 macOS 模型抛在一边,GitHub.com 的大部分开发都转到了 Codespaces。这是日常开发流程的一个根本性转变。Codespaces 产品的功能更强大,我们已经为 GitHub.com 未来的发展做好了准备。
近年来,为使本地开发工作可以快速开始,我们已经投入了大量的时间和精力。一段时间以来,我们所采用的 “脚本管理一切“的方法 为工程师提供了熟悉的界面——新员工可以克隆 github/github ,运行 setup 和 bootstrap 脚本,用半天的时间让一个 GitHub.com 本地实例运行起来。在大多数情况下,这都没什么问题,如果有问题,我们的 bootstrap 脚本会打开一个 GitHub 问题,在新员工和内部支持人员之间建立联系。我们的 Slack 频道 #friction——上面有许多乐于助人的友善的工程师——几乎可以调试天底下所有的系统配置。
尽管我们做了许多工作,但本地开发环境依然很脆弱。任何看似无害的修改都可能会导致本地环境不可用,更糟糕的是,需要花费数小时宝贵的开发时间来恢复。开发环境经常被莫名其妙的破坏,而是还是灾难性的,我们不得不在 bootstrap 脚本中增加了一个选项:--nuke-from-orbit 。触发该选项时,脚本会尽可能地删除它负责的东西,将本地开发环境恢复到一个已知的良好状态。
当然,任何从事软件工程专业的人一看就知道,这是一种很典型的情况。本地开发环境很脆弱。即使现在很完美,但一个针对特定上下文定制的本地开发环境与我们所处的即时在线、随处访问的世界越来越不相称。
在多个项目的多个分支上协同开发是很痛苦的。我们经常会发现,当一个分支引入了新的依赖,做了模式修改,或者从不同的 SHA 创建了分支,在启动时就得眼睁睁地等 45 分钟。鉴于我们的代码变化很快(我们每天部署数以百计的变更),这经常会导致工程摩擦。
并非只有我们注意到了这一点,在构建 Codespaces 的过程中,我们接触了多家一流的工程组织,他们已经构建了类似 Codespaces 这样的平台来解决同类问题。当规模比较大时,消除这类生产力损失可以快速提高生产力。
在基础设施领域,行业最佳实践一直将服务器作为一种商品。其思想是,没有哪一台服务器是独一无二、不可或缺或不可替换的。任何一个部件都可以被悄无声息的取下并替换。如果一台服务器宕了,那没什么问题,另换一台就是了。
然而,每个人的本地开发环境都是独特的,都有自己的习惯性设置。因此,需要时刻保持警惕才能维持。下一次 git pull 或 bootstrap 就可能会导致你的环境快速退化,需要进行昂贵的上下文切换才能恢复工作,而你宁可把这些时间用来构建软件。通常,我们并没有一台即取即用的备用笔记本。
但是,有必要强调一下,把开发环境看作是自己的——我们每天大部分时间都花在那里。我们会调整、优化我们的工作台,一方面是为了提高效率,另一方面也是一种个性的展示。
有了 Codespaces,我们就可以像对待基础设施那样对待我们的开发环境——一种我们可以不断更换的商品——但仍然保有个性化定制工作台的能力。我们借助 Visual Studio Code 扩展、设置同步以及 dotfiles 存储库将开发环境带到了我们个人的计算机上。这样,即使工作台出现了问题,也是小事一桩,我们可以用一个已知的良好状态配置一个新的编码空间,然后继续工作。
迁移到 Codespaces 可以克服现有开发环境的缺点,激励我们进一步推动产品的发展,提升整体的开发体验。
虽然我们的迁移故事有一个圆满的结局,但最初迁移的时候还是充满了挑战。GitHub.com 存储库占了将近 13GB 的磁盘空间;光是克隆存储库就需要 20 分钟。在加上依赖项设置,启动一个 GitHub.com 编码空间需要 45 分钟以上。而当我们成功将代码库加载到代码空间后,却发现应用程序不能运行。
我们不得不推翻 14 年来启动过程以 macOS 为中心的假设。
解决这些挑战成就了最好的 GitHub。全公司的贡献者都帮助我们重新审视以往的决策,质疑长期存在的假设,在代码层面将 GitHub 开发从 macOS 解耦。最终,我们可以(尽管很慢)在 Linux 主机上配置基本的 GitHub.com 代码空间了,从 Visual Studio Code 连接上去,然后交付一些工作。现在,我们得想办法让它活跃起来。
对于 Codespaces,我们的目标是可以根据手头的任务按需配置开发环境(分支和代码空间差不多 1:1)。为了支持基于任务的工作流,我们近乎实时地获得开发环境。45 分钟不能满足我们基于任务的需求,但是我们有很多地方可以优化,让我们先从简单的入手。
首先:改变 Codespaces 克隆 github/github 的方式。在准备开发环境的时候,Codespaces 现在不再全部克隆,而是执行一个浅层克隆,然后在包含最新提交的代码空间创建完成后,在后台对存储库历史做深层克隆。这样一来,克隆时间从 20 分钟缩短为 90 秒。
接下来:缓存支撑 GitHub.com 的软件和服务网络,包括传统的基于 Gemfile 的依赖、使用 C、Go 编写的服务以及一个自定义的 Ruby 构建。在该解决方案中,有一个 GitHub Action 会每晚运行,克隆存储库、引导依赖项,构建并推送克隆结果的 Docker 镜像。然后,发布的镜像会作为 github/github devcontainer(Codespaces 的 config-as-code)的基础镜像。现在,我们的代码空间创建 95% 以上都是自举式的。
这两个变化,以及若干应用和服务层面的优化,将 GitHub.com 代码空间的创建时间从 45 分钟缩短到 5 分钟。但是,5 分钟距离“即时”还有相当一段距离。众所周知,有研究表明,人们等待大约 10 秒 之后思维就会中断。因此,虽然我们已经取得了很大的进步,但仍然还有一段路要走。
虽然 5 分钟意味着巨大的改进,但我们在修改时也做了一些折衷,这意味着还有更一般化的产品需求。
我们的浅层克隆方法——对于快速启动 Codespaces 很有用——有时候仍然需要我们做完整的克隆。生成代码空间后的深层克隆又存在干扰。任何大型的复杂项目都面临类似的问题,在克隆和启动时会产生资源竞争。
如果我们提前克隆并引导存储库会怎么样?那样,当工程师申请代码空间的时候,我们已经完成了大部分工作。
预构建:代码空间池,已完全克隆并引导,只等想要开始工作的开发人员连接。我们在预构建方面的投资已经获得了无数次的回报:我们现在只需 10 秒钟就可以创建一个可靠的、预先配置好的代码空间,准备好 GitHub.com 的开发环境。
现在,新员工从零开始获得一个功能完备的开发环境比安装 Slack 的时间还短。工程师剥离出新的代码空间以实现并行工作,也没什么开销。当环境崩溃时——也许它太落后了,也许是测试数据造成了破坏——工程师可以快速创建一个新环境并继续一天的工作。
迁移到 Codespaces 解决了我们的一些实际问题:它消除了本地开发环境的脆弱性和单轨模式,同时也为我们提供了一个新的强大的杠杆作用点,让我们可以改进 GitHub 的开发体验。
现在,我们有了一个契子来执行额外的设置和优化工作,一些我们在本地环境中从未想过的工作,因为这些优化的成本(时间和耐心)太高。例如,借助预构建,我们现在可以事先准备好语言服务器缓存和 gem 文档,运行待处理的数据库迁移,并启用 GitHub.com 和 GitHub Enterprise 开发模式——这项任务通常需要在另一次引导、设置循环中完成。
借助 Codespaces,我们可以通过一条配置更改升级所有工程师的机器规格。在 Codespaces 迁移的早期阶段,我们使用 8 核 16GB 内存的 VM。那些机器足够了,但 GitHub.com 运行着一个包含不同服务的网络,它会很乐意消耗我们提供的每一颗内核和每一点内存。因此,我们转到了 32 核 64GB 内存的 VM。只需修改一行配置,我们就可以升级所有工程师的机器。
此外,Codespaces 还开始偷走我们的内部平台“评审实验室”的业务,这是一个类生产环境,我们和内部协作者在这里预览更改。在 Codespaces 推出之前,GitHub 工程师需要提交并部署一个评审实验室实例(经常需要同行评审),以便和同事共享他们的工作。现在,我们按住 ctrl 并单击就可以获取一个预览 URL 并发送给同事。不需要提交,不需要推送,不需要审核,也不需要部署。
Visual Studio Code 很棒。这是 GitHub.com 工程师连接代码空间的主要工具。但是,要求 Vim 和 Emacs 用户向图形编辑器提交就不那么好了。如果 Codespaces 是我们的未来,那么我们不能抛下任何人。
令人高兴的是,我们通过一个简单的预构建镜像升级为使用 shell 的同事提供了支持。该升级使用 GitHub 公钥 初始化 sshd,打开 22 端口,并将该端口转发到代码空间之外。
自此,GitHub 工程就可以运行 Vim、Emacs 了,如果他们愿意,甚至可以运行 ed。这一措施很有效。而且,就像 Docker 镜像缓存带来了预构建一样,很明显,下一步是让我们为 GitHub.com 代码空间所做的工作成为每个代码空间的头等体验。
改变是很困难的,涉及到开发环境更是如此。值得庆幸的是,GitHub 的工程师有很强的求知欲,同时也很友善——他们很快就变成了 Codespaces 的超级粉。
现在,Codespaces 成了 GitHub.com 的默认开发环境。至于前面提到的用于帮助调试本地开发环境问题的 Slack 频道 #friction,我们计划将其归档了。
GitHub 每天都会上线更多的服务,每天都会有工程师加入,关于 Codespaces 的价值创造,我们会发现新的故事。但是,每个故事的核心都是一个每名工程师都会产生共鸣的主题:我找到了一个更好的工具,我现在效率更高了,我不要再回到从前那样了。
原文链接:
https://github.blog/2021-08-11-githubs-engineering-team-moved-codespaces/