设想在你的笔记本计算机上进行开发,利用云计算的算力!在 LinkedIn,我们已经将大部分产品的初始设置和构建时间从 10~30 分钟减少到 10 秒,并且为用户带来了全新的远程开发体验。在这篇文章里,我们将介绍我们实现这一点的历程。
身为 LinkedI Developer Productivity and Happiness team(开发者效率与幸福团队)的一员,我们常常会从开发者那儿听说,开发进度太慢,以及环境设置上的问题会对他们的工作效率造成多大的影响。LinkedIn 拥有一个包含各种技术的、庞大的生态系统,它包括 Java、Python、C/C++、Go、JavaScript、iOS 和 Android,可以满足不同的需求。拥有一个庞大的生态系统是有其优势的,但是每个技术的设置都会有差异,并且,新的开发者常常要花费很多时间来设置自己的开发环境。
新冠肺炎大流行期间,在笔记本计算机的 CPU 处理能力、内存和磁盘容量受限的情况下,我们都在进行远程工作,这就更具有挑战性了。与台式机或者服务器级别的计算机相比,笔记本计算机的 CPU 内核、存储和磁盘容量都要少得多,而且会被热节流所限制。另外,其他在后台运行的软件和不完善的网络也会对系统的性能造成更大的影响,从而导致构建速度缓慢。考虑到 LinkedIn 的持续集成(Continuous integrationCI)管道每天要处理的构建规模,CI 构建失败以及本地和 CI 构建之间的不一致也是支持工程师面临的一个重要问题。
LinkedIn 的远程开发活动就是为了解决上述问题,它的目标是为所有的开发者提供可远程访问、可靠、一致、可预测、快速构建、易于设置的远程开发环境,无论他们的本地设备和网络连接如何,都能满足他们的项目需求。我们将这种远程开发环境称作 RDev(Remote development environments),这是一种为特定产品设置的容器,它包含了开发需要的所有工具和软件包。RDev 实例是在我们的私有云中强大硬件上创建的,它在网络上运行时所需的服务时延非常小,比如克隆和下载依赖关系(见图 1 所示)。
图 1:在多次迭代中,以秒为单位,对下载单个依赖关系的时间进行了测量。
我们将 RDev 与开发者最喜欢的 IDE 集成,利用了远程 SSH 功能,提供无缝的开发体验,让开发者有一种在本地开发的感觉。LinkedIn 的一个大型 Play 应用程序的平均构建时间如下图 2 所示。很明显,在 RDev 中,构建时间要短得多。
图 2:对于不同的操作系统/内核,我们的应用程序的构建所花费的平均时间。
在这篇博文中,我们将会介绍我们是怎样使用现有的基础设施和产品生命周期来完成这个基于容器的远程构建和开发环境。我们也将与大家分享一些详细的内容,包括我们怎样利用 RDev 来缩短初始设置时间,以及在整个开发和 CI 生命周期中保持一致。
利用预构建的 RDev 对开发者的需求进行预测
我们维护了一个预构建的 RDev 环境池,基于以前 RDev 的使用模式,可以根据需求来为开发者分配 RDev。预构建的 RDev 包含了启动容器、签出产品、设置环境、构建产品以及使应用程序运行,这样开发者就无需考虑启动应用程序的问题,就可以立即开始工作。这可以为开发者节约很多时间,如下图 3 所示。
图 3:应用程序的克隆和构建时间的本地与预构建的 RDev 进行比较。
构建过程会因产品类型的不同而不同,因为一些产品具有特定的持续构建过程,通过 inotify 观察文件系统并保持构建的进行(例如,Ember 构建的 JavaScript 产品)。即使是普通的产品,构建过程都会返回一个退出代码,也需要记录构建的输出。这可以通过在一个 tmux 会话中运行构建来实现,在得到分配的 RDev 之后,开发者可以访问这个会话。
延伸 RDev 的优势到持续集成管道
开发(在 RDev 中)、构建和部署(在 CI 中)的能力,都可以通过同一个容器实现一致性和可重复性的额外好处。
为了获得这些好处,我们更新了 CI 管道中的构建步骤,并委托它在容器内运行现有的 CI 任务。这个 CI 容器是通过 LinkedIn 的映像基础设施生成和维护的映像创建的(在下一节中解释),它可以被用来进行远程开发,也可以用来构建 CI 工作流。这种方法很像 GitHub 的 “running-on”和“container” 指令的工作方式。
它是如何工作的?
让我们来了解一下,我们是怎样利用一系列巧妙的技巧,把构建时间降低了两个数量级。
图 4 显示了远程开发生态系统的主要组成部分。
远程开发架构
基本映像基础设施
基本映像基础设施将构建容器映像与我们的 CI 管道整合在一起,并帮助开发者轻松地为内部的 LinkedIn 容器映像注册表创建和发布自定义映像。我们有一套针对某些技术的模板映像,比如 Python、Java 和 JavaScript,这些都是开发者可以直接使用或者进行扩展的。
对于“映像”产品的每一次 CI 构建,都会创建一个依赖关系图,其中包含了该映像的所有 RPM 信息和父级基本映像信息。这个依赖关系图支持一个图像依赖关系更新服务,可以将所有 RDev 映像保持最新。它可以从内部 RPM 中接受所有可用的更改,并利用它们来重建映像。任何包含这些 RPM 的映像以及任何相关的映像都会被直接更新。这些映像在 RDev 配置和 CI 中都用来创建开发容器和 CI 构建容器,从而支持一致的开发和构建环境。
RDev 配置
我们遵循 VS Code 的容器配置格式。基本的容器配置,如映像名称、环境变量和要从容器内转发的端口,都在产品库的 root 目录中的 devcontainer/devcontainer.json 文件中以声明方式进行了描述。
RDev CLI
RDev CLI 是一个 Python CLI,它被分发到所有开发者的机器上,具有创建、连接(通过 CLI 或 IDE)和管理这些远程开发环境所需的命令。
RDev 服务器
RDev 服务器是一个 Rest.li Python 服务,充当 CLI 和 Kubernetes Operator 之间的代理。它负责将请求转发给 Kubernetes Operator,查询其结果,并与我们存储开发者偏好和元数据(如 dotfiles)的数据库进行交互。
RDev Operator
我们通过利用 Kubernetes Operator 模式和定义 LinkedIn 特定的自定义资源定义(Custom Resource Definitions,CRD),对 Kubernetes API 进行了扩充。
我们定义了两个 CRD:Rdev 和 RdevPool。Rdev CRD 表示一个单实例有状态应用程序,它的规范有足够的信息可以从头开始重新创建自身。RdevPool CRD 包装了 Cloneset CRD,用于维护预构建的 RDev 池。RDev Operator 利用 Operator SDK Kubebuilder 框架,作为这些 CRD 的控制器,将其当前状态与所需状态进行协调。
Pod 架构
如图 5 所示,RDev 与一个服务相关联,该服务是向 Kubernetes 集群外部公开端口所必需的。节点端口用于公开服务器。
持久卷声明(Persistent Volume Claim,PVC)是保留持久卷(Persistent Volume,PV)的必要条件,以便存储非易失性数据;在这种情况下,这些数据是 RDev 的主目录。当下面描述的 Pod 需要移动到另一个节点或被意外删除时,这一点就至关重要。
每个 RDev 都由一个 Kubernetes Pod 备份,该 Pod 由三个不可变的容器组成:RDev-init-workspace、RDev-sshd 和 RDev-sidecar。它还有两个主要的卷挂载,Home 和 Rdev Info,以及其他与证书和安全有关的必要卷。
容器:
rdev-init-workspace:这是一个 init 容器,用于准备开发者的工作区和偏好。
rdev-sshd:为 RDev 提供登录服务的容器。这个容器是由产品的 devcontainer.json 文件指定的映像创建的,包含了容器中开发所需的所有工具,并运行 sshd。
rdev-sidecar:负责检查和安装 dotfiles 的容器,同时也运行 Startup Probe(启动探针,在下一段描述)。这个探针用于确定 RDev Pod 是否完全构建并准备分配给开发者。
卷挂载:
Home volume:顾名思义,Home volume 是开发人的主卷,它将签出产品,安装开发者的 dotfiles,设置环境变量,以及为开发者配置的用户配置文件。
Rdev info volume:包含使用 Pod 的标签和注释填充的主机和端口详细信息,利用向下的 API。
正如前面提到的,RdevPool 是一个 Cloneset,它根据配置的副本数量维护一个 RDev 池。一旦 RDev Pod 被创建,PostStart 容器钩子会触发 rdev-sshd 容器中的构建命令。在 rdev-sidecar 容器中运行的启动探针不断探测,以确认构建是否已成功完成。它通过寻找记录构建输出的文件,或通过使用 curl 获取配置文件中提供的 URL 来确定产品是否已构建。启动探针成功后,RDev Pod 被标记为“准备就绪”,以便分配给开发者。
当开发者请求一个 RDev 时,RDev 控制器将寻找一个完全构建的未分配的 Pod,取得 Pod 的所有权,并将其从 RdevPool 控制器中移除。RdevPool 控制器将注意到它的一个 Pod 丢失,然后创建一个新的 Pod 以维持 RdevPool 规范中提供的副本数量。
展望未来
由于远程工作已经渗透到了我们的日常生活中,我们认为,不管在哪里,远程开发对于 LinkedIn 开发者的一流开发体验来说都是一个巨大的动力。
我们对于未来的远程开发所提供的支持非常激动,比如:
通过为每个失败的执行提供相应的 RDev,重新生成失败的 CI 构建并简化调试体验。
通过将 RDev 和每一个 GitHub 的 RDev 相关联,这样可以让审阅者对更改有一个直观的理解,这样可以提高审阅的体验。
作者介绍:
Shivani Pai Kasturi,LinkedIn 高级软件工程师。
Swati Gambhir,LinkedIn 主管软件工程师。
原文链接:
https://engineering.linkedin.com/blog/2021/building-in-the-cloud-with-remote-development
评论