使用 Nginx + Gunicorn 部署 Flask 项目

Flask Web 项目开发完成后,开发人员只是在开发环境运行,只有本地可以访问到项目。如果要让用户访问到项目,需要将项目部署到生产环境上,在服务器运行项目。

本文就使用阿里云服务器(CentOS 7.7 64位)来演示部署一个简单的 Flask 项目。

一、阿里云服务器配置

要将项目部署到服务器上,首先要有服务器。阿里云需要实名认证登录,刚注册时可以领一台免费使用的服务器,试用一个月,如果认证用户是24周岁以下可以买学生服务器,比较便宜,实在不行可以只买一个星期来试用。

拥有服务器后,需要在服务器上配置一些规则,才能完成项目的部署。

1. 登录阿里云,点击用户名旁边的“控制台”,然后在控制台点击左上角的菜单,展开菜单后,再点击“云服务器 ECS”(Elastic Compute Service,简称ECS),进入自己的云服务器界面。当前用户有哪些服务器都会在这里显示。

2. 点击“实例”,然后点击服务器信息右边的“管理”,进入服务器管理界面。

3. 在服务器管理界面点击“本实例安全组”,然后点击右边的“配置规则”,就会进入配置安全组规则的界面(页面的按钮是随阿里云的前端界面变化的,仅供参考)。

4. 如果之前配置过安全组规则,在此页面会保留着,如果没有配置过,就点击右上角的“添加安全组规则”按钮,弹出如下的配置框,然后按照下图的方式配置。

下面是按照 Flask 的默认服务器端口5000配置的,也可以配置多个端口,方法完全一样。

5. 我配置了三个端口,5000,7777,8888,配置成功后,结果如下。

配置完成后,后面部署项目时可以设置配置好的端口作为 Flask Web 项目的访问端口。

二、环境搭建和代码部署

配置好阿里云服务器的访问端口后,服务器上还没有安装项目需要使用的软件和库,也没有项目代码,所以需要搭建好项目运行的环境和部署代码。

1. 安装 MySQL

最好先下载好 MySQL 的安装包,然后使用远程连接工具将安装包上传到服务器,这样会快一点。

可以使用 WinSCP 或 FileZilla,都非常方便,将下载好的安装包(如 mysql-5.7.27-1.el7.x86_64.rpm-bundle.tar)上传到服务器。

然后使用 ssh 连接阿里云服务器,如使用 Xshell ,按以下步骤安装 MySQL 和创建需要的用户和数据库。

解压 MySQL 安装包。

tar -xvf mysql-5.7.27-1.el7.x86_64.rpm-bundle.tar

安装 MySQL5.7 。

yum install mysql-community-* -y

查询 root 用户默认的密码。

grep password /var/log/mysqld.log

使用 root 用户登录 MySQL ,然后修改 root 用户的密码(密码自己设,不用跟我一样)。

mysql -u root -p
# 修改root的密码
alter user 'root'@'localhost' identified by 'Mysql!123';

创建需要使用的用户 admin。

grant all privileges on *.* to 'admin'@'%' identified by 'Mysql!123';

退出 root 用户,使用 admin 用户登录 MySQL ,创建需要使用的数据库 MyDB_one 。

mysql -u admin -p
# 创建 MyDB_one 数据库
create database MyDB_one character set utf8;

安装好 MySQL,并创建好用户和数据库,代码中可以正常连接和使用数据库,数据库就准备好了。

2. 安装 Python3.6

正常项目部署时,最好先安装一个虚拟环境,好让当前项目与其他项目隔离开,运行环境不会相互干扰。不过当前的服务器只有一个项目,后续也不会再部署其他的项目,所以不安装虚拟环境了。

在 CentOS 中默认的 Python 版本是2.7 ,如果后面用 Python2.7 运行代码,最新版本的 gunicorn 是不支持 Python2.7 的,可以指定较旧的版本安装,如 gunicorn==19.9.0 ,新版本的 Flask 使用时也有兼容问题,可以指定较旧的版本安装,如 flask==0.10.1 。

当然,安装 Python3 ,就不用担心兼容问题了。

yum install python36 -y

删除 /usr/bin/ 下的 python ,创建一个软连接指向 python3.6 ,这样默认的 Python 版本就是 python3.6 了。

rm /usr/bin/python
ln -s /usr/bin/python3.6 /usr/bin/python

3. 安装 Flask 和 Flask-SQLAlchemy

执行如下命令安装 Flask 和 Flask-SQLAlchemy ,如果是 Python2.7 就将 pip3 改成 pip。

pip3 install Flask
pip3 install flask-sqlalchemy

因为使用的是 MySQL,要使用 Flask-SQLAlchemy 连接数据库,还要安装 flask-mysqldb 。

pip3 install flask-mysqldb

安装时会报如下错误,是因为在 python3.6 中找不到 Python.h ,需要安装 python3-devel 。

MySQLdb/_mysql.c:38:20: fatal error: Python.h: No such file or directory#include "Python.h"^compilation terminated.error: command 'gcc' failed with exit status 1

所以执行命令安装 python3-devel 。

yum install python3-devel -y

但是执行后会报如下错。

File "/usr/bin/yum", line 30except KeyboardInterrupt, e:^
SyntaxError: invalid syntax

因为 yum 是使用 Python2 实现的,默认的 python 已经被改成 python3.6 了(上面创建软链接时) ,需要将 yum 使用的 Python 版本改回 python2.7 。

vim /usr/bin/yum

将第一行的 /usr/bin/python 改为 /usr/bin/python2.7,重新运行还会报如下错误。

Downloading packages:File "/usr/libexec/urlgrabber-ext-down", line 28except OSError, e:^
SyntaxError: invalid syntax

原因与上面相同,继续修改。

vim /usr/libexec/urlgrabber-ext-down

将第一行的 /usr/bin/python 改为 /usr/bin/python2.7 。

然后重新执行 yum install python3-devel -y 和 pip3 install flask-mysqldb 就可以安装成功了,如果还有其他问题,可以使用类似方法解决。

4. 将项目代码部署到服务器上

我准备了一个 ProjectOne 的项目文件夹,项目中包含了一个 Flask 代码文件 flask_project.py ,一个 templates 模板文件夹,在模板文件夹中有一个 flask_project.html 模板文件。

使用 WinSCP 或 FileZilla 将项目代码上传到服务器,使用 tree 命令查看,项目的目录结构如下。

如果没有 tree 命令 ,可以先安装。

yum install tree -y

flask_project.py 中的代码如下:

# coding=utf-8
from flask import Flask, render_template, request, redirect, flash
from flask_sqlalchemy import SQLAlchemy
import randomapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://admin:Mysql!123@127.0.0.1:3306/MyDB_one'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_ECHO'] = True
app.config['SECRET_KEY'] = 'NFAIOSDFHASOGHAOSPIGAOWE'
db = SQLAlchemy(app)class Phone(db.Model):__tablename__ = 'Phone_tb'pid = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(32))person_id = db.Column(db.Integer, db.ForeignKey('Person_tb.mid'))def __repr__(self):return 'Phone_name: {}'.format(self.name)class Person(db.Model):__tablename__ = 'Person_tb'mid = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(64), unique=True)age = db.Column(db.Integer)phones = db.relationship('Phone', backref='person', lazy='dynamic')def __repr__(self):return 'Person_name: {}'.format(self.name)# db.drop_all()
# db.create_all()@app.route('/', methods=['GET', 'POST'])
def index():if request.method == 'GET':persons = Person.query.all()return render_template('flask_project.html', persons=persons)if request.method == 'POST':person_name = request.form.get('person')if not person_name:return redirect('/')submit = request.form.get('search')if submit == '查询':persons = Person.query.filter_by(name=person_name)return render_template('flask_project.html', persons=persons)submit = request.form.get('add')if submit == '添加':phone_name = request.form.get('phone')person = Person.query.filter_by(name=person_name).first()if person:if not phone_name:return redirect('/')phone = Phone.query.filter(Phone.name == phone_name, Phone.person_id == person.mid).first()if phone:flash('数据已存在!')else:phone = Phone(name=phone_name, person_id=person.mid)add_data(phone)else:person = Person(name=person_name, age=random.randint(18, 25))add_data(person)if not phone_name:return redirect('/')phone = Phone(name=phone_name)phone.person = personadd_data(phone)return redirect('/')def add_data(obj):try:db.session.add(obj)db.session.commit()except Exception as e:print(e)db.session.rollback()flash("添加失败")@app.route("/update_person/<id>", methods=['GET', 'POST'])
def update_person(id):person_name = request.form.get('person_{}'.format(id))if not person_name:flash("请输入修改后的人名")return redirect('/')person = Person.query.get(id)if not person:flash("人名不存在")else:person.name = person_nametry:db.session.merge(person)db.session.commit()except Exception as e:print(e)db.session.rollback()return redirect('/')@app.route("/update_phone/<id>", methods=['GET', 'POST'])
def update_phone(id):phone_name = request.form.get('phone_{}'.format(id))if not phone_name:flash("请输入修改后的手机")return redirect('/')phone = Phone.query.get(id)if not phone:flash("手机不存在")else:phone.name = phone_nametry:db.session.merge(phone)db.session.commit()except Exception as e:print(e)db.session.rollback()return redirect('/')@app.route("/delete_person/<id>")
def delete_person(id):person = Person.query.get(id)if not person:flash("人名不存在")else:try:Phone.query.filter(Phone.person_id == person.mid).delete()db.session.delete(person)db.session.commit()except Exception as e:print(e)db.session.rollback()return redirect('/')@app.route("/delete_phone/<id>")
def delete_phone(id):phone = Phone.query.get(id)if not phone:flash("手机不存在")else:try:db.session.delete(phone)db.session.commit()except Exception as e:print(e)db.session.rollback()return redirect('/')if __name__ == '__main__':# per_one = Person(name='You', age=18)# per_two = Person(name='Me', age=81)# per_three = Person(name='JackMa', age=60)# per_four = Person(name='Panshiyi', age=50)# per_five = Person(name='DingLei', age=40)# db.session.add_all([per_one, per_two, per_three, per_four, per_five])## phone_one = Phone(name='IPhone', person_id=1)# phone_two = Phone(name='Mi', person_id=3)# phone_three = Phone(name='NOKIA', person_id=2)# phone_four = Phone(name='HUAWEI', person_id=4)# phone_five = Phone(name='OPPO', person_id=5)# phone_six = Phone(name='VIVO', person_id=1)# db.session.add_all([phone_one, phone_two, phone_three, phone_four, phone_five, phone_six])# db.session.commit()app.run(debug=True)

flask_project.html 中的代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Person</title>
</head>
<body>
<form method="post"><label>人名:</label> <input id="person" name="person" type="text" value=""><br/><label>手机:</label> <input id="phone" name="phone" type="text" value=""><br/><br/><input id="search" name="search" type="submit" value="查询"><input id="add" name="add" type="submit" value="添加"><br/>
</form><br/>
{% for message in get_flashed_messages() %}{{ message }}
{% endfor %}<ul>{% for person in persons %}<li>{{ person.name }} <a href="/delete_person/{{ person.mid }}">删除</a><form method="post" action="/update_person/{{ person.mid }}"><input id="person_{{ person.mid }}" name="person_{{ person.mid }}" type="text" value=""> <input id="update_{{ person.mid }}" name="update_{{ person.mid }}" type="submit" value="修改"></form></li><ul>{% for phone in person.phones %}<li>{{ phone.name }} <a href="/delete_phone/{{ phone.pid }}">删除</a><form method="post" action="/update_phone/{{ phone.pid }}"><input id="phone_{{ phone.pid }}" name="phone_{{ phone.pid }}" type="text" value=""> <input id="update_{{ phone.pid }}" name="update_{{ phone.pid }}" type="submit" value="修改"></form></li>{% else %}<li>无</li>{% endfor %}</ul>{% endfor %}
</ul></body>
</html>

5. 创建数据表和添加数据

将代码中 db.drop_all() 和 db.create_all() 的注释取消,将添加数据的代码注释也取消,然后 python flask_project.py 运行代码,会在数据库中创建两张数据表 Person_tb 和 Phone_tb ,并分别在两张表中添加几条数据。

同时,程序会自动运行在 127.0.0.1:5000 上,开启监听。 先将程序停掉。

停掉之后,如果 5000 端口还被占用着,可以 kill -9 thread id 将进程关掉。

添加成功数据后,注释掉代码中的 db.drop_all() 和 db.create_all() ,将添加数据的代码也全部注释掉。

三、Nginx 安装和配置

1. 安装 Nginx

在 CentOS 中安装 Nginx 很简单,使用如下命令即可。

yum install nginx -y

安装成功后,nginx 还没有启动,要先开启 nginx 服务。

systemctl start nginx

然后使用 ps 命令查看 nginx 服务是否成功开启。

ps -ef | grep nginx

开启 nginx 成功后,在 windows 浏览器上访问服务器的 80 端口(阿里云上已经配置好80端口了,访问 ip:port,ip是服务器ip,port默认就是80),页面如下,说明 nginx 安装和开启成功。

2. 修改 Nginx 配置

nginx 已经安装成功了,但 nginx 默认监听的是80端口,要部署自己的项目,还需要修改配置,增加监听端口和路由转发规则。

nginx 的配置文件是 /etc/nginx/ 下的 nginx.conf ,vim nginx.conf 修改配置文件。

默认 user 是 nginx 用户(需要先创建这个用户才行),先将 user 修改为 root (实际项目中一般不会使用 root) ,然后参照默认监听80端口的配置增加一份 server 配置。这份配置是监听7777端口,这个端口在阿里云上配置好了,当服务器监听到7777端口的请求时,会将请求转发到 127.0.0.1:5000/ (服务器本地运行的Flask项目)。

    server {listen       7777;listen       [::]:7777 default_server;server_name  flask_project;# Load configuration files for the default server block.include /etc/nginx/default.d/*.conf;location / {proxy_pass http://127.0.0.1:5000/;}}

增加后的完整 nginx.conf 如下。

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/user root;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;events {worker_connections 1024;
}http {log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log  /var/log/nginx/access.log  main;sendfile            on;tcp_nopush          on;tcp_nodelay         on;keepalive_timeout   65;types_hash_max_size 2048;include             /etc/nginx/mime.types;default_type        application/octet-stream;# Load modular configuration files from the /etc/nginx/conf.d directory.# See http://nginx.org/en/docs/ngx_core_module.html#include# for more information.include /etc/nginx/conf.d/*.conf;server {listen       80 default_server;listen       [::]:80 default_server;server_name  _;root         /usr/share/nginx/html;# Load configuration files for the default server block.include /etc/nginx/default.d/*.conf;location / {}error_page 404 /404.html;location = /40x.html {}error_page 500 502 503 504 /50x.html;location = /50x.html {}}server {listen       7777;listen       [::]:7777 default_server;server_name  flask_project;# Load configuration files for the default server block.include /etc/nginx/default.d/*.conf;location / {proxy_pass http://127.0.0.1:5000/;}}# Settings for a TLS enabled server.
#
#    server {
#        listen       443 ssl http2 default_server;
#        listen       [::]:443 ssl http2 default_server;
#        server_name  _;
#        root         /usr/share/nginx/html;
#
#        ssl_certificate "/etc/pki/nginx/server.crt";
#        ssl_certificate_key "/etc/pki/nginx/private/server.key";
#        ssl_session_cache shared:SSL:1m;
#        ssl_session_timeout  10m;
#        ssl_ciphers HIGH:!aNULL:!MD5;
#        ssl_prefer_server_ciphers on;
#
#        # Load configuration files for the default server block.
#        include /etc/nginx/default.d/*.conf;
#
#        location / {
#        }
#
#        error_page 404 /404.html;
#            location = /40x.html {
#        }
#
#        error_page 500 502 503 504 /50x.html;
#            location = /50x.html {
#        }
#    }}

修改完成配置文件后需要重启 nginx ,使配置生效。

systemctl restart nginx

四、Gunicorn 安装和配置

在运行 Flask 程序时,默认使用的是 Flask 的 runserver 服务器,现在直接 python flask_project.py 运行 Flask 项目,在 windows 上用浏览器访问 http://120.77.235.113:7777/ (我使用的阿里云服务器ip是120.77.235.113,记得换成自己部署的ip)就可以正常访问到 Flask 项目了。

但是,runserver 只是一个供开发者调试的微型服务器,实际部署时不会这样使用。

通常使用的 HTTP 服务器有 Gunicorn 或 uWsgi ,两个都是满足 Python WSGI 协议的HTTP服务器。使用 uWsgi 需要再配置一份 uWsgi 的配置文件,使用 Gunicorn 会简单些,直接用命令运行代码就可以了,接下来就介绍 Gunicorn 的部署方法。

先安装 Gunicorn 。

pip3 install gunicorn

然后使用如下命令运行 Flask 服务器。

gunicorn -w 1 -b 127.0.0.1:5000 flask_project:app

-w 表示启动的进程数量,-b 表示服务运行的 ip 和端口,与 nginx 配置文件中转发的地址保持一致,后面跟启动文件和 Flask 实例名称,启动文件不带后缀 .py,与实例名称中间用冒号连接。如果需要以守护进程运行项目的话,再加一个 -D 参数,关于 gunicorn 的更多参数,可以使用 -h 查看帮助信息。

运行之后,(如果需要的话)可以查看 gunicorn 是否开启成功,也可以查看服务器是否在监听 7777 和 5000 端口。

ps -ef | grep gunicorn
netstat -ntlp

现在,项目运行起来了,在 windows 上访问 http://120.77.235.113:7777/ ,功能正常,部署成功。

使用 Nginx + Gunicorn 部署 Flask 项目相关推荐

  1. 使用 gunicorn 部署flask项目

    1.WSGI协议 Web框架致力于如何生成HTML代码,而Web服务器用于处理和响应HTTP请求.Web框架和Web服务器之间的通信,需要一套双方都遵守的接口协议.WSGI协议就是用来统一这两者的接口 ...

  2. Linux中 Nginx+uwsgi部署flask项目 Nginx负载均衡 反向代理

    Nginx是一款自由的.开源的.高性能HTTP服务器和反向代理服务器. 轻量级,同样起web服务,比 apache占用更少的内存及资源 抗并发, nginx处理请求是异步非阻塞的,而 apache则是 ...

  3. Gunicorn部署Flask

    Gunicorn是一个被广泛使用的高性能的Python WSGI UNIX HTTP服务组件(WSGI: Web Server Gateway Interface),移植自Ruby的独角兽(Unico ...

  4. Flask部署| gunicorn、nginx部署flask项目,并用supervisor来管理进程

    本科的时候做公众号,开始提供学生教务查询服务,后端从PHP转到Python,无论是使用django还是flask,部署都没PHP那么方便,每次修改程序完,都是ps ax,然后再kill,再run.em ...

  5. Gunicorn 部署flask-socketio项目,项目访问慢的问题

    Gunicorn 部署flask-socketio 项目 问题: 最近使用flask开发的websocket服务,要开始上线了,本地开发环境测试一切的OK.使用Gunicorn部署后,如果在/etc/ ...

  6. 在云平台部署flask项目

    云平台部署flask项目----以收截图小项目为例 因为我的阿里云(Windows server 2012)之前从未配置过python的环境,所以只能从零开始,直入主题 1.安装python3.6 直 ...

  7. 基于docker+gunicorn部署sanic项目

    基于docker+gunicorn部署sanic项目 源代码: https://github.com/ltoddy/Python-useful/tree/master/sanic-app 最近云服务提 ...

  8. nginx怎么部署php项目,nginx怎么正确部署前端项目

    目的: nginx部署打包成为dist的前端项目 (学习视频分享:php视频教程) 相关知识:docker 安装与使用 docker pull nginx docker run --restart=o ...

  9. 在nginx上部署vue项目(history模式);

    在nginx上部署vue项目(history模式): vue-router 默认是hash模式,使用url的hash来模拟一个完整的url,当url改变的时候,页面不会重新加载.但是如果我们不想has ...

最新文章

  1. linux ubuntu下怎样将pdf格式文件转换为doc格式文件,Ubuntu环境下把word文档转成pdf,把pdf文件转成jpg...
  2. Mac上webstorm与git仓库建立连接
  3. Kubernetes1.1源码分析(二)
  4. JMS学习五(ActiveMQ的本地事务)
  5. netbeans 添加gif图片_史上功能最强最全最好用的GIF动画制作手机app——GIF豆豆——手机ae...
  6. 网络协议从入门到底层原理(5)传输层(UDP、TCP - 可靠传输、流量控制、拥塞控制、建立连接、释放连接)
  7. 如何在Angular 2项目中使用Bootstrap css库
  8. 【快讯】中国首个开源协议----木兰隆重发布
  9. 调用大汉三通短信接口,很详细
  10. sop流程图模板_标准作业流程SOP详解,附流程图绘制规范,不愁不会画!
  11. Win10注册表损坏的修复方法
  12. 在 unity中可以使用的直接设置音量大小的方法
  13. 【LaTex】 - 对齐符号的用法,换行符\\的用法,Misplaced 错误怎么解决
  14. 安装tesseract时Status of chi_tra: sendrequest error什么意思
  15. python爬虫之爬取起点中文网小说
  16. wps如何修改已经存在的目录标题内容?
  17. GPIO设置高低电平
  18. 【NLP】如何评价一个摘要是合适的
  19. Ansible playbook Vault 加密
  20. 图数据库――大数据时代的高铁

热门文章

  1. Handler源码解读
  2. Slog59_项目上线之域名备案时两家或多家运营商之间的业务交叉经历
  3. 数据库索引优化原理,索引的工作机制
  4. Linux下启动启动tomcat 服务器报错 The file is absent or does not have execute permission
  5. C# Repeater根据条件后台设置前台行背景色
  6. 一个Web开发的客户端基础技术测试Demo
  7. 2021年100题Java春招面试题
  8. TypeScript入门教程 之 Let 关键字
  9. Serverless 实战 —— 基于 Serverless 的 VuePress 极简静态网站
  10. Docker Compose运行MySQL、Redis服务