Flask之旅

部署

部署

最终,你终于可以向全世界展示你的应用了。是时候部署它了。这个过程总能让人感到受挫,因为有太多任务需要完成。同时在部署的过程中你需要做出太多艰难的决定。我们会谈论一些关键的地方以及我们一些可能的选择。

托管主机

首先,你需要一个服务器。世上服务器提供商成千,但我只取三家。我不会谈论如何开始使用它们的服务的细节,因为这超出本书的范围。相反,我只会谈论它们作为Flask应用托管商上的优点。

Amazon Web Services EC2(因为国情问题,让我们直接看下一个吧)

Amazon Web Services指的是一套相关的服务,提供商是……卓越亚马逊!今日,许多著名的初创公司选择使用它,所以你或许已经听过它的大名。AWS服务中我们最关心的是EC2,全称是Elastic Compute Cloud。EC2的最大的卖点是你能够获得虚拟主机,或者说实例(这是AWS官方称呼),在仅仅几秒之内。如果你需要快速拓展你的应用,就只需启动多一点EC2实例给你的应用,并且用一个负载平衡器(load balancer)管理它们。(这时还可以试试AWS Elastic Load Balancer)

对于Flask而言,AWS就是一个常规的虚拟主机。付上一些费用,你可以用你喜欢的Linux发行版启动它,并安上你的Flask应用。之后你的服务器就起来了。不过它意味着你需要一些系统管理知识。

Heroku

Heroku是一个应用托管网站,基于诸如EC2的AWS的服务。他们允许你获得EC2的便利,而无需系统管理经验。

对于Heroku,你通过git push来在它们的服务器上部署代码。这是非常便利的,如果你不想浪费时间ssh到服务器上,安装并配置软件,继续整个常规的部署流程。这种便利是需要花钱购买的,尽管AWS和Heroku都提供了一定量的免费服务。

参见 Heroku有一个如何在它们的服务器上部署Flask应用的教程: https://devcenter.heroku.com/articles/getting-started-with-python

注意 管理你自己的数据库将会花上许多时间,而把它做好也需要一些经验。通过配置你自己的站点来学习数据库管理是好的,但有时候你会想要外包给专业团队来省下时间和精力。Heroku和AWS都提供有数据库管理服务。我个人还没试过,但听说它们不错。如果你想要保障数据安全以及备份,却又不想要自己动手,值得考虑一下它们。

Digital Ocean

Digital Ocean是最近出现的EC2的竞争对手。一如EC2,Digital Ocean允许你快速地启动虚拟主机(在这里叫droplet)。所有的droplet都运行在SSD上,而在EC2,如果你用的是普通服务,你是享受不到这种待遇的。对我而言,最大的卖点是它提供的控制接口比AWS控制面板简单和容易多了。Digital Ocean是我个人的最爱,我建议你考虑下它。

在Digital Ocean,Flask应用部署方式就跟在EC2一样。你会得到一个全新的Linux发行版,然后需要安装你的全套软件。

部署工具

这一节将包括一些为了向别人提供服务,你需要安装在服务器上的软件。最基本的是一个前置服务器,用来反向代理请求给一个运行你的Flask应用的应用容器。你通常也需要一个数据库,所以我们也会略微谈论下这方面的内容。

应用容器

在开发应用时,本地运行的那个服务器并不能处理真实的请求。当你真的需要向公众发布你的应用,你需要在应用容器,例如Gunicorn,上运行它。Gunicorn接待请求,并处理诸如线程的复杂事务。

要想使用Gunicorn,需要通过pip安装gunicorn到你的虚拟环境中。运行你的应用只需简单的命令。为了简明起见,让我们假设这就是我们的Flask应用:

rocket.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Hello World!"

哦,这真简明扼要。现在,使用Gunicorn来运行它吧,我们只需执行这个命令:

(ourapp)$ gunicorn rocket:app
2014-03-19 16:28:54 [62924] [INFO] Starting gunicorn 18.0
2014-03-19 16:28:54 [62924] [INFO] Listening at: http://127.0.0.1:8000 (62924)
2014-03-19 16:28:54 [62924] [INFO] Using worker: sync
2014-03-19 16:28:54 [62927] [INFO] Booting worker with pid: 62927

你应该能在 http://127.0.0.1:8000 看到“Hello World!”。

为了在后台运行这个服务器(也即使它变成守护进程),我们可以传递-D选项给Gunicorn。这下它会持续运行,即使你关闭了当前的终端会话。

如果我们这么做了,当我们想要关闭服务器时就会困惑于到底应该关闭哪个进程。我们可以让Gunicorn把进程ID储存到文件中,这样如果想要停止或者重启服务器时,我们可以不用在一大串运行中的进程中搜索它。我们使用-p <file>选项来这么做。现在,我们的Gunicorn部署命令是这样:

(ourapp)$ gunicorn rocket:app -p rocket.pid -D
(ourapp)$ cat rocket.pid
63101

要想重新启动或者关闭服务器,我们可以运行对应的命令:

(ourapp)$ kill -HUP `cat rocket.pid` # 发送一个SIGHUP信号,终止进程
(ourapp)$ kill `cat rocket.pid`

默认下Gunicorn会运行在8000端口。如果这已经被另外的应用占用了,你可以通过添加-b选项来指定端口。

(ourapp)$ gunicorn rocket:app -p rocket.pid -b 127.0.0.1:7999 -D

将Gunicorn摆上前台

注意 Gunicorn应该隐藏于反向代理之后。如果你直接让它监听来自外网的请求,它很容易成为拒绝服务攻击的目标。它不应该接受这样的考验。只有在debug的情况下你才能把Gunicorn摆上前台,而且完工之后,切记把它重新隐藏到幕后。 }

如果你像前面说的那样在服务器上运行Gunicorn,将不能从本地系统中访问到它。这是因为默认情况下Gunicorn绑定在127.0.0.1。这意味着它仅仅监听来自服务器自身的连接。所以通常使用一个反向代理来作为外网和Gunicorn服务器的中介。不过,假如为了debug,你需要直接从外网发送请求给Gunicorn,可以告诉Gunicorn绑定0.0.0.0。这样它就会监听所有请求。

(ourapp)$ gunicorn rocket:app -p rocket.pid -b 0.0.0.0:8000 -D

注意

Nginx反向代理

反向代理处理公共的HTTP请求,发送给Gunicorn并将响应带回给发送请求的客户端。Nginx是一个优秀的客户端,更何况Gunicorn强烈建议我们使用它。

要想配置Nginx作为运行在127.0.0.1:8000的Gunicorn的反向代理,我们可以在/etc/nginx/sites-available下给应用创建一个文件。不如称之为exploreflask.com吧。

/etc/nginx/sites-available/exploreflask.com

# Redirect www.exploreflask.com to exploreflask.com
server {
        server_name www.exploreflask.com;
        rewrite ^ http://exploreflask.com/ permanent;
}

# Handle requests to exploreflask.com on port 80
server {
        listen 80;
        server_name exploreflask.com;

        # Handle all locations
        location / {
                # Pass the request to Gunicorn
                proxy_pass http://127.0.0.1:8000;

                # Set some HTTP headers so that our app knows where the request really came from
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

现在在/etc/nginx/sites-enabled下创建该文件的符号链接,接着重启Nginx。

$ sudo ln -s \
/etc/nginx/sites-available/exploreflask.com \
/etc/nginx/sites-enabled/exploreflask.com

你现在应该可以发送请求给Nginx然后收到来自应用的响应。

参见 Gunicorn文档中关于配置Nginx的部分会给你更多启动Nginx的信息: http://docs.gunicorn.org/en/latest/deploy.html#nginx-configuration

ProxyFix

有时,你会遇到Flask不能恰当处理转发的请求的情况。这也许是因为在Nginx中设置的某些HTTP报文头部造成的。我们可以使用Werkzeug的ProxyFix来fix转发请求。

app.py

from flask import Flask

# Import the fixer
from werkzeug.contrib.fixers import ProxyFix

app = Flask(__name__)

# Use the fixer
app.wsgi_app = ProxyFix(app.wsgi_app)

@app.route('/')
def index():
    return "Hello World!"

参见 在Werkzeug文档中可以读到更多关于ProxyFix的信息: http://werkzeug.pocoo.org/docs/contrib/fixers/#werkzeug.contrib.fixers.ProxyFix

总结

  • 你可以把Flask应用托管到AWS EC2, Heroku和Digital Ocean。(译者注:建议托管到国内的云平台上)
  • Flask应用的基本部署依赖包括一个应用容器(比如Gunicorn)和一个反向代理(比如Nginx)。
  • Gunicorn应该退居Nginx幕后并监听127.0.0.1(内部请求)而非0.0.0.0(外部请求)
  • 使用Werkzeug的ProxyFix来处理Flask应用遇到的特定的转发报文头部。