你好,我是葛俊。今天,我来和你聊聊研发环境的问题,也就是如何才能让开发人员少操心环境,甚至不操心环境。从这篇文章开始,我们就一起进入工程方法模块了。

第5篇文章关于“持续开发”的讨论中,我与你介绍了获取开发环境是开发工作的核心步骤之一,对提高研发效能来说是非常值得优化的环节。当时,我与你提到了开发环境服务系统以及沙盒环境,解决了开发环境的准备工作。

而这里的“开发环境”只是研发环境的一部分,特指开发机器环境,包括开发机器的获取、网络配置、基本工具以及代码的获取和配置。今天,我们就来看看整体的研发环境的配置问题,从全局上切实解决开发人员因为操心环境而导致的效能低下。

在此之前,你可以先回忆下是否对以下问题似曾相识呢?

这些问题,实际上都可以归结为研发环境不够高效。就像低劣的空气质量和食物质量会影响我们的身体健康一样,不理想的研发环境会严重降低研发效能。

具体来说,按照开发流程,软件研发需要以下几种环境:

接下来,我会按照这个顺序,分别与你介绍这5套环境如何配置。

因为Facebook在环境配置上做得非常好,比如说,他们可以保证开发人员在5分钟之内拿到一套干净的开发机器环境。所以,我会与你着重分享Facebook的实践,然后根据这些实践,总结出一套提供高效研发环境的原则。同时,我也会与你介绍一些我在其他公司的落地实践,帮助你掌握与环境配置相关的内容。

如何配置高效的研发环境?

开发机器

Facebook从不吝啬在开发机器上的投资。每个开发人员除了有一台笔记本外,还在远程数据中心有一台开发机器。数据中心的机器用于后端以及网站的开发,为方便描述,我称之为后端开发机。而笔记本有两个用处:一是用来做移动端App的开发;二是作为终端,连接到后端开发机做后端和网站开发。

两台机器的配置都非常强劲。后端开发机一开始是实体机器,后来为了便于管理和提高资源利用率,逐步转为了虚拟机,但不变的是配置依然强大。在2015年的时候,绝大部分机器配置都能达到16核CPU 、144G内存。笔记本有两种选择,一种是苹果MacBook Pro,另一种是联想ThinkPad,都是当时市场上顶配或者顶配下一级的配置。

开发机器的获取和释放,则是通过共享机器池以服务化自助化的方式完成的。更多细节,你可以再复习下第5篇文章的相关内容。

IDE

IDE,指的是集成开发环境。Facebook持续在IDE上投入,以完善其功能、一致性及一体化体验。接下来,我会以后端和网站开发为例来说明。

因为代码存放并且运行在后端开发机上,所以在2012年以前,绝大部分开发人员都是使用SSH远程登录到后端开发机,在那里使用VIM/Emacs这类能在命令行里运行的编辑器进行开发工作。也有人尝试在笔记本上运行GUI的IDE,远程挂载后端开发机上的文件系统,但总是有卡顿,效果不好。

在这种情况下,公司首先采用的办法是,尽量提高命令行编辑器的体验,把VIM/Emacs配置成类似IDE的工具,将最常见的功能,比如代码搜索、代码跳转、Debugger等集成进去,效果还算可以。

不过,与GUI形式的IDE相比,这种命令行的工作方式还是有一些局限性,比如,和其他工具服务集成不方便,需要记忆大量快捷键,在显示形式上不够丰富导致用户体验不够好,等等。所以,Facebook持续投入研究GUI形式的IDE。

第一个成型的GUI IDE是一个Web IDE,也就是在数据中心运行一些Web IDE服务。这些IDE服务连接到开发者的后端开发机上获取代码,同时提供网页界面,供开发人员用浏览器登录,进行开发工作。这种专门的IDE服务解决了远程文件夹挂载的卡顿问题,同时跟其他服务集成起来也很方便。

比如,它可以与Phabricator的代码审查功能集成在一起,在代码中用inline的方式显示其他人对你的代码的comments,也就是说,在两行代码之间显示一个额外区域,用于展示comments。类似的,它还可以方便地与代码搜索服务、代码跳转服务、Debugger集成,甚至还可以跟CI/CD工具链的发布工具集成,所以非常方便。

到2014年的时候,Facebook的大部分开发人员都逐渐转移到这个Web IDE上去了。如果你想深入了解Web IDE,可以参考下Apache Che项目。

这个Web IDE已经很方便了,但基于网页的IDE在易用性和安全性还是有一些局限,所以Facebook继续加大在GUI形式的IDE方面的投入,最后使用Electron框架实现了一个原生的IDE,也就是Nuclide

Nuclide的工作原理和Web IDE基本一致,都是在数据中心运行IDE服务,IDE的前端则运行在本地笔记本上,通过与IDE服务联通实现代码编辑等功能。只不过Nuclide的前端,是运行在开发者笔记本上的一个原生应用,而Web IDE的前端是运行在笔记本上的网页浏览器而已。Nuclide的功能比Web IDE更强大,易用性也更好,同时因为没有浏览器的依赖,安全性也更好一些。

本地环境、联调环境

关于Facebook的本地环境,我已经在第5篇文章中介绍过了,主要就是加快本地构建,使用生产环境的数据,从而使得本地环境更加快捷、方便。

而至于联调环境,我曾在第6篇文章中简单提过。在代码提交到Phabricator上进行审查的时候,Phabricator会调用一个沙盒系统创造出一个沙盒环境,运行正在被审查的代码。这个系统也是用机器池实现的,同时也是一个自助式服务,也就是说开发人员可以不通过Phabricator,直接调用API来生成沙盒环境。

本地环境和联调环境是开发中最高频使用的环境,对持续开发很重要。接下来,我再与你分享两个我在其他公司的实施案例吧。

第一个例子是,我在Stand公司搭建本地环境。Stand的业务规模小,因此并没有使用微服务,主要的服务只有单体的网站后端服务、数据库服务MySQL、缓存服务Redis,以及一些数据监控服务,相对来说比较简单。

我们的做法是,把这些依赖服务尽量在本地开发机器(也就是笔记本)上都运行一个实例。实在不能在本地运行的服务,要么在本地环境运行时不调用它,要么就在调用它的时候传递额外的参数,表明这个调用来自开发环境,而被调用的服务则针对这样的调用进行特殊处理,从而达到不污染线上环境的效果。

这是一个很常见的办法,简单有效。不过,它的缺点是本地环境数据跟线上环境有区别。

如果你的系统采用的是微服务,则可以采用以下3种常见办法。

第二个例子是,我为一个云产品团队提供联调环境。这个云产品结构非常复杂,有十多个服务,至少需要3台服务器;不但有软件,还有数据、组网等复杂的设置,部署很困难;更严重的问题是,这个环境一旦损坏就很难修复,需要从头再来,所以开发人员自己配置基本不可能,运维人员也是忙于维护,应接不暇。

针对这个问题,我们也是使用了机器池的办法。不过,这个机器池里面的单元不再是单个机器,而是由3台机器组成的服务。这个服务自助化提供给开发者使用,使用之后自动回收销毁。

同时,确保机器池中有两套空闲环境。不够就补充,多了就删除,以保证获取环境时可以马上得到,同时又不会因为有太多空闲环境而造成资源浪费。

另外,每次有了新的稳定版本,运维人员都会更新脚本,并重新安装和配置系统,保证开发人员能够在稳定版本上进行联调。

这样一来,就解决了团队开发人员的环境问题,将获取环境的时间由2~4个小时缩短到了几分钟。

开发过程中使用的各种工具、数据和配置

除了IDE,开发过程中还会用到其他工具,比如代码搜索、发布部署,以及日志查看工具等。这些工具的组合,可能与你平时理解的开发环境不大一样,但事实上它也是一种广义的开发环境,对开发效率影响很大。

这部分环境的优化,主要是使用工具之间的网状互联来提高效率。关于工具的网状互联,我在第4篇文章中有介绍。这里,我想强调Facebook的另一个重要实践:重视开发体验,将开发流程中常用步骤的自动化做到极致

我用一个具体的例子来说明。我们都知道,Git 的Commit Message(代码提交描述)是提供信息的重要渠道。但它有一个局限,就是只能存储文本,而图片在描述问题时常常比文字有效得多,也就是“A picture is worth a thousand words”,翻译为中文就是我们常说的“一图胜千言”。

为解决这个问题,Facebook采用了以下方式:

  1. 提供了一个图片存储服务,并为上传的图片提供永久URL;
  2. 开发了一个端测的截屏工具,截屏之后自动上传到图片存储服务,而且,在上传成功之后自动把图片URL保存到本地笔记本的系统剪贴板中;
  3. 提供了一个内部使用的URL缩短工具,避免URL太长占用太多文字空间。

这三个工具集成起来,一个具体的使用场景是这样的:我在写Commit Message的时候,如果要截屏描述修改效果,就使用一个快捷键(比如Cmd+Alt+4)激活截屏工具,随后拖动鼠标截屏,然后使用Cmd+v就可以直接把图片的URL粘贴到Commit Message里面了。

这个工作流非常顺畅、高效,不仅被大量用于Commit Message的书写中,也经常被用在聊天工具中。后来,我到了其他公司,都会先配置这样一套工具流程。

测试环境、类生产环境

在测试环境、类生产环境的管理上,Facebook使用一个叫作TupperWare的内部系统,以IaC的方式进行管理。不过,Facebook并没有开源这个系统。如果你所在公司使用的是Docker,那可以使用Kubernetes实现类似的功能。

如果你们没有使用Docker,可以试试HashiCorp公司的Terraform,或者使用AnsibleChef之类的配置管理工具,来产生一套干净的环境供团队成员使用,之后再销毁,既方便又不浪费资源。

这里,我再与你分享一个我在Stand公司使用AWS管理压测环境的例子。当时我们没有使用Docker,于是,我们选择了AWS的OpsWorks框架。它是AWS基于Chef-Solo开发的一个应用程序管理解决方案,同时支持基础设施的建模和管理。

OpsWorks这个框架的用法也是声明式的,只不过这个声明不是纯代码,而是在AWS的网页上配置的,使用效果和TupperWare差不多,就是首先定义一个压测环境需要几台机器,需要运行什么操作系统、需要什么负载均衡器、需要什么数据库等。

然后,通过OpsWorks上暴露的钩子,使用代码来管理应用的生命周期,从而实现系统和应用的初始化。通过这种方式,我们可以很方便地使用OpsWorks产生一个云主机集群用于压测,结束之后马上删除,方便而且划算。

其实,使用Ansible或者Chef也可以实现类似功能,但需要自己开发些东西,这里我就不详细讨论了。

提供高效研发环境的原则

通过以上实践可以看出,配置高效的研发环境主要包括以下几条原则:

  1. 舍得投入资源,用资源换取开发人员时间。Facebook之所以从不吝啬在开发机器硬件上的投入,是因为人力成本更高。
  2. 对环境的获取进行服务化、自助化。这一点可以在开发机器、联调环境的获取上,得到很好的体现。同时常常使用IaC、配置管理系统(比如Chef)和机器池的方法来实现,同时利用弹性伸缩来节约资源。
  3. 注重环境的一体化、一致性,也就是要把团队的最佳实践固化下来。比如Facebook一个常见的操作是,配置文件统一处理。以VIM为例,将VIM的配置文件存放到网络共享文件夹中,开发人员只要在自己的.bashrc文件中加上一行就可以搞定。
source /home/devtools/vimconfig

小结

今天,我首先按照开发流程,也就是开发机器、IDE、本地环境和联调环境、开发过程中使用的各种工具及配置,以及测试环境和类生产环境的顺序,与你讲述了高效研发环境的具体实践。然后,基于这些实践,总结了3个基本原则:一是,用资源换时间;二是,服务化、自助化环境的获取;三是,实现环境的一体化、一致性。

我认为,这些原则和实践的背后有一个重要思路,就是Facebook重视环境,并持续优化环境。这一点在IDE的演化上尤为明显,从命令行到Web IDE再到Nuclide,一直在进步。另外,在去年年底,Facebook停止了对Nuclide的开源项目的维护,这也意味着后面他们可能还会有对IDE的一轮新的优化。

以上种种做法,使得我在Facebook做开发的时候,对研发环境的感觉就是不用操心,需要使用的时候直接到网站上申请就可以使用;配置也方便,团队的配置都已经在那里了;同时,环境中的各种工具、流程都很顺畅,让我能够静下心来做开发、写算法,做我最能提供价值的事情。

思考题

我在“开发过程中使用的各种工具、数据和配置”这一章节中提到的截屏工具流程,你觉得价值大吗?值得引入你所在的公司吗?如果值得的话,可以怎么来实现?

感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

评论