使用uWSGI在Heroku上运行python web应用

前提条件:一个Heroku账户 (在cedar平台上),git (本地系统跟上) 以及heroku toolbelt。

注意:需要uWSGI version >= 1.4.6来确保正确运行python应用。较老的版本可能可用,但并不支持。

准备环境

在你的本地系统上,为项目准备一个目录:

mkdir uwsgi-heroku
cd uwsgi-heroku
git init .
heroku create

最后一个命令将会创建一个新的heroku应用 (你可以在网页界面上检查它)。

在我们的例子中,我们会运行Werkzeug WSGI testapp,因此,除了uWSGI之外,我们还需要安装werkzeug包。

第一步是创建一个requirements.txt文件,然后用git来跟踪它。

该文件的内容很简单

uwsgi
werkzeug

现在,用git来跟踪

git add requirements.txt

创建uWSGI配置文件

现在,可以创建我们的uWSGI配置文件了。基本上,在heroku上可以使用所有特性

[uwsgi]
http-socket = :$(PORT)
master = true
processes = 4
die-on-term = true
module = werkzeug.testapp:test_app
memory-report = true

正如你所看到的,这是一个非常标准的配置。heroku必备的选项只用 –http-socket 和 –die-on-term。

需要第一个来将uWSGI socket绑定到由Heroku系统请求的端口 (通过环境变量PORT导出,我们可以通过$(PORT)访问)

需要第二个(–die-on-term)来修改uWSGI接收到一个SIGTERM时的默认行为 (粗暴的重载,而Heroku期望是关机)

memory-report选项 (因为我们处于内存受限的环境中)是个不错的东东。

记得跟踪这个文件

git add uwsgi.ini

准备第一次commit/push

现在,需要最后一步:创建Procfile。

Procfile是一个描述启动哪个命令的文件。一般来说(对于其他部署系统),对于每个由你的应用请求的额外进程,你都将使用它 (例如memcached, redis, celery...),但在uWSGI之下,你可以继续使用它的高级功能来管理它们。

因此,Procfile是需要启动你的uWSGI实例:

web: uwsgi uwsgi.ini

跟踪它:

git add Procfile

最后,提交所有东西:

git commit -a -m "first commit"

以及,push它 (即:部署)到Heroku:

git push heroku master

第一次的适合,将需要一点时间,因为需要准备你的virtualenv以及编译uWSGI。

之后的push将会快得多。

检查你的应用

运行 heroku logs ,你将能够访问uWSGI日志。你应该获取所有你熟悉的信息,以及发生问题的情况下的一些最终提示。

使用python的另一个版本

Heroku支持不同的python版本。默认情况下 (目前是2013年二月份),允许Python 2.7.3。

如果你需要另一个版本,那么在你的仓库中创建一个runtime.txt,然后在里面写上如下字符串:

python-2.7.2

来使用python 2.7.2

记得在仓库中add/commit它。

每当你的修改python版本,就会构建一个新的uWSGI二进制。

多进程还是多线程?

这显然取决于你的应用。但由于我们在一个内存受限的环境中,因此使用线程可以期望获得更好的内存使用。

除此之外,如果你计划将生产应用放在Heroku上,那么确保了解Dynos和它们的代理是如何工作的(这很重要。真的)

异步/绿色线程/协程?

像往常一样,不要相信那些让你总是使用某种类型的异步模式(例如gevent)的人。如果你的应用是异步友好型的,那么显然,你可以使用gevent (在近期的uWSGI发布版本中,会默认构建它),但如果你不知道,那么保持使用多进程(或多线程)。

Harakiri

如之前所述,如果你计划将生产应用放在heroku上,那么确保了解dynos和它们的代理是如何工作的。基于此,试着总是为你的应用将harakiri参数设置成一个不错的值。 (不要要求默认值,它取决于应用)

静态文件

一般来讲,在Heroku上提供静态文件并不是一个好主意 (主要从设计的角度来看)。你当然可以有此需求。在这种情况下,记得使用uWSGI功能,特别是卸载(offloading)是在提供大文件时留出worker的最好方法 (另外,记得必须使用git来跟踪你的静态文件)

自适应进程生成

对于Heroku方法,没有好的支持的算法,并且很可能,在这样一个平台上使用一个动态进程数并没有什么意义。

日志记录

如果你计划在生产环境上使用heroku,那么记住在一个外部服务器上(有持续存储)发送你的日志(例如,通过udp)。

检查uWSGI可用的记录器。当然,会有一个满足你的需要的。(重视安全性,因为日志会记录明文)。

更新:一个具有crypto特性的udp记录器正在开发中。

告警

所有的告警插件应该工作正常

Spooler

由于你的应用运行在一个非持久化的文件系统上,因此使用Spooler是个糟糕的主意 (你会很容易丢失任务)。

Mule

它们可以正常使用

信号 (定时器、文件监控器、cron……)

它们都能用,但不要依赖于cron功能,因为heroku每时每刻都能杀掉/摧毁/重启你的实例。

外部守护进程

–attach-daemon 选项及其 –smart 变量可以正常使用。只是记住,你处于一个不稳定的文件系统中,并且你无法任意如你所愿的绑定端口/地址

监控你的应用(高级/hack)

尽管Heroku和newrelic服务工作良好,但是你总是需要监控你的uWSGI实例内部。

一般来说,作为客户端,你使用诸如uwsgitop这样的工具启动stats子系统。

你可以简单的添加uwsgitop到你的requirements.txt中

uwsgi
uwsgitop
werkzeug

并在一个TCP端口上启动stats服务器 (unix socket将不能用,因为运行uwsgitop的实例并不在同一个服务器上!!!):

[uwsgi]
http-socket = :$(PORT)
master = true
processes = 4
die-on-term = true
module = werkzeug.testapp:test_app
memory-report = true
stats = :22222

现在,我们有个问题:如果访问我们的实例?

我们需要知道物理运行我们的实例的机器的LAN地址。要完成它,一个原始的技巧是在uWSGI启动的适合运行ifconfig:

[uwsgi]
http-socket = :$(PORT)
master = true
processes = 4
die-on-term = true
module = werkzeug.testapp:test_app
memory-report = true
stats = :22222
exec-pre-app = /sbin/ifconfig eth0

现在,有了 heroku logs 命令,你就可以知道你的stats服务器在哪里了

heroku run uwsgitop 10.x.x.x:22222

将x.x.x修改成发现的地址,然后记住,你不能绑定到端口22222上,因此,相应修改它。

为了监控,值得搞得一团糟吗?如果你在上线之前测试你的应用,那么这是一个好主意,但如果你计划购买更多的dynos,那么一切都变得太复杂,此时最好使用一些heroku相关技术 (如果有的话)