部署方式
Flask 应用可以采用多种方式部署。在开发时,你可以使用内置的服务器,但是在生产环境 下你就应当选择功能完整的服务器。下面为你提供几个可用的选择。
除了下面提到的服务器之外,如果你使用了其他的 WSGI 服务器,那么请阅读其文档中与使用 WSGI 应用相关的部分。因为 Flask 应用对象的实质就是一个 WSGI 应用。
如果需要快速体验,请参阅《快速上手》中的部署到一个网络服务器 。
mod_wsgi (Apache)
如果你正在使用 Apache 网络服务器,那么建议使用 mod_wsgi 。
小心
请务必把 app.run() 放在 if name == 'main': 内部或者放在单独 的文件中,这样可以保证它不会被调用。因为,每调用一次就会开启一个本地 WSGI 服务器。当我们使用 mod_wsgi 部署应用时,不需要使用本地服务器。
安装 mod_wsgi
可以使用包管理器或编译的方式安装 mod_wsgi 。在 UNIX 系统中如何使用源代码安装请阅读 mod_wsgi 安装介绍 。
如果你使用的是 Ubuntu/Debian ,那么可以使用如下命令安装:
# apt-get install libapache2-mod-wsgi
在 FreeBSD 系统中,可以通过编译 www/mod_wsgi port 或使用 pkg_add 来安装 mod_wsgi :
# pkg_add -r mod_wsgi
如果你使用 pkgsrc ,那么可以通过编译 www/ap2-wsgi 包来安装 mod_wsgi 。
如果你遇到子进程段错误的话,不要理它,重启服务器就可以了。
创建一个 .wsgi 文件
为了运行应用,你需要一个 yourapplication.wsgi 文件。这个文件包含 mod_wsgi 开始时需要运行的代码,通过代码可以获得应用对象。文件中的 application 对象就是以后要使用的应用。
对于大多数应用来说,文件包含以下内容就可以了:
from yourapplication import app as application
如果你的应用没有创建函数,只是一个独立的实例,那么可以直接把实例导入为 application 。
把文件放在一个以后可以找得到的地方(例如 /var/www/yourapplication ),并确保 yourapplication 和所有需要使用的库都位于 pythonpath 中。如果你不想在整个系统中安装,建议使用 virtual python 实例。请记住,最好把应用安装到虚拟环境中。 有一个可选项是在 .wsgi 文件中,在导入前加入路径:
import sys
sys.path.insert(0, '/path/to/the/application')
配置 Apache
最后一件事是为你的应用创建一个 Apache 配置文件。基于安全原因,在下例中我们告诉 mod_wsgi 使用另外一个用户运行应用:
<VirtualHost *>
ServerName example.com
WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5
WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
<Directory /var/www/yourapplication>
WSGIProcessGroup yourapplication
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
注意: WSGIDaemonProcess 在 Windows 中不会被执行, 使用上面的配置 Apache 会拒绝运行。在 Windows 系统下,请使用下面内容:
<VirtualHost *>
ServerName example.com
WSGIScriptAlias / C:\yourdir\yourapp.wsgi
<Directory C:\yourdir>
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
更多信息参见 mod_wsgi 维基 。
故障排除
如果你的应用无法运行,请按以下指导排除故障:
问题: 应用无法运行,出错记录显示 SystemExit ignored
应用文件中有 app.run() 调用,但没有放在 if __name__ == '__main__’:
块内。要么把这个调用放入块内,要么把它放在一个单独的 run.py 文件中。
问题: 权限错误 有可以是因为使用了错误的用户运行应用。请检查用户及其所在的组 ( WSGIDaemonProcess 的 user 和 group 参数)是否有权限访问应用 文件夹。
问题: 打印时应用歇菜 请记住 mod_wsgi 不允许使用 sys.stdout 和 sys.stderr 。把 WSGIRestrictStdout 设置为 off 可以去掉这个保护:
WSGIRestrictStdout Off
或者你可以在 .wsgi 文件中把标准输出替换为其他的流:
import sys
sys.stdout = sys.stderr
问题: 访问资源时遇到 IO 错误
你的应用可能是一个独立的 .py 文件,且你把它符号连接到了 site-packages 文件夹。这样是不对的,你应当要么把文件夹放到 pythonpath 中,要么把你的应用转换为一个包。
产生这种错误的原因是对于非安装包来说,模块的文件名用于定位资源,如果使用符号连接的话就会定位到错误的文件名。
支持自动重载
为了辅助部署工具,你可以激活自动重载。这样,一旦 .wsgi 文件有所变动, mod_wsgi 就会自动重新转入所有守护进程。
在 Directory 一节中加入以下指令就可以实现自动重载:
WSGIScriptReloading On
使用虚拟环境
使用虚拟环境的优点是不必全局安装应用所需要的依赖,这样我们就可以更好地按照自己的需要进行控制。如果要在虚拟环境下使用 mod_wsgi ,那么我们要对 .wsgi 略作改变。
在你的 .wsgi 文件顶部加入下列内容:
activate_this = '/path/to/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
以上代码会根据虚拟环境的设置载入相关路径。请记住路径必须是绝对路径。
独立 WSGI 容器
有一些用 Python 写的流行服务器可以容纳 WSGI 应用,提供 HTTP 服务。这些服务器是 独立运行的,你可以把代理从你的网络服务器指向它们。如果遇到问题,请阅读代理设置一节。
Gunicorn
Gunicorn ‘Green Unicorn’ 是一个 UNIX 下的 WSGI HTTP 服务器,它是一个移植自 Ruby 的 Unicorn 项目的 pre-fork worker 模型。它既支持 eventlet , 也支持 greenlet 。在 Gunicorn 上运行 Flask 应用非常简单:
gunicorn myproject:app
Gunicorn 提供许多命令行参数,可以使用 gunicorn -h 来获得帮助。下面的例子使用 4 worker 进程( -w 4 )来运行 Flask 应用,绑定到 localhost 的 4000 端口( -b 127.0.0.1:4000 ):
gunicorn -w 4 -b 127.0.0.1:4000 myproject:app
Tornado
Tornado 是构建 FriendFeed 的服务器和工具的开源版本,具有良好的伸缩性,非阻塞性。得益于其非阻塞的方式和对 epoll 的运用,它可以同步处理数以千计的独立 连接,因此 Tornado 是实时 Web 服务的一个理想框架。用它来服务 Flask 是小事一桩:
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from toranado.ioloop import IOLoop
from yourapplication import app
http_server = HTTPServer(WSGIContainer(app))
http_server.listen(5000)
IOLoop.instance().start()
Gevent
Gevent 是一个 Python 并发网络库,它使用了基于 libevent 事件循环的 greenlet 来提供一个高级同步 API:
from gevent.wsgi import WSGIServer
from yourapplication import app
http_server = WSGIServer(('', 5000), app)
http_server.serve_forever()
Twisted Web
Twisted Web 是一个 Twisted 自带的网络服务器,是一个成熟的、异步的、事件驱动的网络库。 Twisted Web 带有一个标准的 WSGI 容器,该容器可以使用 twistd 工具运行命令行来控制:
twistd web --wsgi myproject.app
这个命令会运行一个名为 app 的 Flask 应用,其模块名为 myproject 。
与 twistd 工具一样, Twisted Web 支持许多标记和选项。更多信息参见 twistd -h 和 twistd web -h 。例如下面命令在前台运行一个来自 myproject 的应用, 端口为 8080:
twistd -n web --port 8080 --wsgi myproject.app
代理设置
如果你要在一个 HTTP 代理后面在上述服务器上运行应用,那么必须重写一些头部才行。 通常在 WSGI 环境中经常会出现问题的有两个变量: REMOTE_ADDR 和 HTTP_HOST 。 你可以通过设置你的 httpd 来传递这些头部,或者在中间件中修正这些问题。 Werkzeug 带有一个修复工具可以用于常用的设置,但是你可能需要为特定的设置编写你自己的 WSGI 中间件。
下面是一个简单的 nginx 配置,代理目标是 localhost 8000 端口提供的服务,设置了适当的头部:
server {
listen 80;
server_name _;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
proxy_pass http://127.0.0.1:8000/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
如果你的 httpd 无法提供这些头部,那么最常用的设置是调用 X-Forwarded-Host 定义的主机和 X-Forwarded-For 定义的远程地址:
from werkzeug.contrib.fixers import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)
头部可信问题
请注意,在非代理情况下使用这个中间件是有安全问题的,因为它会盲目信任恶意客户端发来的头部。
如果你要根据另一个头部来重写一个头部,那么可以像下例一样使用修复工具:
class CustomProxyFix(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
host = environ.get('HTTP_X_FHOST', '')
if host:
environ['HTTP_HOST'] = host
return self.app(environ, start_response)
app.wsgi_app = Cus
tomProxyFix(app.wsgi_app)
uWSGI
uWSGI 也是部署 Flask 的途径之一,类似的部署途径还有 nginx 、 lighttpd 和 cherokee 。其他部署途径的信息参见 FastCGI 和 独立 WSGI 容器 。使用 uWSGI 协议来部署 WSGI 应用的先决条件是需要一个 uWSGI 服务器。 uWSGI 既是一个协议也是一个服务器。如果作为一个服务器, 它可以服务于 uWSGI 、 FastCGI 和 HTTP 协议。
最流行的 uWSGI 服务器是 uwsgi ,本文将使用它来举例,请先安装它。
小心
请务必把 app.run() 放在 if __name__ == '__main__':
内部或者放在单独 的文件中,这样可以保证它不会被调用。因为,每调用一次就会开启一个本地 WSGI 服务器。当我们使用 uWSGI 部署应用时,不需要使用本地服务器。
使用 uwsgi 启动你的应用
uwsgi 是基于 python 模块中的 WSGI 调用的。假设 Flask 应用名称为 myapp.py , 可以使用以下命令:
$ uwsgi -s /tmp/uwsgi.sock --module myapp --callable app
或者这个命令也行:
$ uwsgi -s /tmp/uwsgi.sock -w myapp:app
配置 nginx
一个 nginx 的基本 uWSGI 配置如下:
location = /yourapplication { rewrite ^ /yourapplication/; }
location /yourapplication { try_files $uri @yourapplication; }
location @yourapplication {
include uwsgi_params;
uwsgi_param SCRIPT_NAME /yourapplication;
uwsgi_modifier1 30;
uwsgi_pass unix:/tmp/uwsgi.sock;
}
这个配置把应用绑定到 /yourapplication 。如果你想要在根 URL 下运行应用非常简单,因为你不必指出 WSGI PATH_INFO 或让 uwsgi 修改器来使用它:
location / { try_files $uri @yourapplication; }
location @yourapplication {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi.sock;
}
FastCGI
FastCGI 是部署 Flask 的途径之一,类似的部署途径还有 nginx 、 lighttpd 和 cherokee 。其他部署途径的信息参见 uWSGI 和独立 WSGI 容器 。本文讲述的是使用 FastCGI 部署,因此先决条件是要有一个 FastCGI 服务器。 flup 最流行的 FastCGI 服务器之一,我们将会在本文中使用它。在阅读下文之前先安装好 flup 。
小心
请务必把 app.run() 放在 if name == 'main': 内部或者放在单独 的文件中,这样可以保证它不会被调用。因为,每调用一次就会开启一个本地 WSGI 服务器。当我们使用 FastCGI 部署应用时,不需要使用本地服务器。
创建一个 .fcgi 文件
首先你必须创建 FastCGI 服务器配置文件,我们把它命名为 yourapplication.fcgi:
#!/usr/bin/python
from flup.server.fcgi import WSGIServer
from yourapplication import app
if __name__ == '__main__':
WSGIServer(app).run()
如果使用的是 Apache ,那么使用了这个文件之后就可以正常工作了。但是如果使用的是 nginx 或老版本的 lighttpd ,那么需要显式地把接口传递给 FastCGI 服务器,即把接口的路径传递给 WSGIServer:
WSGIServer(application, bindAddress='/path/to/fcgi.sock').run()
这个路径必须与服务器配置中定义的路径一致。
把这个 yourapplication.fcgi 文件放在一个以后可以找得到的地方,最好是 /var/www/yourapplication 或类似的地方。
为了让服务器可以执行这个文件,请给文件加上执行位,确保这个文件可以执行:
# chmod +x /var/www/yourapplication/yourapplication.fcgi
配置 Apache
上面的例子对于基本的 Apache 部署已经够用了,但是你的 .fcgi 文件会暴露在应用的 URL 中,比如 example.com/yourapplication.fcgi/news/ 。有多种方法可以避免出现这中情况。一个较好的方法是使用 ScriptAlias 配置指令:
<VirtualHost *>
ServerName example.com
ScriptAlias / /path/to/yourapplication.fcgi/
</VirtualHost>
如果你无法设置 ScriptAlias ,比如你使用的是一个共享的网络主机,那么你可以使用 WSGI 中间件把 yourapplication.fcgi 从 URL 中删除。你可以这样设置 .htaccess:
<IfModule mod_fcgid.c>
AddHandler fcgid-script .fcgi
<Files ~ (\.fcgi)>
SetHandler fcgid-script
Options +FollowSymLinks +ExecCGI
</Files>
</IfModule>
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ yourapplication.fcgi/$1 [QSA,L]
</IfModule>
设置 yourapplication.fcgi:
#!/usr/bin/python
#: optional path to your local python site-packages folder
import sys
sys.path.insert(0, '<your_local_path>/lib/python2.6/site-packages')
from flup.server.fcgi import WSGIServer
from yourapplication import app
class ScriptNameStripper(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
environ['SCRIPT_NAME'] = ''
return self.app(environ, start_response)
app = ScriptNameStripper(app)
if __name__ == '__main__':
WSGIServer(app).run()
配置 lighttpd
一个 lighttpd 的基本 FastCGI 配置如下:
fastcgi.server = ("/yourapplication.fcgi" =>
((
"socket" => "/tmp/yourapplication-fcgi.sock",
"bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
"check-local" => "disable",
"max-procs" => 1
))
)
alias.url = (
"/static/" => "/path/to/your/static"
)
url.rewrite-once = (
"^(/static($|/.*))$" => "$1",
"^(/.*)$" => "/yourapplication.fcgi$1"
请记住启用 FastCGI 、 alias 和 rewrite 模块。以上配置把应用绑定到 /yourapplication 。如果你想要让应用在根 URL 下运行,那么必须使用 LighttpdCGIRootFix 中间件来解决一个 lighttpd 缺陷。
请确保只有应用在根 URL 下运行时才使用上述中间件。更多信息请阅读 FastCGI 和 Python (注意,已经不再需要把一个接口显式传递给 run() 了)。
配置 Nginx
在 Nginx 上安装 FastCGI 应用有一些特殊,因为缺省情况下不传递 FastCGI 参数。
一个 Nginx 的基本 FastCGI 配置如下:
location = /yourapplication { rewrite ^ /yourapplication/ last; }
location /yourapplication { try_files $uri @yourapplication; }
location @yourapplication {
include fastcgi_params;
fastcgi_split_path_info ^(/yourapplication)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}
这个配置把应用绑定到 /yourapplication 。如果你想要在根 URL 下运行应用非常简单,因为你不必指出如何计算出 PATH_INFO 和 SCRIPT_NAME:
location / { try_files $uri @yourapplication; }
location @yourapplication {
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param SCRIPT_NAME "";
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}
运行 FastCGI 进程
Nginx 和其他服务器不会载入 FastCGI 应用,你必须自己载入。 Supervisor 可以管理 FastCGI 进程。 在启动时你可以使用其他 FastCGI 进程管理器或写一个脚本来运行 .fcgi 文件,例如使用一个 SysV init.d 脚本。如果是临时使用,你可以在一个 GNU screen 中运行 .fcgi 脚本。运行细节参见 man screen ,同时请注意这是一个手动启动方法, 不会在系统重启时自动启动:
$ screen
$ /var/www/yourapplication/yourapplication.fcgi
调试
在大多数服务器上, FastCGI 部署难以调试。通常服务器日志只会告诉你类似 “ premature end of headers ”的内容。为了调试应用,查找出错的原因,你必须切换到正确的用户并手动执行应用。
下例假设你的应用是 application.fcgi ,且你的网络服务用户为 www-data:
$ su www-data
$ cd /var/www/yourapplication
$ python application.fcgi
Traceback (most recent call last):
File "yourapplication.fcgi", line 4, in <module>
ImportError: No module named yourapplication
上面的出错信息表示 “yourapplication” 不在 python 路径中。原因可能有:
- 使用了相对路径。在当前工作路径下路径出错。
- 当前网络服务器设置未正确设置环境变量。
- 使用了不同的 python 解释器。
CGI
如果其他的部署方式都不管用,那么就只能使用 CGI 了。 CGI 适应于所有主流服务器, 但是其性能稍弱。
这也是在 Google 的 App Engine 使用 Flask 应用的方法,其执行方式类似于 CGI 环境。
小心
请务必把 app.run() 放在 if __name__ == '__main__’:
内部或者放在单独 的文件中,这样可以保证它不会被调用。因为,每调用一次就会开启一个本地 WSGI 服务器。当我们使用 CGI 或 App Engine 部署应用时,不需要使用本地服务器。
在使用 CGI 时,你还必须确保代码中不包含任何 print 语句,或者 sys.stdout 被重载,不会写入 HTTP 响应中。
创建一个 .cgi 文件
首先,你需要创建 CGI 应用文件。我们把它命名为 yourapplication.cgi:
#!/usr/bin/python
from wsgiref.handlers import CGIHandler
from yourapplication import app
CGIHandler().run(app)
服务器设置
设置服务器通常有两种方法。一种是把 .cgi 复制为 cgi-bin (并且使用 mod_rewrite 或其他类似东西来改写 URL );另一种是把服务器直接指向文件。
例如,如果使用 Apache ,那么可以把如下内容放入配置中:
ScriptAlias /app /path/to/the/application.cgi
在共享的网络服务器上,你可能无法变动 Apache 配置。在这种情况下,你可以使用你的公共目录中的 .htaccess 文件。但是 ScriptAlias 指令会失效:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files
RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L]
更多信息参见你所使用的服务器的文档。
© Copyright 2013, Armin Ronacher. Created using Sphinx.