November 25, 2016
由于每个 Linux 发行版的内置 Python 都不太一样,而且为了避免你的项目在不同的 Python 版本下出现各种奇怪问题。
比如,requests
库在 python 2.7.5 的环境下访问 HTTPS 网站会出现 SNI 的问题,导致访问失败。
所以我们需要使用 pyenv
和 virtualenv
。
注:强烈不建议直接更新系统里面的 Python ,否则你会出现各种各样的奇怪问题
pyenv 允许你在一台机器上配置多个版本的 python 环境
sudo apt-get install curl git-core // 依赖库
curl -L https://raw.github.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash
如果执行成功,那么 pyenv
就会安装到 ~/.pyenv
目录里面。
为了方便我们用 pyenv subcommand
的方法来调用 pyenv
,我们要在 ~/.bashrc
文件中添加以下内容
export PYENV_ROOT="${HOME}/.pyenv"
if [ -d "${PYENV_ROOT}" ]; then
export PATH="${PYENV_ROOT}/bin:${PATH}"
eval "$(pyenv init -)"
fi
~/.bashrc
的作用大概就是用户配置文件
执行 source ~/.bashrc
以重新加载用户配置文件。
至此,pyenv
就安装好了。
pyenv install --list
列出目前 pyenv
安装过的所有 Python 版本
pyenv install $version
$version
就是你想要安装的 Python 版本
pyenv versions
显示当前系统用的是哪个 Python 版本
pyenv global $version
把当前系统的 Python 版本切换到 $version
这个版本。该命令在本教材中并不用到,故不多介绍,如果你想了解更多,可以查看pyenv
的 API 文档
用于分离 Python 库
如果你是使用以上的方法安装pyenv
的话,那么 virtualenv
就已经安装完了。
强烈不建议用其他方法安装 virtualenv, 那将会是一种很麻烦很折腾的方法
pyenv virtualenv $version $name
用于创建一个指定 Python 版本的虚拟环境
$version
你需要的 Python 版本$name
该虚拟环境的名称rm -rf ~/.pyenv/versions/$name/
删除名称为$name
的虚拟环境。
注意: 这里用到了rm -rf
谨慎使用,使用前先检查代码
首先我们需要一下这几个库来来帮助我们部署,建议在 virtualenv 中安装,以免感染系统
gunicorn
用 WSGI 的方式来启动 flask 项目
gevent
python 中比较好的网络库,提供更加高效的 I/O 读写
supervisor
守护进程,避免进程意外退出
安装方法:
pip install gunicorn gevent supervisor
为了做演示,我们暂定需要部署的 Flask 项目结构是这个样子的
flaskproject
|----app
|----|----templates
|----|----static
|----|----models.py
|----|----views
|----|----|----__init__.py
|----|----|----user.py
|----|----functions.py
|----config.py
|----run.py
项目结构并不是固定的,可以自己根据自己项目的情况自行分配
run.py
中就是 Flask 项目的入口,内容大致如下
from flask import Flask
app = Flask()
# ... some configs
或者使用create_app()
模式
from flask import Flask
def create_app():
app = Flask()
# ... some configs
return app
首先启动之前已经创建好的 virtualenv 虚拟环境
pyenv activate $name
如果正常启动的话,在 bash 命令行上面应该会出现环境的名字
($name) #
没正常启动的样子是这样的
#
进去之后呢,用pip
安装你的项目依赖
pip install -r requirements.txt
进入项目文件夹
然后输出 supervisor 默认配置文件 supervisor.conf
: ($name
是 virtualenv 环境的名称)
~/.pyenv/versions/$name/bin/echo_supervisord_conf > supervisor.conf
然后打开 supervisor.conf
,然后在文件的最后添加一下内容
[program:myapp]
command=~/.pyenv/versions/$name/bin/gunicorn -k gevent -w2 -b0.0.0.0:9000 run:app ; supervisor启动命令
directory=/home/myproject ; 项目的文件夹路径
startsecs=0 ; 启动时间
stopwaitsecs=0 ; 终止等待时间
autostart=true ; 是否自动启动
autorestart=true ; 是否自动重启
stdout_logfile=/home/myproject/log/gunicorn.log ; log 日志
stderr_logfile=/home/myproject/log/gunicorn.err ; 错误日志
command
中的是用 gunicorn
和 gevent
启用 Flask 项目的命令
-w2
指的是 2 worker
一般设置成 2 * cpu_nums
-b0.0.0.0:9000
指的是启动在 9000 端口run:app
是 Flask 项目入口,如果你用的是create_app()
方法的话, 就改成run:create_app()
启动 Supervisor
~/.pyenv/versions/$name/bin/supervisord -c supervisor.conf
查看 Supervisor 情况
~/.pyenv/versions/$name/bin/supervisorctl -c supervisor.conf status
重启 Supervisor
~/.pyenv/versions/$name/bin/supervisorctl -c supervisor.conf reload
启动 Supervisor 中某个 / 全部程序
~/.pyenv/versions/$name/bin/supervisorctl -c supervisor.conf start [all][appname]
关闭 Supervisor 中某个 / 全部程序
~/.pyenv/versions/$name/bin/supervisorctl -c supervisor.conf stop [all][appname]
配置好你的 supervisor.conf
文件后,执行启动命令,如果没打错命令基本就已经启动好了,这个时候你应该就可以访问 http://ip:9000
来访问到你的 Flask 项目
这里只讨论如何使用 Nginx ,Apache 党请自行转换
详情查看 Install | Nginx
进入 Nginx配置文件,或者/etc/nginx/conf.d/default.conf
,或者新建 /etc/nginx/conf.d/myproject.conf
,修改配置文件,以下这是几个建议的配置
禁止 IP 访问
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
return 404;
}
}
禁止 HTTP 访问
server {
listen 80;
server_name www.myproject.com myproject.com;
server_tokens off;
# 跳转至 HTTPS
location / {
rewrite ^/(.*)$ https://myproject.com/$1 permanent;
}
}
HTTPS设置 / 443端口设置
server {
listen 443 ssl http2 fastopen=3 reuseport;
server_name www.myproject.com myproject.com;
server_tokens off;
# SSL 设置
ssl_certificate /root/.acme.sh/myproject.com_ecc/fullchain.cer;
ssl_certificate_key /root/.acme.sh/myproject.com_ecc/myproject.com.key;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets on;
ssl_stapling on;
ssl_stapling_verify on;
resolver 114.114.114.114 valid=300s;
resolver_timeout 10s;
# NGINX 日志
access_log /home/myproject/log/nginx.log;
# 禁止非 GET HEAD POST OPTIONS 的访问
if ($request_method !~ ^(GET|HEAD|POST|OPTIONS)$ ) {
return 444;
}
# www.myproject.com 跳转至 myproject.com
# 可有可无,看个人情况
if ($host != 'myproject.com' ) {
rewrite ^/(.*)$ https://myproject.com/$1 permanent;
}
# 代理 Flask 端口
location / {
proxy_http_version 1.1;
# HSTS 头设置
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
add_header X-Frame-Options deny;
# 添加两个 Request Header
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 代理 9000 端口
proxy_pass http://127.0.0.1:9000;
}
}
这里的SSL申请是用 acme.sh 申请的 Let‘s Encrypt ECC证书
然后重启 Nginx ,项目就配置完成了。就可以使用域名正常访问了(DNS解析正常的情况下)
因为 Python 的执行方式不像 PHP 那样的脚本式执行,所以当项目代码更新后,需要重启才能使代码生效。
为了方便重启,我们在项目的根目录下创建reload.sh
git pull // 我是使用 git 拉去项目代码的,这里的作用是更新项目代码
~/.pyenv/versions/$name/bin/supervisorctl -c supervisor.conf reload // 重启 Supervisor
echo 'Reload Done'
下次我们就可以直接用 sh reload.sh
来直接更新项目状态了
iptables
等等相关软件限制端口访问supervisor
配置中的监听端口暴露(即使用9000端口)待完善的内容
fabric
来代替sh reload.sh
的执行方式