本文的第一部分中,我们不仅讨论了Codenvy 平台和其基础架构,还讨论了云IDE 与桌面IDE 之间的差别、IDE 工作区管理、SDK、插件架构和生命周期、Codenvy.com 架构–用户管理和身份认证、公开vs 私有项目、IDE 协作、多租户等等。
4.9 虚拟文件系统
在 Codenvy 平台上,一个开发者的工作空间是由多种物理资源虚拟而成的,这些资源用来完成不同的 IDE 功能。
依赖性管理、构建器、运行器和代码助手能在不同集群的物理节点上运行。为了适当地虚拟化对这些资源的访问,我们需要实现一个 VFS,它不仅能支撑各种服务和物理资源,而且还本地了解 IDE 的行为。
一个云 IDE 需要一个来保存用户项目的存储系统。Codenvy 使用的 VFS 需要拥有以下特性:
4.9.1 要求特性
- 它应是 C/S 架构,客户端主要以 Ajax 请求方式通过 HTTP(REST API)来访问其服务器端。不同的 IDE 浏览器客户端都可以通过这种方式访问项目资源。
- 它的 API 不应绑定到任何一个文件或内容管理系统。它应是足够灵活的、抽象的,这样才能相对容易地将它们用作一个后端存储库。比如,其 API 可能已绑定至某个 JCR 系统或基于 POSIX 的文件系统。
- 它应是多根的,就是说一旦某个用户进入了一个域或工作区,他们访问自己筛选过的分支。由定义,这就意味着一个单一的 VFS 能由此拥有着多个视角(每域、每工作区或每用户),而“全局的”VFS 则是全体的视图。多根能力为某个视图应用访问控制列表提供了必要的基础,而这对于公开 / 私有的项目实施来说都是必须的。
- 它应支持文件和文件夹的标准 CRUD 操作,允许使用路径和 UID 来相对和绝对寻址。通过使用基于 UID 寻址的 CRUD 操作来扩展一个 AJAX 系统,这样开放了灵活性,允许那些非 Codenvy 的客户端和浏览器直接与项目空间一起工作。
- 它还应具有其它功能,比如访问权限、搜索(包括全文搜索)、加锁和版本管理。一个具体的 VFS 实现都应包含有这些能力。
- 它应包含另一个一级的的项目资源类型,它给文件夹增加了特殊的属性和功能。事实上,我们自己的 VFS 已经扩展成能将不同的文件夹节点分类为不同类型的一级节点,它们随后可以继承特定的行为。该 VFS 包括了项目、模块和包。完全可想象的是它能扩展成具有诸如源码、库等等其它独特项目特性。
4.9.2 实现
由于我们拥有内容管理系统(如 eXo、JCR、xCMIS)和 REST API(如 everREST)实现的经验,决定不采用如 WebDAV 或 CMIS 这样基于 HTTP 的传输方式。因为这些选择过于复杂并有很多冗余数据交换,所以它们不算是一个很理想的解决方案。我们定义了自己的特定于 IDE 的 VFS REST API。
我们在开发和改进过程中创建了几种不同的后端实现,这包括:
- JCR:文件系统作为一个 JCR 项(节点和属性)保存。其当前用于某个由 eXo 平台所 OEM 的 Codenvy IDE 中。该 JCR 实现提供了可通过 IDE 访问的文件的本地版本管理,但同时也可以执行某些 git 操作,这些操作是文件密集的,非常慢。
- POSIX:这是我们产品当前所采用的实现。POSIX 是一个普通的文件系统,工作于一个 GlusterFS- 分布式文件系统之上。
- 内存驻留(In-Memory):我们为 QA 单元测试还创建了一个内存驻留的实现。
4.9.3 虚拟文件系统入口点(VFS 工厂)
登入一个连接到某个工作区的用户帐号后,用户就可以访问工作区相关的 VFS 视图。在某个浏览器登录后,VFS 会为这个浏览器分配一个令牌,浏览器可以将它与 VFS API 一起使用来访问正确的 VFS 视图。此浏览器然后就可以直接使用 REST 来访问 VFS 了。
下面是 VFS REST 服务一个入口点的 URL 结构:
http(s)://
我们使用 JSON 响应来表示 API 所返回的 VFS 信息,响应中包含有一个唯一的 VFS 标识符、一个指向根文件夹的指针、支持功能列表、一个 URL 模板列表,这些 URL 模板可用作访问某些 VFS 功能的超链接。
可选的功能则依赖于当前的 VFS 实现,可能包括有控制访问权限的访问控制列表(ACL)、文件版本管理、加锁、查询(搜索)。
4.9.4 主要资源:项目、文件夹和文件
VFS 有树状的结构,而项目则位于工作区的顶层。
展开树状结构可以看到文件夹、文件,在某些特定的场合还有子文件夹(模块)。我们使用不同的 API 调用集合来展开树,并且我们区分了项目节点和项目的子结构视觉表现。
上面有 3 种不同的资源类型:
- 文件:可被归为不同的一类,其本身带有有用(可索引的、可搜索的)内容。
- 文件夹:标准的结构单元。
- 项目:一种特殊的文件夹,它拥有一个可帮助识别项目的性质、合适行为和视图等等的属性集。
这 3 种资源类型的层次式组织以如下方式管理着 VFS 的结构:
有 3 个很重要的层次规则管理着 VFS 的结构:1)只有项目才能是最上层的资源(也就是说它的父节点是工作区的根文件夹);2)项目可以含有文件、文件夹或其它项目(对于多模块的项目而言)子资源;3)文件夹可以含有文件或文件夹子资源。
4.9.5 基于 JSON 的虚拟文件系统
所有的文件查找、加载和访问都是通过一个自定义的 API。这个 API 在不同的 IDE 客户端之间使用 JSON 来回传递参数。我们来比较下云 IDE 和桌面 IDE 的文件访问。在桌面 IDE 中,应用能本地访问磁盘驱动器,使用着本地命令来操作文件,并依赖于操作系统来为锁定、查找定位和其它形式的访问提供关键的功能。
然而在云环境中,位于很多物理节点上的 IDE 同时在操作。不同的 IDE 之间使用同样位于分布节点上一套代码助手、构建器、运行器来互相协作。使用不同的 IDE、位于不同节点的多个开发者可以同时访问某个工作区。于是一个 VFS 的角色不仅是提供文件访问,而且还提供分布式、可控制的文件访问。
无论客户端是否运行在我们的基础设施之内还是使用一个浏览器直接访问,我们都可以通过结合使用 RESTful API 和 JSON 来规范不同类型客户端所使用的技巧。我们需要将与文件操作和访问相关的那些核心系统功能封装成这种格式。
4.9.6 虚拟文件系统功能
Codenvy 的虚拟文件系统 API 为既为步进式资源导航提供了方法,也为直接使用其唯一标识符(UID)或路径来访问特定资源提供了方法。你可以通过根文件夹来访问数据。
虚拟文件系统的结构已经允许使用非 IDE 中的 HTML 应用来查询、搜索、项目排序。这样开发者就可以创建自己的应用,并和位于 VFS 上 Codenvy 工作区直接交互。创建一个使用 POST 请求参数的查询语句是特定于实现的
虚拟文件系统可能也支持很多功能,包括监控、访问控制、文件内容版本管理。每个功能都是特定于实现的。
4.10 记录、分析和管理控制台
我们有着下面的用例,这些用例是关于系统中收集的数据和事件的:
- 测量用户的获取数、参与度和推广程度,以便改善体验,提高采用程度以及提高客户满意度。
- 洞察用户、雇员、管理员行为以便跟踪管理。
- 导出和生成最终可被产品采纳的见解,能供用户提高生产率。为独特的、企业和 ISV 客户生成购买合约中所约定的报告和见解。
- 为外部受众的查询、调查和研究使用而开放系统所收集数据。
(点击图像放大)
由于需要创建一个在内部工作时也能像在云中一样好的解决方案,所以像 loggly 这样仅限在公有云上使用的服务就只好放弃。我们需要一个可以被 OEM 的并可在不同的云环境中工作的系统。
我们使用了所有主要的客户端和服务器端事件。它们都是像登入和登出一样的典型事件。但是就如“重构”、“构建”、“调试”、“步进”、“预览”、“导出”一样,这些也是开发者的头等事件。这些事件记录为基于文件形式的消息,它们位于每个物理节点之上。消息存放在可长期保存信息的存储库中,并拥有归档策略,它可以随着消息老化进程而移动消息位置。我们使用 pig/hive 来编程查询,从而处理消息并从中导出各种度量。消息和度量二者都存放在 Hadoop 中。因为存在大量的分析、报表、各种管理控制台解决方案—能提供极好地浏览、分析和数据关联体验而且我们可以选择安装的,我们在本解决方案中选择使用 Hadoop。
现在我们运行着三个提供已分析数据的服务。我们也有一个管理控制台,它是一个可以展示关于用户、采用、参与和推广的分析数据的简易的网站。我们也提供很多 CSV、Excel 和 PDF 格式的已生成报告以供管理,这些报告是在可重复或参数化的基础上生成的。最后,你们还可以通过一个 RESTful API 来查询访问这些数据和度量值。这个 API 将会最终封装打包并提供给开发者以便他们可以直接使用。
4.11 托管的 API
Codenvy 中的每个服务器端的服务,比如重构、身份认证、构建、部署等,都是以一个 RESTful web service 形式构建和提供。可以通过下面的方式访问这些 Web Services:
- 内部 Shell:预定义的 Groovy 命令集,使用参数数据来调用 Web Services。
- 外部程序:其注册了一个 id 并直接访问 Web Services。
在产品之内已发布了这些 Web Services。你可以使用“帮助—REST 服务发现”获取关于服务包括传参在内的详细信息。下面是节选自通过 URL 可访问的函数类型和参数组织部分:
服务路径
方法
消费
生产
简介
角色
用户服务
/organization/users
POST
application/json
application/json
创建用户
cloud/admin
/organization/users(id)
GET
application/json
通过 id 查找用户
cloud/admin,
cloud/manager,
developer
/organization/user?alias=alias
GET
application/json
通过别名查找用户
cloud/admin,
cloud/manager,
developer
/organization/users/(id)
POST
application/json
application/json
更新用户
cloud/admin,
developer
/organization/users/(id)/remove
POST
删除用户
cloud/admin,
developer
/organization/users/authenticate
POST
application/json
用户认证
4.12 SHELL 技术
我们当前的 shell 是一个浏览器层,它可以访问内部的 Web Services。我们并没有为用户提供一个直接的 bash shell,然而在不久的将来我们会提供一个 SSH 会话,它可以连接到一个映射到某个私有的构建器 / 运行器队列的专用虚拟机。这个 SSH 会话会拥有 bash 功能。在很多方式上,集成于 IDE 的非 SSH 的 shell 是一个虚拟的 shell,它是对我们 Web Services 的顶层抽象。我们使用开源的 EverREST 项目简化这些调用,该项目是一个 JAX-RS 实现。这儿可以了解EverREST 更多信息。
4.13 IDE、构建器、运行器集群管理
IDE 和构建器 / 运行器之间通过 Web Sockets 连接。在我们的服务器端基础设施中运行着 BuilderManager 和 RunnerManager 服务,它们管理来自不同 IDE 客户端的请求并将之路由至合适的服务。客户端上下文环境(比如付费和非付费)决定了请求所映射到的队列。不同的队列拥有不同的处理策略,这些策略比如有专用的虚拟机、共享虚拟机、或带有 SSH 访问的虚拟机等等。
用于运营 IDE 集群、构建器集群、运行器集群的物理节点的数目由 HAProxy 所决定。我们拥有可配置的准则,该准则可以推断在每个集群中什么时候一个节点应扩张或收缩。对于 IDE 集群,准则是基于活跃的租户数。如果阀值是 300 个活跃租户的话,还会有一个预冲击阀值,当达到这个值时,将会创建一个新物理节点。同样的过程也会在活跃租户数下降时发生。在我们需要关闭一个物理节点的场合中,我们不会做任何将租户从某个节点迁移到另外一个节点的工作。当下次该租户连接到服务时,它将会在另外一个当前在线的节点上重新激活,而同时也会恢复其服务状态。对于构建器和运行器来说,所发生的过程是类似的,然而每个系统行为的配置准则都是唯一的。就构建器来说,我们优化了构建器的数目,试图消除任何阻塞行为。对于运行器来说,同样也是如此。
4.14 云连通性服务
对于任何需要连接至如像 GitHub、App Engine、或某个持续集成服务器这样的外部云服务的 IDE 功能,我们一直使用第三方所提供的直接 API 来访问这些服务。我们使用一个位于自己的云基础设施之内的代理服务器来控制所有的通讯。代理服务器可以同时处理向外和向内的来自所有第三方的 API 调用,我们还可以利用它们的性能和操作的可配置性。
4.15 集成
从逻辑上看,下面就是系统如何全规模运行的情形—同时运行在多个节点上。
(点击图像放大)
- 客户端的浏览器加载 Codenvy 网站,并提出某些基于 URL 的请求。请求有两种类型:正常的(业务逻辑)和元数据的。
- 如果是正常的请求,HAProxy 则加载负载平衡器来决定其路由。
- 如果是元数据请求,云管理节点则执行像租户创建或删除等这样的特定行为。
- 当执行元数据请求时,云管理节点可能会更新 HAProxy 的配置,指导其在以后应如何处理该租户。
- 一个业务请求会路由至某个应用服务器上动态部署的一个 IDE。
- 当执行元数据请求时,云管理节点使用内部的 REST 请求来向位于某个应用中的执行 IDEs 池功能的云代理提出请求。云管理节点也能指示系统添加或移除额外的应用服务器节点,这些节点根据扩展规则使用作 IDE、构建器或运行器。
- 某些元数据请求(比如身份认证)会调用组织的 LDAP 服务器中的数据库。这些 LDAP 服务器含有帐号、组织、工作区、用户信息和配置。
- IDE 调用组织数据库来访问帐号信息、付费 / 免费状态、还有游戏化度量值。
- IDE 调用内部如构建器、运行器和代码助手这样的服务。
- IDE 使用虚拟文件系统接口访问项目、代码、存储仓库的信息。
- 统计数据存储从应用获取日志供将来分析使用。IDE 访问保存的统计数据的目的是为用户提供统计信息和关于提高生产率的建议。
- 向云管理控制台发出的请求会触发我们分析系统的事件记录,事件记录首先是以文件形式保存于文件系统中,但最终会存入 Hadoop。
- 你可以使用 CLI、编程 APIs、GUI 客户端来管理云管理节点。
- 管理者可以通过一个专用的访问 Hadoop 和其它分析系统的 GUI 客户端访问控制台、报告和统计数据。
5.0 发布模式
5.1 环境结构
我们拥有多种环境,这些环境为来自于我们自己的过程的不同的需求提供服务。这些环境如下:
目的
部署地点
产品(production)
具有全 SLA 的、技术支持、内建弹性的产品环境
AWS
预发布(pre-production)
运行着一个带有某些面向产品部署的成品功能 / 问题的完整的 Codenvy 环境。这些功能可能是在一组 sprint 中完成的。该环境是用于:和客户一起验证,创建相应的示例的文档化工作,支持为即将发布的特性所进行的培训,便于营销从新版本中创造有用资源。
内部数据中心
测试环境(staging)
是一个可以被来自某个特定 sprint 的问题所升级的模拟产品环境。这个环境用作扩展性测试、热修复验收、用于配置至在一个云环境中的第三方服务的连接。
AWS
验收(acceptance)
执行开发团队所完成的特定功能,其等待来自某个产品拥有者的验收。我们运行多达 5 个的验收环境,它们可以通过我们的持续集成系统自动创建。验收环境是单服务器的,不具弹性。
内部数中心
5.2 SCRUM 过程
作为一个为开发者创造工具和解决方案的公司,我们花费了大量的时间来就项目管理和客户进行讨论。我们将与客户分享 Codenvy 的内部过程和技巧置于优先的地位。这有助于我们围绕着最佳实践进行讨论,有助于改进我们的内部过程,并且能更进一步地提高对于开发者所面对的复杂问题的洞察力。
在过去的 5 年中,我们使用 Scrum 作为推动开发的主过程。Scrum 是一种迭代的、增量式的开发方法学(IID),它可以帮助指导和塑造我们的日常工作。它是一种灵活的方法,可以帮助开发者集中精力于开发,同时仍能提供能保证一切都按时运转的纪律性。
Scrum 为我们提供了推动自己快速产品进化所需要的迭代计划、规范、执行、测试和分发能力。
我们的产品经理担当 sprint 项目主管(PO)。非开发者作为 PO,我们这些开发者就可以关注于编程而不是基本的项目管理了。按照这种方式,产品发布过程与产品开发过程保持分离。
我们使用带有 Greenhopper 插件的 JIRA 作为项目的问题跟踪工具。
开发工作是在用时约为 2-4 周的“Sprint”中进行,以便将每个发布都分解成可管理的分段。在那段时间内,我们集中精力于将最新路线图中的特定特性转化为已测试的代码。
Sprint 的固定周期是基于工作量和团队目前的工作能力。这些周期可以是灵活的,但是只能在审查和讨论过其如何影响 Sprint 目标之后才能变更。
在一个 sprint 开始之前,我们的开发团队从每个角度检查 PO 所提出的特性,并创建详细的概括了正确规范的“sprint 摘要”。他们将这些摘要展示给 PO,确保每个人都对预期目标达成一致。
在 Sprint 中,我们的开发团队举行每日站立会议。团队成员讨论当前工作的状态、任何发生的问题或障碍。我们的会议是短时间的(~15 分钟),所以它们不会占用太多的编程时间,而且它们可以帮助大家都处于同一进度。
在Sprint 结束时,我们的开发者为PO 演示这些特性。在演示过程中,我们的PO 查找在“sprint 摘要”中所提出的特定特性。这个验收工作发生于我们开发环境中所运行的专门Codenvy 实例上,在这个环境中可以允许验证单个问题。
一旦所有的事情都得到批准,我们的团队和PO 在一个回顾会上分析结果,并提出可能在下一次迭代中能得以改进的区域。在一个sprint 完成之后,在这个sprint 中所编目的所有的问题都被移动到一个预发布和临时工作台环境中。在那里,市场、支持、以及文档人员能在发布成产品之前完成其它工作。
我们从Scrum 中获得的制度和自由的结合是集二者之长。Scrum 给我们开发者提供了创建高质量解决方案所需的灵活性,同时还不会牺牲最后期限的纪律性。
5.3 自动化测试
我们的系统大约有 95% 的自动化测试覆盖率。在任何 IDE 和网站功能测试中都使用了 Selenium。我们用 JUnit 做服务器端测试,同时在基于 GWT 的插件中测试功能性方法。我们对于那些尚无法自动化的用例做日常手工测试,比如与 PaaS 提供商的集成,在这里连接速度能改变接收到的结果。我们所有的测试工作是作为我们持续集成过程的一部分自动启动的,并且每天至少运行一次,这是在某次检入之后开始。
5.4 运维技术
如果是部署在某个云环境中,我们使用 Amazon 和 Eucalyptus 所制作的镜像来配置和部署。如果是部署在某台 pc 中,我们则使用自制的 RPM 包和 bash 脚本。配置环境进一步通过 Puppet orchestration 来管理。
我们使用了一个很简单的 monit、logwatch 和 cacti 实现来监控所有系统的可用性。
作者简介
Tyler Jewell是 Codenvy 的 CEO,并且是 Toba Capital 的投资合伙人—他关注于开发者投资。他是 Sauce Labs、WSO2、Exo Platform 和 Codency 委员会成员,同时投资于 Cloudant、ZeroTurnaround、InfoQ 和 AppHarbor。
查看英文原文: Codenvy’s Architecture, Part 2
感谢杨赛对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。
评论