Phusion Passenger/mod_rails 令 Rails 部署变得更容易

阅读数:1464 2008 年 4 月 24 日

话题:RubyRuby on Rails开源DevOps语言 & 开发架构

一个旨在从Apache网页服务器上更便捷的部署 Ruby on Rails的新项目诞生了。Phusion Passenger/mod_rails似乎已经实现了这个目标,并得到了Rails 官方和很多人的推荐。我们采访了 Phusion Passenger 的创建者之一,Ninh Bui,想了解一下项目背后的故事和它未来的发展方向。

首先,我们想弄清楚名字的问题──这个项目的网站是modrails.com, 但是名字却起为 Phusion Passenger。Ninh Bui 是这么解释的:

Phusion 是我们公司的名字,然而 Phusion Passenger 是 mod_rails 官方的名称。大多数社区的朋友都会从 mod_rails 这个名字中了解它的用途,因为它的命名方式为传统的 mod_*,所以我们由此保留了这个别名。

尽管起初我们打算就只叫它 mod_rails,但是 Ruby on Rails 核心团队和我们都认为“Passenger”(乘客)要比用一个 Rails 词汇更贴切。特别是,我们想让 Phusion Passenger(也就是 mod_rails)为用户带来的体验与(火车)乘客是一致的:只要坐在那里,便可享受乘坐的乐趣 ;-)。

另一个问题是关于 Phusion Passenger 所发放的许可:

对于许可来说,我们使用的是 GPLv2,并附加了一个额外的条款。大家通常都会对许可产生误解,也正好趁此机会把这些问题一并加以解释:我们实际上已经允许社区对源代码做更广泛的应用,而不是限制这个许可。

同样,我们有很多在开源社区工作的经验,我们是与它一起成长的。我们相信,将开源的开发模式与商业公司的驱动结合起来,会令我们提供更好的产品。换句话说,我们认为,对于商业机构来说,这是一种回馈社区最好的方式。

拿 Phusion Passenger 为例,它的设计和开发是为了追求产品环境(production environments)下的稳定与高效。然而,可能会有人在部署的时候遇到问题,或者需要某些特定的功能。我们会为这些人提供付费的商业技术支持。我 们团队的技术专家在各个领域都有着丰富的经验──特别是基于 Linux 的环境下──并且对 Ruby 的内在机理、Ruby on Rails 和 Passenger 都很熟悉。

我们都很好奇,Phusion Passenger 在应对 Rails 部署中类似的解决方案时是如何进行叠加的:

与 Swifiply、Mongrel Cluster 等不同,Phusion Passenger 的应用池(application pool)是在当前流量的基础上进行自动管理的。还有就是,如果 Rails 应用程序崩溃了,它会自动重启。这意味着,配置或者进程监控都是多余的,而且这 会削减系统管理的成本。有很多种配置选项是可用的,它们都被记录在用户指南中了。

我们感兴趣的是,究竟如何用 Phusion Passenger 去部署和重新部署 Rails 应用程序。

只要花费你两分钟的时间,不过这要看你的网速如何了 ;-)。开个玩笑。用 Phusion Passenger 更新并且重新部署你的 Rails 应用程序只需再上传一次应用程序,并调用 restart.txt。要做的,就这么多。如果你想重新启动 Apache(我认为大多数人可能都想这么做 ;-)),就必须做第二步。

从技术角度来讲,第二步是用来更新 restart.txt 文件中时间戳的,Phusion Passenger 会根据那个时间戳决定是否重启你的 Rails 应用程序。

Rails 部署并不是 Phusion 开发者试图解决的唯一的问题。当运行很多 Rails 进程时,内存占用过多也是一个问题。每 个 Ruby 进程都必须把 Ruby 和 Rails 函数库载入到内存中──因为这些是不同的进程,此时它是没有被共享的(Ruby 代码是储存在堆栈中的,所以不 像共享的函数库,在不同进程中是无法共享的)。

Phusion 的一位开发人员,Hongli Lai试图通过 Unix 系统函数fork()让 Ruby 不同进程之间共享数据。它的核心思想是首先获取一个设置好函数库的 Ruby 进程,然后通过fork()让进程去获取其他的进程。在进程中调用fork(),可以说是一种非常有效复制进程的方式。这两个进程基本上是一样的,在它们的地址空间中包含有相同的数据。但是,这不是共享内存,而是系统的虚拟内存,操作系统调用了一个名为Copy On Write(写 时复制,简写成 COW)的方法让不同进程可以读取相同的数据,但是却没有修改它们私有的拷贝。只要数据是只读的,那么有一份数据就够用了。不过,一旦进程 开始对数据进行了修改,数据的拷贝就只能归属于这个进程了。很明显,共享只读数据(例如函数库代码或其他类似的)是一种非常可行的方法。

然而, 对于使用垃圾回收的编程语言来说,存在一个问题:完整的垃圾回收会遍历堆栈,并将所有遍历到的对象作标记。这里面很重要的一个词是“作标记”──在对象上插上一面旗。如果这些对象中的任何一个被其他进程(通过fork())共享了,COW 便会起作用,并将数据拷贝过来──这意味者数据不会再被共享,所有进程都拥有它自己的拷贝。

Hongli Lai 针对这个问题的解决方案是令Ruby 的垃圾回收器 COW 的更友好,也就是说,回收的时候不会再引起数据的拷贝。Ninh 对这个问题和它与 Phusion 产品之间的关联进行了详细的阐述:

COW 垃圾回收器已经基本上完成了。我们现在正准备发布并建立它的官网。而且,我们还在屯特大学(University of Twente)的 ir. Hans Scholten 帮助下撰写了一篇相关的论文。未来几周内,我们就会发布一个版本。我们理解大家可能不太喜欢为 Ruby 打补丁,所以我们付出很多额外的努 力来确保安装尽可能的简单,而且要完全独立,不要涉及任何的系统文件。

我们会把它同“Ruby 企业版”(是的,我们很清楚这个名字,但是请允许我们在 Railsconf 上对它作详细说明 ;-))一 起发布。恳请大家对我们有一些耐心,我们向你保证,那将是非常值得关注的东西。还有一点要注意的是,Ruby 企业版是完全与标准 Ruby 版(1.8)向后 兼容的。

如果 Phusion Passenger 被应用到 Ruby 企业版上,那么 Passenger 会自动使用 COW 语义。这可令 Rails 应用程序大幅节省使用的内存。实际上,先前的测试已经表明平均会节省 33% 的内存使用量。

认真的读者将会注意到,Ruby 企业版是以一种透明的方式完成这些工作的,而且同样为程序开发者提供了在运行时打开和关闭 GC 优化的功能。因为这个原因,Ruby 企业版应当算作 Ruby 的一个超集(superset)。

由此,我们不得不改变 Ruby 堆栈的执行方式。它现在使用了一组作了标记的位阈(bitfield),而不是在对象中对二进制 位(bit)进行标记。它的性能有些不确定,这要取决于应用程序和工作量的大小。在一些 Rails 应用程序中,我们进行了测试,有的提高了 5%,有的提高 了 20%,有的则是 0%,也就是说,性能没有变化。如果您想了解更多的相关内容,我们会在 Railsconf 上对这个现象作详细的阐述,或者日程上允许的 话,可能会提前给大家讲讲。

如果大家想试用一下 Phusion Passenger,Phusion 网站上提供了一个基础视频教程或者直接查看安装文档。Phusion Passenger 是一个开源项目,你可以到Phusion Passenger 的 GitHub 的 repository中获取源代码。