*记:基于Centos+uWSGI+Nginx部署web应用
碎碎念
- 最近在给手里在做的几个python web应用统一配置云服务器。期间涉及的配置步骤和一些通信概念还是比较多,而且网上提供的方法大多都不是很有效,鱼龙混杂。故写篇文章详细记录一下过程,整合一些可信且有效的信息来源,方便乘凉/提前周更。
- 本文是对 在基于Centos系统的云服务器上配置python web 的一个简略的过程记录,此外还包括了django的产品环境配置,以及对python web通信协议WSGI,nginx代理服务器等概念的介绍。
【1】
在Centos上进行python配置
- 安装python3.6版本。切记不能动原有的python2环境(centos系统运行依赖该环境)。
$ yum -y install python36 python36-devel
- 创建虚拟环境pyweb,用于web应用程序的运行。
$ cd /opt
$ python3.6 -m venv pyweb
- 为其安装库依赖,requirements.txt 中是一些常用的第三方包,可以自己定义。
$ source /opt/pyweb/bin/activate
(pyweb)$ pip install --upgrade pip setuptools
(pyweb)$ vi /opt/pyweb/requirements.txt
django==3.1.5
djangorestframework==3.12.2
django-allauth==0.44.0
django-cors-headers==3.7.0
django-rest-auth==0.9.5
django-filter==2.4.0
coreapi==2.3.3
numpy==1.19.5
pymysql==1.0.2
joblib==1.0.0
pandas==1.1.5
sklearn
pillow==8.2.0
(pyweb)$ pip install -r /opt/pyweb/requirements.txt
uwsgi安装
- 当构建uWSGI时,需要使用到C编译器,如gcc或clang。如下:
(pyweb)$ yum install gcc -y
(pyweb)$ pip install uwsgi==2.0.19
MYSQL数据库配置
- 数据库安装,并配置其服务端开机启动。
$ sudo dnf install @mysql
$ sudo systemctl enable --now mysql
- 设置数据库密码安全。
$ sudo mysql_secure_installation
- 启动数据库客户端并为服务器创建数据库。
$ mysql -u root -p
- 安全起见,也可以创建一个用户tester,并开启其只能读写dbtest数据库的权限。
mysql> create database dbtest;
mysql> create user 'tester'@'%' identified by '******';
mysql> grant all privileges on dbtest.* to 'tester'@'%';
NGINX安装
- 在下述位置写入一个nginx.repo文件。
$ vi /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1
- 安装并配置其开机启动。
$ yum -y install nginx
$ systemctl enable nginx
【2】
settings环境配置
- 以下修改在本地进行即可。
- 修改settings.py文件为settings文件目录,方便本地和产品环境的切换。如下目录中,base.py即为原settings.py文件。其上覆盖两个文件,local.py用于本地测试环境,pro.py用于生产环境。
settings/
__init__.py
base.py
local.py
pro.py
- 修改base.py中的BASE_DIR配置。
BASE_DIR = os.path.dirname(os.path.dirname(
os.path.abspath(
os.path.join(__file__, os.pardir)
)
))
- 配置local.py
from .base import *
DEBUG = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
- pro.py
from .base import *
DEBUG = False
ADMINS = (
('zixun HUANG', '[email protected]'),
)
ALLOWED_HOSTS = ['*']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dbtest',
'OPTIONS': {
'charset': 'utf8mb4',
},
'USER':'tester',
'PASSWORD':'******',
'HOST':'127.0.0.1',
'PORT':3306,
'CHARSET':'utf8'
}
}
# REDIS_HOST = 'localhost'
# REDIS_PORT = 6379
# REDIS_DB = 0
- __init__.py
import pymysql
pymysql.install_as_MySQLdb()
静态数据集的收集
- 编辑base.py中的STATIC_ROOT,并注释掉STATICFILES_DIRS,否则导出django的静态数据集会失败。
STATIC_URL = '/static/'
# STATICFILES_DIRS = (
# os.path.join(BASE_DIR, "static"),
# )
STATIC_ROOT= os.path.join(BASE_DIR, "static")
- 导出静态数据集,该命令会在你的项目目录中创建一个静态文件目录。之后会配置NGINX直接服务于这些静态文件。
(pyweb)$ python manage.py collectstatic --settings=\
your_project_name.settings.base
CSRF和跨域配置
- 编辑base.py文件。
INSTALLED_APPS += [
'corsheaders',
]
MIDDLEWARE += [
'corsheaders.middleware.CorsMiddleware',
]
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
'http://*.*.*.*:*',
)
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
'VIEW',
)
CORS_ALLOW_HEADERS = (
'XMLHttpRequest',
'X_FILENAME',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
'Pragma',
)
在云服务器同步数据库
- 如果已经在本地makemigrations,则将文件用xftp拷贝至云服务器后。直接migrate即可。
- 该步骤要注意文件目录的管理,笔者在根目录下新建了mysites文件夹用于web应用的管理。
(pyweb)$ python manage.py makemigrations --settings=your_project_name.settings.local
(pyweb)$ python manage.py migrate --settings=your_project_name.settings.pro
【3】
WSGI,uWSGI,uwsgi简单说明
- WSGI全称为Web Server Gateway Interface,是为python语言定义的一种描述web server如何与web application通信的规范。以下内容摘自PEP 3333:
- The WSGI interface has two sides: the "server" or "gateway" side, and the "application" or "framework" side. The server side invokes a callable object that is provided by the application side. The specifics of how that object is provided are up to the server or gateway.
- 在WSGI通信协议中:web server负责从客户端接收request,并转发给web application。接收web application返回的response,并返回给客户端。web application接收由server转发的request,处理请求,并把处理结果response发送给server。
- WSGI协议解决了server与application之间互相限制的问题。即可以选择任意实现了WSGI application的框架(如django,flask),和任意实现了WSGI server协议的服务器(如uWSGI,Gunicorn)。
- uwsgi和WSGI一样,也是一种通信协议。其为uWSGI自有协议,用于定义传输信息的类型,用于与nginx等代理服务器通信。
- uWSGI即为实现了WSGI和uwsgi两种协议的Web服务器。
使用uwsgi.ini配置文件启动项目
- 新建config文件夹和tmp文件夹处于settings的上一级目录,也就是和manage.py处于同一级目录。在config文件夹中新建uwsgi.ini文件。
$ vi /your_absolute_path/config/uwsgi.ini
[uwsgi]
# https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html
chdir=/your_absolute_path/
module=your_project_name.wsgi:application
env = DJANGO_SETTINGS_MODULE=your_project_name.settings.pro
#the local unix socket file than commnuincate to Nginx
socket=%(chdir)tmp/uwsgi.sock
processes = 8
workers=5
procname-prefix-spaced=your_project_name
py-autoreload=1
http=0.0.0.0:8082
uid=root
gid=root
master=true
vacuum=true
thunder-lock=true
enable-threads=true
harakiri=30
post-buffering=4096
daemonize=%(chdir)tmp/uwsgi.log
pidfile=%(chdir)tmp/uwsgi.pid
- 运行uWSGI,该命令需要在之前配置的pyweb环境下进行。停止该进程为--stop相应pid文件。
(pyweb)$ uwsgi --ini config/uwsgi.ini
- 但页面显示无静态文件加载。可在uwsgi.ini中添加如下配置:
for =static media
static-map=/static=%(chdir)/%(_)
endfor =
Nginx简单介绍
- Nginx是一款轻量级的Web服务器/反向代理服务器,其启动快,内存占用少,高并发能力强。套娃分享一篇文章:https://zhuanlan.zhihu.com/p/34943332。
使用nginx配置文件启动uwsgi
- Nginx可以更高效的提供静态文件,并将动态请求转发给uWSGI worker。此外还可以利用其配置反向代理,负载均衡等来提高服务器性能。
vi /your_absolute_path/config/nginx.conf
upstream app_name{
server unix:/your_absolute_path/tmp/uwsgi.sock;
}
server{
listen 8081;
server_name your_server_name;
access_log /var/log/nginx/access.log;
charset utf-8;
gzip_types text/plain application/x-javascript text/css text/javascript application/x-httpd-php application/json text/json image/jpeg image/gif image/png application/octet-stream;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location / {
include /etc/nginx/uwsgi_params;
uwsgi_connect_timeout 20;
uwsgi_pass app_name;
}
location /static {
alias /your_absolute_path/static;
}
location /media {
alias /your_absolute_path/media;
}
}
$ vi /etc/nginx/nginx.conf
http {
......
# Load modular configuration files from the /etc/nginx/conf.d directory.
include /etc/nginx/conf.d/*.conf;
include /your_absolute_path/config/*.conf;
......
server {
......
}
}
- 运行nginx,或重新加载/重启。
service nginx start/reload/restart
- 访问8081端口,已经可以正常访问该页面。此外可以注释掉8082和80端口的页面(分别在uwsgi.ini文件和nginx主配置文件中)。
nginx worker对media目录的访问权限
- 分别运行以下命令,可查看nginx和uWSGI的worker权限:
$ ps -ef|grep nginx
$ ps -ef|grep uWSGI
- 可以看到,uWSGI的worker用户为root(在uwsgi.ini文件中已进行配置)。而nginx worker的用户默认为nginx,该用户对media目录没有读权限(该目录指向root用户)。通过以下命令,对nginx worker赋权:
chown -R nginx:nginx /your_absolute_path/static/
chmod -R ug+r /your_absolute_path/static/
chown -R nginx:nginx /your_absolute_path/media/
chmod -R ug+r /your_absolute_path/media/
SSL安全连接配置
- HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。如果没有配置ssl的话,是无法通过https对站点进行访问的。只能通过http进行访问。
- 编辑nginx.conf主配置文件,修改server 443 ssl中的两个文件路径配置为自己域名的ssl证书以及密钥的路径即可:
......
ssl_certificate "/etc/pki/nginx/yours.crt";
ssl_certificate_key "/etc/pki/nginx/private/yours.key";
......
- 重启nginx代理服务器。
service nginx restart
- 之后为django代码中的settings/pro.py文件添加如下配置,该配置会把客户端请求自动转向https:
SECURE_SSL_REDIRECT = True
CSRF_COOKIE_SECURE = True