配置uwsgi+flask+nginx+https

本文最后更新于:2022年7月21日 下午

配置uwsgi+flask+nginx+https

由于项目需要,最近在一台腾讯云Ubuntu服务器上配置了全套的环境,实现了一个微信小程序的后端服务器,记录下这个笔记以免忘记。

整体理论架构

用户使用浏览器或者微信小程序通过80/443端口发送数据到Nginx,然后通过一个指定的端口发送到uWSGI,然后uWSGI调用我们写的Python代码,也就是Flask框架,在Flask框架中使用数据库或深度学习对数据进行其他处理。

graph 
A[浏览器/小程序] --HTTP/HTTPS--> B[Nginx]
B --转发请求--> C[uWSGI]
C --uWSGI协议--> D[Flask]
D --> E(Redis)
D --> F(MySQL)
D --> G(PyTorch)

服务器初始化

刚到手的服务器镜像肯定要update一下啦

1
2
sudo apt-get update
sudo apt-get upgrade

配置Python环境

Python虚拟环境使用virtualenv而不是conda,因为后者尝试之后,在启动uwsgi的时候会出现PYTHONHOME的Bug,很久都解决不了,换成virtualenv就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 安装python3
sudo apt-get install python3
# 找到python路径
which python
# 安装virtualenv环境
sudo pip install virtualenvwrapper
sudo pip install virtualenv
# 找到virtualenvwrapper.sh路径
which virtualenvwrapper.sh
> /usr/local/bin/virtualenvwrapper.sh
# 创建虚拟环境的存放目录
mkdir ~/.virtualenvs

# 配置环境
vi ~/.bashrc
# 最后加上
# 第一行是添加虚拟环境的目录 第二行是指定python的目录 第三行运行虚拟环境的初始化脚本
> export WORKON_HOME=$HOME/.virtualenvs
> VIRTUALENVWARPPER_PYTHON=/usr/bin/python
> source /usr/local/bin/virtualenvwrapper.sh
# 可能需要
source ~/.bashrc

# 使用mkvirtualenv创建环境
mkvirtualenv -p [python路径] [环境名字]
# 然后就可以使用workon来切换
workon [环境名字]
# 或者退出
deactivate

然后就是安装python的一系列包了,直接用pip安装,腾讯云或者阿里云买的服务器自动用内网下载还是非常快的,这里就只列出一部分了

1
pip install flask redis pymysql

配置数据库

Redis

1
2
3
4
5
6
# 比较容易安装
sudo apt-get install redis-server
# 开启服务端
redis-server
# 开启命令行
redis-cli

MySQL

1
2
3
4
5
sudo apt-get install mysql-server
# 初始化配置 会问你一些问题 并且设置密码
sudo mysql_secure_installation
# 检查服务状态 假如有绿色active就正常
systemctl status mysql.service

为了用pymysql操作数据库,需要确定能用密码登录

1
2
3
4
5
6
7
8
9
10
11
# root是默认用户名 输入这行命令之后输入密码
mysql -u root -p
# 假如无法进入(报错1045 1698),则进行以下步骤
sudo mysql
mysql> USE mysql;
mysql> UPDATE user SET plugin='mysql_native_password' WHERE User='root';
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';
mysql> FLUSH PRIVILEGES;
mysql> exit;
service mysql restart

配置完这些之后,可以写一些代码来测试是否可用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# python代码
import redis
redis_pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)
r = redis.Redis(connection_pool=redis_pool)

import pymysql
db = pymysql.connect(
host='localhost',
port=3306,
user='root',
password='密码',
database='数据库',
charset='utf8'
)

配置uWSGI

1
2
# 安装uwsgi 假如出现了很多红色的编译错误,则搜索报错的头文件并安装对应的库
pip install uwsgi

然后找一个地方来保存uWSGI的配置文件,后缀名为.ini

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[uwsgi]

# master 不知道是啥,写成true
master = true
# 就是flask应用运行的那个py文件名
wsgi-file = app.py
# flask应用的工程目录
chdir = /home/ubuntu/dev/Myproj
# 与nginx进行通信的端口,指定一个不被占用的就行,记住有个冒号
socket = :5200
# flask中的应用名字,假如没有指定就是文件名
callable = app
# 处理器数量和线程数
processes = 4
threads = 10
# 日志保存目录,自动新建
daemonize = logs/uwsgi.log
# python的环境
home = /home/ubuntu/.virtualenvs/test
# pid文件,自动新建,用来确定启动的进程pid
pidfile = uwsgi.pid

配置好之后,通过下面命令来管理

1
2
3
4
5
6
7
8
9
# 启动
uwsgi --ini ini文件名.ini
# 可以查看日志文件是否运行成功
vi logs/uwsgi.log
# 停止运行
uwsgi --stop uwsgi.pid
# 假如停止失败,手动查询然后杀死进程
ps ax|grep uwsgi
kill -9 [pid]

配置nginx

1
2
3
4
# 直接安装
sudo apt-get install nginx
# 查看运行是否正常,有绿色active就是正常
systemctl status nginx

假如一切正常,这个时候用浏览器访问ip应该就能Nginx的欢迎页面了(也可能出现要备案的信息)

HTTPS

微信小程序需要后端是https,所以去腾讯云买一个SSL证书,然后下载nginx格式可以得到四个文件:.csr .key .crt .pem 。把crt和key上传到服务器,找一个地方保存下来,之后要用到。

Nginx与uWSGI打通

找到nginx的安装目录,我的在/etc/nginx。然后找到nginx.conf这个文件,在http字段下添加下述信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
##
# flask uWSGI
##
upstream flask {
server 0.0.0.0:5200;
}
server {
listen 443 ssl;
server_name 网站域名;
ssl_certificate crt文件路径;
ssl_certificate_key key文件路径;
下面的4个要根据证书类型填写
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;

charset utf-8;
client_max_body_size 75M;
location /static/(.*) {
alias 静态网页存放路径;
}
location / {
uwsgi_pass flask;
include /etc/nginx/uwsgi_params;
}
}

重要!uWSGI的多进程说明

uWSGI默认是采用一个master process来初始化application,然后把它复制到其他的工作进程中。也就是说,在app.py里面加载的深度学习模型或者初始化的数据库连接,只会加载一次。

若不想要这种模式,则在ini文件中指定lazy-apps = true

坑!uWSGI调用flask不会执行if __name__ == '__main__'

因为uWSGI自动调用flask,__name__的值发生变化,在我这里是uwsgi_file_app.

所以用uWSGI的时候也不需要app.run()