在生产环境中部署Flask

2020-05-11 17:46:06 浏览数 (1)

前言:在生产环境中部署flask,我们需要考虑的要素有很多,其中最重要的就是并发和高可用了。今天我们将会在这里详细的讲解到。当然可能水平有限,如有谬误之初,请不吝斧正。

当然我们将会从一个一无所有的Linux开始一步步的教大家安装Nginx ,uWSGI,MySQL(你的生产环境可能会用到这种数据库。),然后部署你的Flask应用,在我们的案例中,我们将会创建一个非常简单的Flask应用来进行访问验证。以下以REHL系列为例(包括了CentOS 等),如果使用Debian或者Ubuntu的小伙伴,可以参照着将包替换了。本文基于:CentOS 7 相关的,除了启动设置有点区别其他的和老版本CentOS没多大区别。

前提准备

1、Linux机器一台,包括但不限于,VPS,本地虚拟机,实体服务器等。

2、机器的网络(电脑本地能访问就OK了)

3、连接Linux服务器的工具(Putty,或者xshell)

第一部分,环境的安装

1、安装Nginx

安装Nginx有多种,最简单的就是直接使用Linux分发版本的包管理工具来进行安装了。还有进行编译安装。两者各有各的好处。生产环境建议使用编译安装,仅编译需要使用的组件,以防止组件多,漏洞破绽多。当然我们这里也会介绍使用包管理工具来进行安装。

编译安装:

复制黏贴下面的命令就OK了(”#”后面的是注释,就不要复制了!)

代码语言:javascript复制
yum install wget # 为什么要装wget?因为安全需要生产环境一般安装的都是最小化的安装
wget -c http://nginx.org/download/nginx-1.12.2.tar.gz  # 目前官网的稳定版本是这个版本
# 你可以去官网查看它的稳定版本:http://nginx.org/en/download.html
tar zxvf nginx-1.12.2.tar.gz && cd nginx-1.12.2 # 请将这里的版本号替换为你当时下载的稳定
# 版本的版本号。
useradd -M nginx -s /sbin/nologin #添加不支持登录的用户nginx和nginx组
yum install -y openssl openssl-devel gcc g   pcre pcre-devel zlib zlib-devel
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --user=nginx --group=nginx
# 配置需要的模块,这里仅需要替换模块stub ssl模块,和gzip模块,可能以后还会需要http2模块那么就加上 --with-http_v2_module
# 当没有指定prefix的时候,也会默认安装到/usr/local/nginx之下的。可能新版本有所不同?小伙伴们试试看?
make && make install # 这个两个合体的命令是安装Nginx,如果没有报错,则安装完成
chown -R nginx:nginx /usr/local/nginx # 直接给该目录添加权限,防止可能发生意外(就是为了防止发生意外)
# 至此应该是完成了安装的。不过所有使用nginx命令的时候都要输入完整的nginx路径,很麻烦,所以,可以这么干
ln -s /usr/local/nginx/sbin/nginx /usr/bin/ # 这样就给Nginx可执行文件建立了软连接
# 可以直接使用 nginx -t 来执行了。

至此应该是完成了Nginx部署的所有工作。

源安装:

既然编译安装结束了那么我们就可以快乐的开始使用源安装了。源安装简单方便快捷。可以看看支持源安装的系统版本:http://nginx.org/en/linux_packages.html

这里Nginx官方都已经给出了预编译好的包支持的版本。所以,如果自己的系统不支持,就老老实实的编译安装?

我在github上已经写了一键包,既然选择了源安装,那么方便起见也用工具包把,直接把需要的都装好了,轻松 愉快

https://github.com/rffanlab/Set-Up-yum-install-for-nginx-on-centos

找到setupnginxyum 这个文件,下载后,直接sh setupnginxyum 就可以了。 搞定之后还得使用systemctl enable nginx 来设置Nginx开机启动

2、安装MySQL

那么下面我们需要安装的是MySQL了。可能由于MySQL被Oracle收购的缘故,开源社区更看重MariaDB这个数据库,因为相比同样兼容的MySQL,MariaDB被毕源的风险更小。虽然使用的方法和MySQL没有区别,但是可能你就是需要MySQL

所以我们就以MySQL为例子,当然请小伙伴没相信开源社区的威力。

不多说,下面上代码:

代码语言:javascript复制
wget -c https://dev.mysql.com/get/Downloads/MySQL-5.5/mysql-5.5.58.tar.gz
# 这里我以5.5为例子,至少5.5没有内存至少需要2G的限制,5.6开始内存1G以下的就被禁止安装,会报错。
tar zxvf mysql-5.5.58.tar.gz && cd mysql-5.5.58 # 解压并进入安装目录
# 值得注意的是MySQL从5.1开始就使用cmake来进行编译了。因此 你在安装依赖包的时候需要安装cmake 我这里偷个懒,把所有的依赖都贴了上来(偷的是lnmp包的不过我们也需要)。
yum install -y patch make gcc gcc-c   gcc-g77 flex bison file libtool libtool-libs autoconf kernel-devel libjpeg libjpeg-devel libpng libpng-devel libpng10 libpng10-devel gd gd-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glib2 glib2-devel bzip2 bzip2-devel libevent libevent-devel ncurses ncurses-devel curl curl-devel e2fsprogs e2fsprogs-devel krb5 krb5-devel libidn libidn-devel openssl openssl-devel vim-minimal nano fonts-chinese gettext gettext-devel ncurses-devel gmp-devel pspell-devel unzip libcap
# 依赖安装了之后我们需要对MySQL进行配置了
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql/ -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DEXTRA_CHARSETS=all -DENABLED_PROFILING=ON -DWITH_READLINE=1 -DWITH_DEBUG=0 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DMYSQL_DATADIR=/home/mysql/data/ -DMYSQL_UNIX_ADDR=/tmp/mysql.sock -DMYSQL_TCP_PORT=3306 -DENABLED_LOCAL_INFILE=1
# 这里我们已经开启了Innodb了。并且默认设置的字符集为utf8字符集。所以妈妈再也不用担心我乱码了?
# 这里我设置了mysql的数据目录在/home/mysql/data 目录下,如果小伙伴们需要的话,可以自行更改。
# 好了,这里设置好了,我们开始编译安装吧
make && make install
groupadd mysql
useradd -s /sbin/nologin -M -g mysql mysql
mkdir -p /home/mysql/data/
cd /usr/local/mysql/
scripts/mysql_install_db --basedir=/usr/local/mysql --datadir=/home/mysql/data --user=mysql
chown -R mysql:mysql /usr/local/mysql/
chown -R mysql:mysql /home/mysql

cp /usr/local/mysql/support-files/mysql.server /etc/rc.d/init.d/mysqld
chmod 755 /etc/rc.d/init.d/mysqld
chkconfig --add mysqld
chkconfig --level 35 mysqld on

ln -s /usr/local/mysql/bin/mysql* /usr/bin
# 这句是把MySQL目录下的可执行文件都放到系统路径下去,好方便执行的时候不用带上路径名。

/etc/rc.d/init.d/mysqld start
# 下面的这几句是在脚本中执行的。如果你是直接执行的,那么,请复制EOF之间的命令行,再执行,这个需要执行mysql -uroot 之后才执行。
cat > /tmp/mysql_sec_script << EOF
use mysql; #选择mysql 数据库 如果你要把这些放到脚本里,那么请去掉这些#
update user set password=password('$mysqlrootpwd') where user='root'; # 把$mysqlrootpwd替换成自己的密码,我这是偷懒复制的脚本
delete from user where not (user='root') ;   # 删除不是root的用户
delete from user where user='root' and password='';   # 删除密码为空的root用户
drop database test;  # 删除测试库
DROP USER ''@'%';  # 删除匿名用户
flush privileges;  # 刷新权限
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'rffanlab2017' WITH GRANT OPTION;  # 然后添加一个密码为rffanlab2017 的root用户
EOF


/usr/local/mysql/bin/mysql -u root < /tmp/mysql_sec_script
# 执行 上面生成的脚本。 如果你单独执行了上面的脚本,那么这句应该就不用执行了。

rm -f /tmp/mysql_sec_script
# 删除上面生成的脚本

# 下面两句是选择运行的,意思就是如果你是CentOS7 用户,或之前其他使用firewalld防火墙替代iptables的用户,那么你需要执行下面两句来让其他服务器能够访问你的MySQL数据库,如果你不想让其他服务器访问,那么下面两句都不用执行。
firewall-cmd --permanent --add-port=3306/tcp
systemctl restart firewalld.service 

3、部署Python环境

Python环境取决于你的程序是使用Python3的还是使用Python2的,相当一部分Python2的程序还是不支持Python3。

包括运行Flask的uWSGI,supervisor等。 uWSGI是支持Python3的,但是supervisor是不支持的(因为听说有人安装成功了,但是pip上没有放出,那么我默认认为是 不支持Python3的。)

所以我们需要安装2套Python(Python2,Python3)来适应我们可能的需求,当然如果你只是跑Python2程序,那么你只需要安装Python2就OK了。

幸运的是,高版本的Ubuntu,CentOS等都已经默认安装了Python2.7 (CentOS 7 安装的是Python2.7.5 ,Ubuntu16 内置了Python2.7.11,最新的Python2版本是2.7.13,截止发稿时),所以如果你的Python2版本是2.7的,那么下面的安装Python的步骤都可以省略了,但是你的Python是2.6版本的话一定是需要独立安装的。

安装Python2

代码语言:javascript复制
wget -c https://www.python.org/ftp/python/2.7.13/Python-2.7.13.tgz --no-check-certificate
tar zxvf Python-2.7.13.tgz
cd Python-2.7.13
./configure --prefix=/usr/local/python27
make && make install
wget https://bootstrap.pypa.io/get-pip.py
/usr/local/python27/bin/python  get-pip.py
ln -s /usr/local/python27/bin/pip /usr/bin/pip27

安装Python3,Python3 我建议还是安装Python3.5 毕竟TensorFlow之类的都是只支持到3.5(谷歌官方的文档上说还是Python3.5好,3.6毕竟太新了,求稳不求新。)

代码语言:javascript复制
wget -c https://www.python.org/ftp/python/3.5.4/Python-3.5.4.tgz --no-check-certificate
tar zxvf Python-3.5.4.tgz
cd Python-3.5.4
./configure --prefix=/usr/local/python35
make && make install
wget https://bootstrap.pypa.io/get-pip.py
/usr/local/python35/bin/python  get-pip.py
ln -s /usr/local/python35/bin/pip /usr/bin/pip35

至此,所有需要的环境都已经部署好了。但是,在跑flask之前,还得装几个必要的python依赖。包括我们这篇教程中需要用的uWSGI。我们需要安装的有3个分别是uwsgi,virtualenv,以及supervisor。 uwsgi是符合wsgi规范的web服务器。(CentOS 使用pip安装的时候会出问题,这时候你得用yum install python-devel 装一下python依赖) virtualenv 这个是虚拟环境允许你的python安装不同版本的包,而不相互干扰 supervisor 是一个监控管理程序,用来启动和监控python应用。 当然这三个钟,只有virtualenv是必装的,其他两个都是选装的,因为其他两个都会在virtualenv中安装。

代码语言:javascript复制
pip install virtualenv # 用这个命令来进行安装

至此所有的准备工作和环境配置都差不多了,只要一个flask脚本就OK了

代码语言:javascript复制
from flask import Flask
application = Flask(__name__)

@application.route("/")
def hello():
    return "小伙伴们,你们好!"

if __name__ == "__main__":
    application.run(host='0.0.0.0') # 这个脚本本来是需要设置port的,如果在ide的环境下跑的话,但是我们这里采用uwsgi跑,port什么的都在配置文件里了。

PS: 这里如果你们用python2会报错,因为有中文,需要在脚本头部加上

代码语言:javascript复制
# encoding:utf8

至此,我们的Flask脚本也准备好了。下面我们就开始部署工作吧!

4、部署Flask 这是一个看起来简单,而实际上操作也简单的步骤

代码语言:javascript复制
mkdir -p /home/www/myflask # 我这里先在www目录下新建一个myflask目录
cd /home/www/myflask  # 我们要进去了!
virtualenv myflaskenv # 在myflask目录下创建一个env目录,用以存储python解析器以及包
source myflaskenv/bin/activate  # 激活虚拟环境 激活的标志应该是(myflaskenv)user@host:~/myflask$
pip install -r requirement.txt # 一般来说这个requirement.txt 是你用pip freeze > requirement.txt 来生成的不过如果你知道你所有的包
# 那么你也可以自己一个一个的pip 安装过去
pip install uwsgi supervisor # 这两个其中一个uwsgi是Python3支持的,能pip安装 supervisor是不可以pip安装的
# 之所以还有这一个步骤是因为你可能是在Windows下开发的python,那么恭喜uwsgi和supervisor根本不可能装在Windows下
# 别和我说什么Windows 10的Ubuntu模式
vi /home/www/myflask/myflask.py # 进入编辑模式,编辑这个文件
from flask import Flask
application = Flask(__name__)

@application.route("/")
def hello():
    return "小伙伴们,你们好!"

if __name__ == "__main__":
    application.run(host='0.0.0.0')
# 把上面这个脚本写进去 。 小伙伴们,如果你不懂vi没关系,你可以在Windows下写好,然后上传上去!请注意哦,Windows的编码会和Linux的不同
# 所以请用IDE编辑吧,抹除烦恼

下面我们来运行这个脚本,看看这玩意有没有写错

代码语言:javascript复制
python myflask.py

访问:5000端口看看是不是显示出页面,”小伙伴们,你们好!”,显示了,说明OK,这个脚本是好用的。 ctrl c 结束这玩意 创建WSGI节点 我们需要创建一个WSGI入口,来让我们的uWSGI识别并访问这个应用

代码语言:javascript复制
vi /home/www/myflask/wsgi.py
# 内容如下
from myflask import application   
# 注意,这里,如果是pycharm 新建的flask应用 他是默认会设置为app的你要as为application
# 否则将会500错误

if __name__ == "__main__":
    application.run()
# 按esc
:wq # 保存退出

然后让我们使用uwsgi跑跑看能不能跑起来

代码语言:javascript复制
uwsgi --socket 0.0.0.0:8000 --protocol=http -w wsgi

然后你访问:8000 端口的时候你会发现还是跟上面跑的一样的显示,如果,没有,请自行参照教程重新操作一遍,有了,则代表OK了。

代码语言:javascript复制
dactivate # 退出虚拟环境

下面,我们来新建一个配置文件,来跑这个Flask。一下的配置文件时.ini格式的,你也可以使用json,yaml,xml等格式。 如下(请注意,配置文件是不支持#的,请将复制后,删除#号):

代码语言:javascript复制
[uwsgi]
module = wsgi   # 这一行是注明要运行的文件,我们是wsgi.py 所以使用wsgi
master = true
processes = 5   # 进程数设置为5,因为Python是伪多线程,只有当是IO密集型的应用时多线程才有用,否则,还不如单线程性能强劲

socket = myflask.sock # 现在我们使用unix socket来和Nginx通信,当然你也可以使用端口,不过我更更建议使用unix socket如果你的Nginx和你的Flask在同一台机器上的话。
# 这不仅仅是因为unix socket的性能比http性能更高,而且能够免除很多因为http,和https的问题造成的无法访问。
chmod-socket = 660  # 设置权限为660
gid = nginx # 这里必须得是Nginx的用户组,因为如果不是,Nginx就无法访问socket导致500错误

上面就是配置文件了,这是最简单的配置文件,更多的配置方式小伙伴们可以自行参悟。 一般来说小应用(博客)在服务器上部署,这个配置应该是够了的。 启动它吧!

代码语言:javascript复制
/home/www/myflask/myflaskenv/bin/uwsgi /home/www/myflask/config.ini

下面是Nginx配置文件,由于我自己使用的是源安装的Nginx(PS:这个渣渣,让我们去编译,自己却偷懒使用源安装) 目录在/etc/nginx/conf.d下,我用我自己闲置域名dufu.pw来玩这个就新建一个dufu.pw.conf

代码语言:javascript复制
upstream dufupw {
    ip_hash;                # 用这玩意的意思是,你可以有多个后端,然后通过ip_hash 来定位一个用户访问一个后端,起到初级的负载均衡的作用
    server unix:/home/www/myflask/myflask.sock;  # 你可以有很多的这个unix socket 而不必占用端口,开心吧,理论上,你可以弄无限个这玩意
}
server {
    # 这里我们先不玩https,因为毕竟https也是简单的。申请一下https就好了。
    listen 80;
    server_name www.dufu.pw dufu.pw;
    location / {
		include uwsgi_params;
		uwsgi_pass dufupw;
	}
}

这里的Nginx设置也是非常简单的配置,你可以增加各种其他的东西。但是这里Nginx并不是我们的重点。所以,使用nginx -t 来看看配置文件是否正确吧! 出现success字样,那么成功了。 现在访问域名看看是否成功了? 成功了,你就进行下一步吧,失败?继续看文章研究! 下一步,也就是收尾工作,就是使用supervisor来监控和启动uwsgi启动脚本。 由于我们在开始的时候就装了supervisor,因此我们现在不用再安装了。

代码语言:javascript复制
mkdir -p /etc/supervisor/conf.d # 新装的supervisor是没有配置文件夹和文件的,我们需要手动创建
echo_supervisord_conf>/etc/supervisor/supervisord.conf #创建默认的配置文件

然后我们在这个supervisord.conf文件的最后添加两行用来引入其他的文件

代码语言:javascript复制
[include]
files = conf.d/*.ini

这个就是为了让我们的supervisor能够引入conf.d这个文件夹里的配置文件。 然后让我们在conf.d文件夹下建立一个myflask.ini吧,内容如下(再次申明,请不要直接复制,请删除#后面的内容后再复制)

代码语言:javascript复制
[program:myflask]  #声明应用名
command=/home/www/myflask/myflaskenv/bin/uwsgi /home/www/myflask/config.ini # 声明命令地址
directory=/home/www/myflask  # 声明程序路径
autostart=true  # 声明随supervisor启动就启动
autorestart=true  # 声明自动重启我们用supervisor就是为了这玩意!
stdout_logfile=/home/www/myflask/uwsgi_supervisor.log # 声明日志地址

文件配置好了之后,我们就可以直接输入supervisord 启动supervisor了,但是当你用root用户的时候,他会提示一个警告,暂时别理他,因为我们不会用root用户登录的。 supervisorctl 看看运行状态,如果你找到myflask 这一栏是running状态,那么成功,你搞定了。然后你访问以下你的网址,成功!OK,你牛逼了! 下面我们来设置supervisord的开机启动

代码语言:javascript复制
vi /usr/lib/systemd/system/supervisor.service # 编辑这个supervisor这个文件
# 内容如下
[Unit]
Description=Supervisor daemon

[Service]
Type=forking
ExecStart=/usr/bin/supervisord -c /etc/supervisor/supervisord.conf
ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown
ExecReload=/usr/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

OK 我们新建了一个supervisor的服务。 下面我们就可以用CentOS 7 熟悉的命令来设置开机启动了

代码语言:javascript复制
systemctl enable supervisor.service

所以我们来reboot来验证我们的成果吧! 所以,如果我们reboot了,还是能成功,说明,我们真的完成了这项伟大的事业!

后记

这里有很多东西值得小伙伴们去记住,包括了nginx的负载均衡,以及supervisor的监控以及任务启动等。当然有些小伙伴们可能会说systemd就能实现supervisor,而且是系统内置的,我想说,有多少人是用CentOS7的?还有多少人是出于CentOS5时代的?所以这篇教程的兼容性非常不错。当然中间可能会出错,那么我会不定时更新和查漏补缺。

0 人点赞