准备工作
目前应该确保服务器上已经装有 python3 ,因此这将是我部署项目所用的 python 版本。
基础的 Python 解释器可能已经预先安装在你的服务器上,但有一些额外的软件包可能却没有,而且 Python 之外还有一些其他软件包可用于创建健壮的生产环境部署。对于数据库服务器,选用 MySQL。 Supervisor 工具将监视 Flask 服务器进程,并在其崩溃时自动重启,并当 Supervisor 服务重启后自动启动其监视的服务。Nginx 服务器将接受来自外部世界的所有请求,并将它们转发给应用程序。最后,我将使用 git 来从 github 仓库下载应用程序。1
2
3$ sudo yum -y update
$ sudo yum -y install python3 python3-venv python3-dev
$ sudo yum -y install mysql-server postfix supervisor nginx git
这些安装大部分是无人值守的,但是在运行第三条安装语句到一定进度时,系统会提示你为 MySQL 服务选择一个 root 密码,并且还会询问关于安装 postfix 软件包的一些问题,你可以接受他们的默认答案。
安装应用
现在我要使用 git 从我的 GitHub 代码库下载 FlaskAPI 源代码。1
2$ git clone https://github.com/oxyzhg/flask-api
$ cd flask-api
现在我需要创建一个虚拟环境并使用所有的包依赖项来填充它,此前我已将依赖包的列表保存到requirements.txt文件中,并上传 github:1
2
3$ python3 -m venv venv
$ source venv/bin/activate
(venv) $ pip install -r requirements.txt
除了requirements.txt中的包之外,我还将使用此生产部署指定的两个包,因此它们不包含在requirements.txt文件中。 gunicorn软件包是 Python 应用程序的生产 Web 服务器。 pymysql软件包包含 MySQL 驱动程序,它使 SQLAlchemy 能够与 MySQL 数据库一起工作:1(venv) $ pip install gunicorn pymysql
我需要创建一个.env文件,其中包含所有需要的环境变量:
/home/flask-api/.env1
2SECRET_KEY=52cb883e323b48d78a0a36e8e951ba4a
DATABASE_URL=mysql+pymysql://root:@localhost:3306/flask-api
SECRET_KEY 使用了一个随机字符串,为了生成这个随机字符串,使用下面的命令:1(venv) $ python3 -c "import uuid; print(uuid.uuid4().hex)"
如果你的数据库配置是正确的,你现在应该能够运行数据库迁移以创建所有的表:1(venv) $ flask db upgrade
配置 Gunicorn
当你使用 flask run 运行服务器时,正在使用的是 Flask 附带的 Web 服务器。 该服务器在开发过程中非常有用,但它不适合用于生产服务器,因为它不考虑性能和稳健性。 取而代之,我决定使用 Gunicorn,它是一个纯粹的 Python Web 服务器,但与 Flask 不同,它是一个支持高并发的强大生产服务器,同时它也非常容易使用。
要在 gunicorn 下启动项目,你可以使用以下命令:1(venv) $ gunicorn -b localhost:8000 -w 4 manage:app-w 选项告诉 gunicorn 将运行多少 worker。拥有四个进程可以让应用程序同时处理多达四个客户端,这对于 Web 应用程序通常足以处理大量客户端请求,因为并非所有客户端都在不断请求内容。 根据服务器的 RAM 大小,你可能需要调整 worker 数量,以免内存不足。
-b 选项告诉 gunicorn 在哪里监听请求,我在 8000 端口监听了内部网络接口。在没有外部访问的情况下运行 Python Web 应用程序通常是一个好主意,然后还需要一个非常快速的 Web 服务器,它可以优化来自客户端的所有静态文件的请求。这个快速的 Web 服务器将直接提供静态文件,并将用于应用程序的任何请求转发到内部服务器。我将在下一部分中展示如何将 nginx 设置为面向公众的服务器。
manage:app 选项告诉 gunicorn 如何加载应用程序实例。冒号前的名称是包含应用程序的模块,冒号后面的名称是此应用程序的名称。
此外,--daemon 参数可以在后台运行。
配置 Supervisor
虽然 gunicorn 的设置非常简单,但从命令行运行服务器在生产服务器实际上不是一个恰当的方案。 我想要做的是让服务器在后台运行,并持续监视,因为如果由于某种原因导致服务器崩溃并退出,我想确保新的服务器自动启动以取代它。 而且我还想确保如果机器重新启动,服务器在启动时自动运行,而无需人工登录和启动。 我将使用上面安装的 Supervisor 包来执行此操作。
Supervisor 使用配置文件定义它要监视什么程序以及如何在必要时重新启动它们。
配置文件存储在 /etc/supervisord.d/flaskapi.ini :1
2
3
4
5
6
7
8[program:flaskapi]
command=/home/flask-api/venv/bin/gunicorn -b localhost:8000 -w 4 manage:app
directory=/home/flask-api
user=docker
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
command,directory 和 user 设置告诉 supervisor 如何运行应用程序。 如果计算机启动或崩溃,autostart和 autorestart 设置会使 microblog 自动重新启动。 stopasgroup 和 killasgroup 选项确保当 supervisor 需要停止应用程序来重新启动它时,它仍然会调度成顶级 gunicorn 进程的子进程。
这时,要修改 supervisor 配置文件,/etc/supercisord.conf :1
2[include]
files = supervisord.d/*.ini
编写此配置文件后,必须重载 supervisor 服务的配置才能导入它:1$ supervisorctl reload
像这样,这个 gunicorn web 服务器就已经启动和运行,并处于监控之中!
supervisor 使用:1
2
3
4
5
6
7
8$ supervisorctl status
$ supervisorctl stop node # 关闭 [program:node] 的进程
$ supervisorctl start node # 启动 [program:node] 的进程
$ supervisorctl restart node # 重启 [program:node] 的进程
$ supervisorctl stop all # 关闭所有进程
$ supervisorctl start all # 启动所有进程
$ supervisorctl reload # 重新读取配置文件,读取有更新(增加)的配置文件,不会启动新添加的程序
$ supervisorctl update # 重启配置文件修改过的程序
配置 Nginx
由 gunicorn 启动的 microblog 应用服务器现在运行在本地端口 8000。 我现在需要做的是将应用程序暴露给外部世界,为了使面向公众的 web 服务器能够被访问,我在防火墙上打开了两个端口(80 和 443)来处理应用程序的 Web 通信。
修改项目配置文件 /usr/local/nginx/conf/vhost/flaskapp.conf :1
2
3
4
5
6
7
8
9
10
11
12
13
14location / {
# forward application requests to the gunicorn server
proxy_pass http://localhost: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;
}
location /static {
# handle static files directly, without forwarding to the application
alias /home/flask-api/app/static;
expires 30d;
}
Nginx 的配置不易理解,添加了一些注释,至少你可以知道每个部分的功能。
修改此文件后,你需要告诉 nginx 重新加载配置以激活它:1$ sudo service nginx reload
现在应用程序应该部署成功了。 在你的 Web 浏览器中,可以键入配置的域名,然后该服务器将连接到应用程序。
部署应用更新
我想讨论的基于 Linux 的部署的最后一个主题是如何处理应用程序升级。
应用程序源代码通过git安装在服务器中,因此,无论何时想要将应用程序升级到最新版本,都可以运行git pull来下载自上次部署以来的新提交。
当然,下载新版本的代码不会导致升级。 当前正在运行的服务器进程将继续运行,旧代码已被读取并存储在内存中。 要触发升级,你必须停止当前的服务器并启动一个新的服务器,以强制重新读取所有代码。
进行升级通常比重新启动服务器更为复杂。 你可能需要应用数据库迁移或编译新的语言翻译,因此实际上,执行升级的过程涉及一系列命令:1
2
3
4(venv) $ git pull # download the new version
(venv) $ sudo supervisorctl stop flaskapi # stop the current server
(venv) $ flask db upgrade # upgrade the database
(venv) $ sudo supervisorctl start flaskapi # start a new server