提供:ZStack云计算
前言
我们在之前的《Python Web服务器对比》文章中介绍过,uWSGI是一个宏大的项目,它能做的事情远远超过提供Web服务。不过很多Web应用的部署仍然青睐于它,因为它功能丰富,容易配置,而且跟Nginx配合的很好。
本文将深入介绍uWSGI的安装,以及在上面部署各种Python框架应用的步骤,无论是中小规模的部署还是生产环境的大规模部署都有所涉及。
本文还将同时介绍Nginx的相关配置(以及原理)。Nginx与uWSGI能够默契配合,针对大多数应用部署都是极好的选择。
了解uWSGI和Nginx的用法
uWSGI是一个充满野心的项目,它提供了各式各样的工具,其功能远远超过了Web应用托管的需求。多年以来,它已经成为了很多系统管理员和开发者们必不可少的工具之一。
Nginx自从0.8.40版本以来开始支持uwsgi协议,从此,在uWSGI上运行的WSGI应用就可以以最佳的方式与Nginx进行通讯,在配置的灵活性和性能方面都十分出色,使得该组合成为很多部署场景下的极佳选择。
uWSGI概述
以下是从《Python Web服务器对比》一文中摘抄的段落:
“虽然uWSGI的命名规范有点让人摸不着头脑,不过它是一个包含众多组件的大项目,致力于为托管服务提供一整套解决方案。uWSGI server作为其组件之一提供了运行Python WSGI应用的功能,该组件支持多种协议,包括其自家的uwsgi wire协议——可以说是SCGI的对应。应对在应用服务器之前使用独立HTTP服务器的需求,NGINX和Cherokee web服务器进行了模块化以支持uWSGI自家的uwsgi协议(该协议在uWSGI上是性能最好的),如此可以直接控制其进程。”
uWSGI的亮点
- uWSGI提供了WSGI适配器,如此可以完全支持运行在USGI上的Python应用。
- uWSGI提供了libpython。它在启动时加载应用代码并提供Python解释器的功能,对进来的请求进行语法分析以调用Python callable。
- uWSGI内置NGINX支持(以及Cherokee+和lighttpd支持)。
- uWSGI采用C语言编写。
- 由于uWSGI组件众多,所以如果需要扩展功能的时候会比较方便。
- 项目处于活跃状态,更新周期很快。
- 它提供了不同的应用引擎(异步、同步都支持)。
- 它占用的内存比较少。
配合Nginx进行Web应用部署
Nginx是一个高性能的web服务器/代理服务器/反向代理服务器,因为其轻量、易用和可扩展性(插件)而广受欢迎。其架构的设计使得它具备很强的请求处理能力(理论上是无限的),以至于一些大流量的网站几乎没有其他选择。
注:“处理”连接的技术含义是不丢弃并返回一些结果,一次成功的返回也可能是一条错误信息。要返回用户期待的结果,还需要应用和数据库方面能够保持正常工作。
uWSGI配合Nginx反向代理
很多框架和应用服务器都可以对静态文件的请求(如JavaScript、CSS、图片等)返回来自应用的响应,不过更好的做法是用反向代理服务器(比如Nginx)来处理此类请求,减轻应用服务器的负载,获得更好的性能。
随着应用扩大,可能会需要分布到不同的服务器(云主机)上,此类需求在早期可以用反向代理实现。
此外,Nginx的可扩展性(除了缓存之外,还有failover等其他机制)对于一些比较复杂的Web应用也很有帮助。
一个简单的服务器架构:
客户端请求 ----> Nginx (反向代理)
|
/|\
| | `-> App. Server I. 127.0.0.1:8081
| `--> App. Server II. 127.0.0.1:8082
`----> App. Server III. 127.0.0.1:8083
注:只监听127.0.0.1
的应用只能从本地访问,而如果设置成0.0.0.0
就能够从外部访问了。
针对生产环境预备云主机
云主机的预备将执行如下步骤:
- 更新操作系统
- 下载安装通用Python工具(pip、virtualenv等)
- 创建应用运行的虚拟环境(提供了uWSGI等依赖)
有关Python工具的安装可参考这篇文章。
更新操作系统
以下操作将在一台新建的云主机上进行。如果你打算在一台经常使用的云主机上操作,我的建议是切换到一台新主机上。
Debian类系统(Ubuntu、Debian等)的更新:
aptitude update
aptitude -y upgrade
RHEL类系统的更新:
yum -y update
安装配置Python、pip和virtualenv
CentOS/RHEL系统默认状态下是比较裸的,大部分工具都需要自己安装(比如通过yum)。这里有一篇文章介绍如何在CentOS 6.4和5.8上安装Python 2.7.6和3.3.3。
Ubuntu和Debian的最新版本默认包含了Python解释器,我们只需要安装下面的这些软件包就行:
- python-dev(开发工具)
- pip(包管理工具)
- virtualenv(用于创建隔离的虚拟环境)
python-dev
python-dev是系统级的软件包,包含了用于构建Python模块的开发工具。
使用aptitude进行安装:
aptitude install python-dev
pip
pip软件包管理器可用于安装我们需要的应用。
运行如下命令以安装pip(需要sudo权限):
curl https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py | python -
curl https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python -
export PATH="/usr/local/bin:$PATH"
virtualenv
每个Python应用最好是跟它的依赖项一起在一个独立的环境下运行。针对一个环境,最简单的描述方式就是一个独立的路径。virtualenv就是应对环境管理的需求而诞生的工具。
使用pip安装virtualenv:
sudo pip install virtualenv
创建独立的虚拟(Python)环境
我们将创建一个环境用于我们的应用部署。如果你之前在本地开发机上的Python应用还没有设置过virtualenv,我建议你创建一个环境,将你的应用以及其依赖项都转移到该环境下。
首先创建一个目录。目录的命名按照自己的需求来,这里使用my_app
作为命名。
mkdir my_app
进入该目录,创建一个新的虚拟环境。这里使用my_app_venv
作为该环境的命名,你可以根据自己的需要进行命名:
cd my_app
virtualenv my_app_venv
创建一个目录用于存放Python应用模块:
mkdir app
在虚拟环境中激活解释器:
source my_app_venv/bin/activate
至此,你的目录结构应该是这样的:
my_app # 主目录
|
|=== my_app_venv # 虚拟环境目录,包含Python解释器
|=== app # 应用模块所在目录
|..
|.
下载安装uWSGI
应用管理的最佳实践之一是在虚拟环境下包含所有的应用依赖项,所以我们之后的下载安装都要在刚才创建的环境中进行。
在刚才的虚拟环境目录下执行安装操作,软件会安装在该虚拟环境下。如果执行安装操作时不在任何虚拟环境下,则会全局生效,而这是我们不想要的。请随时记得使用virtualenv。
用pip安装uWSGI:
pip install uwsgi
下载安装Nginx
CentOS/RHEL用户可参考这篇文章用yum安装Nginx。Ubuntu/Debian用户请执行如下命令(也可参考这篇文章):
sudo aptitude install nginx
然后启动nginx:
sudo service nginx start
Nginx服务可通过如下命令停止:
sudo service nginx stop
以及重启(每次更改Nginx配置后都需要重启):
sudo service nginx restart
用uWSGI托管Python WSGI应用
uWSGI的用法跟其他应用容器没有太大差别,它需要你的应用提供一个接入点(callable)。在启动时,这个callable与其他的配置变量一起被传递给uWSGI,然后uWSGI开始工作。当请求到达uWSGI时,它对请求进行处理,然后将结果传递给应用控制器。
架构:
........
/|\
| | `-> App. Server I. 127.0.0.1:8080 <--> Application
| `--> App. Server II. 127.0.0.1:8081 <--> Application
.....
WSGI
WSGI是web服务器和应用之间的一个接口,以确保不同的服务器和应用(框架)之间的通讯满足一组标准,从而满足兼容性的需求(比如从生产环境切换到生产环境的场景)。这在现代软件开发中很有必要。(有关WSGI的详细说明可参考这篇文章。)
WSGI应用对象(Callable):“wsgi.py”
如上所述,运行在WSGI上的Web服务器需要一个应用对象(也就是你的应用的callable)。大部分框架和应用会包含一个wsgi.py作为这个对象。
下面,我们会创建一个wsgi.py,将其导入应用以提供给uWSGI。你也可以使用其他的文件名,不过wsgi.py是最常用的(比如Django使用的就是这个文件名。)
创建wsgi.py文件(我使用nano,你可以使用自己习惯的编辑器):
nano wsgi.py
将基本的WSGI应用代码复制粘贴到该文件内(此处是最基本的示范文件,到时候你需要把自己应用的内容替换进来):
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return ["Hello!"]
每次请求到达服务器时,服务器在解析URL的时候(比如 mysite.tld/controller/method/variable)都会使用这个callable来运行应用的请求处理器(也就是控制器)。
代码粘贴完毕后,保存退出。至此,你的应用目录应该是如下结构:
my_app # Main Folder to Contain Everything Together
|
|=== my_app_venv # V. Env. folder with the Python Int.
|=== app # Your application module
|
|--- wsgi.py # File containing application callable
|..
|.
运行服务器
uWSGI的配置项众多,我们先从最简单的配置开始。
uWSGI的常规用法如下:
uwsgi [option] [option 2] .. -w [wsgi file with app. callable]
从wsgi.py运行uWSGI:
uwsgi --socket 127.0.0.1:8080 --protocol=http -w wsgi
该命令会在前台运行服务,用CTRL+C快捷键停止。后台运行的命令如下:
uwsgi --socket 127.0.0.1:8080 --protocol=http -w wsgi &
注意:稍后配置Nginx的时候需要移除--protocol=http
这一部分,否则Nginx和uWSGI之间会无法通讯。
应用在后台运行之后,我们需要进程管理器来对其进行管理。
管理uWSGI服务器进程
进程管理的命令列表如下:
- SIGHUP
-HUP
正常重启worker和应用 - SIGTERM
-TERM
强制重启 - SIGINT
-INT
和 SIGQUIT-QUIT
立刻终止所有worker - SIGUSR1
-USR1
输出状态数据(stdout) - SIGUSR2
-USR2
输出worker状态 - SIGURG
-URG
从快照恢复 - SIGTSTP
-TSTP
暂停或恢复一个实例的运行 - SIGWINCH
-WINCH
唤醒之前被syscall阻塞的worker
示范:用SIGHUP重启服务
该命令按正常方式重启服务,即,它会等待worker完成当前的任务,然后将其终止。下次启动时会继承本次的配置。
kill -HUP [PID]
你也可以不使用PID,而使用pidfile
选项通过文件控制进程。
示范:用SIGINT停止服务
服务和进程的停止需要INT
信号,该命令会终止所有后台中运行的进程。
kill -INT [PID]
配置Nginx
接下来我们将配置Nginx以使其与uWSGI服务器对话。打开nginx.conf
文件进行编辑:
sudo nano /etc/nginx/nginx.conf
将如下内容复制粘贴到文件中以启用反向代理功能。(有关SSL证书的配置可参考这篇文章。)
针对Web应用的配置示范:
worker_processes 1;
events {
worker_connections 1024;
}
http {
sendfile on;
gzip on;
gzip_http_version 1.0;
gzip_proxied any;
gzip_min_length 500;
gzip_disable "MSIE [1-6]\.";
gzip_types text/plain text/xml text/css
text/comma-separated-values
text/javascript
application/x-javascript
application/atom+xml;
# Configuration containing list of application servers
upstream uwsgicluster {
server 127.0.0.1:8080;
# server 127.0.0.1:8081;
# ..
# .
}
# Configuration for Nginx
server {
# Running port
listen 80;
# Settings to by-pass for static files
location ^~ /static/ {
# Example:
# root /full/path/to/application/static/file/dir;
root /app/static/;
}
# Serve a static file (ex. favico) outside static dir.
location = /favico.ico {
root /app/favico.ico;
}
# Proxying connections to application servers
location / {
include uwsgi_params;
uwsgi_pass uwsgicluster;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
}
}
保存退出,运行如下命令重启Nginx以激活变更的配置:
sudo service nginx stop
sudo service nginx start
有关Nginx配置的更多说明,可参考这篇文章。
配置uWSGI
uWSGI在启动时有多种方法接收必要的配置(如运行在哪个socket、进程的数量、主进程设置等),下面将介绍其中三种:
- 以参数方式传递配置变量
- 使用
.ini
文件 - 使用
.json
文件
注:uWSGI还提供了若干协议以将这些配置文件转换成stdin和HTTP。
方案1:以参数方式传递配置变量
最基础的方法,但容易造成混乱且不易管理。该方法的使用就跟其他shell脚本一样,将配置作为参数放到命令中。
用法:
# uwsgi [option] [option 2] .. -w [wsgi.py with application callable]
# Simple server running *wsgi*
uwsgi --socket 127.0.0.1:8080 -w wsgi
# Running Pyramid (Paster) applications
uwsgi --ini-paste production.ini
# Running web2py applications
uwsgi --pythonpath /path/to/app --module wsgihandler
# Running WSGI application with specific module / callable names
uwsgi --module wsgi_module_name --callable application_callable_name
uwsgi -w wsgi_module_name:application_callable_name
更完整的列表可参考其官方文档页面。
方案2:使用.ini
文件
使用.ini
文件的方案可能比第一种好一些,此类文件的结构很容易理解,只需要每次uWSGI启动脚本执行的时候一起加载即可生效。
.ini
文件示范(example_config.ini):
[uwsgi]
# -------------
# Settings:
# key = value
# Comments >> #
# -------------
# socket = [addr:port]
socket = 127.0.0.1:8080
# Base application directory
# chdir = /full/path
chdir = /my_app
# WSGI module and callable
# module = [wsgi_module_name]:[application_callable_name]
module = app:application
# master = [master process (true of false)]
master = true
# processes = [number of processes]
processes = 5
..
用法:
uwsgi --ini example_config.ini
方案3:使用.json
文件
Working with .json
files is, apart from the structure, same as the examples shown above.
跟上面的用法差不多,只是文件结构不一样。
.json
文件示范(example_config.json):
{
"uwsgi": {
"socket": ["127.0.0.1:8080"],
"module": "my_app:app",
"master": true,
"processes": 5,
}
}
用法:
uwsgi --json example_config.json
有关uWSGI配置的更多信息可参考其官方文档页面。
uWSGI的常用配置
uWSGI默认是可以通吃各种框架/应用/平台的,不过有时候我们可能还是需要根据自己的需求指定一些配置。
根据uWSGI文档的说明,可能的配置组合几乎是无限的,本文只能讨论一些最常用的或者最关键的部分,并对其实现进行说明。
下面的示范是配合.ini
文件使用的。使用.json
文件的同学需要将其修改成json的格式。
Sockets
http-socket
设定uWSGI绑定的HTTP socket,如:
http-socket = :8080
socket
通过默认协议将uWSGI绑定到特定的socket,如:
socket = 127.0.0.1:8080
processes (workers)
设定可用于处理请求的进程数,如:
processes = 5
protocol
默认协议是uwsgi,这个配置项可以对其进行修改,如:
protocol = http
管理
master
用于启用或禁用uWSGI主进程。主进程可对其他接收请求的进程进行管理,这样有很多好处,比如无需打扰sockets就能优雅的重启workers,从而避免当机时间。配置方法:
master = true
max-requests
设定最大可以处理的请求数。如果你担心内存泄露而又没有更好的预防办法,可以考虑这个配置项。用法:
max-requests = 1001
threads
设定每个进程内运行的线程数量(rethread模式)。可以配合processes设置项使用以调整并发的规模。用法:
threads = 2
日志
disable-logging
关闭日志功能。用法:
disable-logging = true
uWSGI processes
procname
可自行设定进程名称,如:
procname = My Application
uid
指定uWSGI服务器运行的用户uid
,如:
uid = 1001
gid
指定uWSGI服务器运行的用户组gid
,如:
gid = 555
vacuum
退出时移除所有生成的pidfile/socket。
daemonize
将uWSGI设置为守护进程,并将信息写入指定的参数(也就是日志文件)。用法:
daemonize = /tmp/uwsgi_daemonize.log
pidfile
让uWSGI将进程的PID写入指定文件。对于uWSGI进程管理很有用。用法:
pidfile = /tmp/proj_pid.pid
其他
harakiri
设置一个进程在被终止或回收(一般由内存管理或进程管理服务触发)之前被允许存活的最大时间。用法:
harakiri = 30
针对上百个配置项的完整配置说明请参考官方文档。
延伸阅读:
防火墙
- Setting up a firewall using IP Tables
SSH加固
- How To Protect SSH with fail2ban on Ubuntu
- How To Protect SSH with fail2ban on CentOS 6
创建报警
- How To Send E-Mail Alerts on a CentOS VPS for System Monitoring
每日的访问日志监控
- How To Install and Use Logwatch Log Analyzer and Reporter