目标
在虚拟环境(virtual environment)中运行 Flask 应用,由 Gunicorn 托管,通过 Nginx 进行反向代理最后由 Systemd 管理进程。
Why
在使用 Python 时,我们可能会在不同项目中使用同一个软件包或模块的不同版本,如果直接安装必然会导致冲突。而解决方案是通过创建一个虚拟环境,其中安装有特定的 Python 版本和其他包,我们就可以在不同的虚拟环境中运行各个项目而不产生冲突。
而 Flask 只是一个 web 框架,直接运行的 web 服务只适合开发使用,在生产环境中无法承受大量的并发请求,所以我们使用 Gunicorn 拉起 Flask 应用并由 Nginx 转发 Gunicorn,并且 Nginx 还能托管项目中的静态文件,减小了服务器压力。而且 Nginx 还能让我们使用域名通过 80 或 443 端口访问,而不用指定特定端口。
How
虚拟环境(virtual environment)
进入项目根目,创建虚拟环境
python3 -m venv venv
接下来执行
source env/bin/activate
这样终端前面应该会有一个 (venv)
标识,表示我们已经进入了虚拟环境。此时我们需要重新安装之前的各种软件包,并且在这里安装的软件包只会对该虚拟环境有效。
最后可以使用该命令来退出虚拟环境
deactivate
Gunicorn
Gunicorn 是一个高性能的 Python WSGI UNIX HTTP 服务器。可以直接通过 pip 进行安装。
$ pip install gunicorn
$ cat myapp.py
def app(environ, start_response):
data = b"Hello, World!\n"
start_response("200 OK", [
("Content-Type", "text/plain"),
("Content-Length", str(len(data)))
])
return iter([data])
$ gunicorn -w 4 myapp:app
[2014-09-10 10:22:28 +0000] [30869] [INFO] Listening at: http://127.0.0.1:8000 (30869)
[2014-09-10 10:22:28 +0000] [30869] [INFO] Using worker: sync
[2014-09-10 10:22:28 +0000] [30874] [INFO] Booting worker with pid: 30874
[2014-09-10 10:22:28 +0000] [30875] [INFO] Booting worker with pid: 30875
[2014-09-10 10:22:28 +0000] [30876] [INFO] Booting worker with pid: 30876
[2014-09-10 10:22:28 +0000] [30877] [INFO] Booting worker with pid: 30877
其中 -w
为工作进程的数量,还可以通过 -d 0.0.0.0:8000
来指定端口号。
Nginx
创建一个 Nginx 配置文件,其中增加一段来进行反向代理。
location / {
proxy_pass http://localhost:8000/;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
其中 proxy_pass
则为 Gunicorn 运行时所指定的地址和端口。
Systemd
最开始我不知道如何通过 Systemd 在虚拟环境中来运行项目,因为要预先进入虚拟环境再运行,后来在 Stack Overflow 找到了答案:How to enable a virtualenv in a systemd service unit?,直接通过 env/bin/
中的 Python 解释器运行,会自动进入虚拟环境,无需额外手动进入。所以我们可以在虚拟环境中安装 Gunicorn 来实现我们的目标。
Systemd 默认从 /etc/systemd/system/
读取配置文件,最简单的配置文件大致是这个样子。
[Unit]
Description=TEST
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/wwwroot/test/
ExecStart=/home/wwwroot/test/venv/bin/gunicorn -w 4 test:app -b 0.0.0.0:8000
Restart=on-failure
[Install]
WantedBy=multi-user.target
其中 [Unit]
通常为配置文件的第一个区块,用于定义 Unit 的元数据以及配置与其他 Unit 的关系。其中的 Description
为该 Unit 的描述,After
表示该字段中的 Unit 必须在此 Unit 之前启动。
[Service]
用于定义 Service 的配置,只有 Service 类型的 Unit 才有这个区块。
Type
定义启动时的进程行为。simple
为默认值,执行 ExecStart 指定的命令,启动主进程。
WorkingDirectory
为项目所在目录。
ExecStart
就是启动项目的命令。
Restart=on-failure
表示在失败时重启当前服务。
[Install]
通常是配置文件的最后一个区块,用来定义如何启动,以及是否开机启动。
具体内容可以参考 Systemd 入门教程:命令篇
保存配置文件后重载 daemon
systemctl daemon-reload
这样我们就完成了所有的配置工作。之后就可以通过 systemctl start test.service
来运行我们的项目了。