相关概念:
1)我们将以Nginx为web服务器作为反向代理,为应用服务器作代理,提供更稳定连接和处理任务。
2)WSGI:是一个Python规范,定义了应用框架和应用网络服务器间通信的标准接口。它可以简化和标准化这些组件通信,已保证一致性和互换性。这就定义了一个可以被其他协议使用的基本的API接口。
3)uWSGI:是一个应用服务器容器,致力于提供全栈的开发和部署网络应用和服务。主要的部件是应用服务器,它可以处理不同语言的应用程序。它可以与使用WSGI规范方法的应用进行通信,也可以与大量使用其他协议的web服务器通信。将传统web服务器请求转换成应用可处理的请求是它的一部分功能。
4)uwsgi:一个快速的,二进制协议。通过uWSGI服务器实现,来和全功能web服务器通信。他是一个有线协议,不是传输协议。与web服务器交互的首选方法就是将请求由uWSGI代理。
WSGI 应用的要求:
WSGI规范定义了Web服务器和堆栈的应用部分之间的接口。在此,Web服务器就是指uWSGI服务器,它负责通过WSGI规范将客户请求转换为应用,这简化了通信,并创建了解耦和的组件,这样你可以轻松的交换任意一边,而不会遇到麻烦。
Web服务器(uWSGI)必须能够触发定义的应用程序入口点向应用发送请求。Web服务器可以带参数调用一个函数。预期的参数是一个环境变量的字典和一个由web服务器(uwsgi)组件提供的应用程序入口点。
作为响应,应用返回一个迭代,它可以用于产生客户响应的主体。应用也会访问web服务器组件入口,入口将接收一系列参数。第一个参数是HTTP状态码,第二个是元组列表,每个元组定义返回客户端的响应头和值。
由uWSGI提供的web服务器组件,在这个交互中,我们只需要确保应用有上述的质量。我们也将设置Nginx来处理实际客户请求,并将其代理送到uWSGI服务器。
安装组件:
Centos需要安装EPEL库,以获取更大范围的包:
yum install epel-release
安装编译环境:用以编译uWSGI二进制文件。
yum groupinstall "Development tools"
apt-get install build-essential
安装python开发库和编译头部,安装pip:python包管理器。
yum install python-pip python-devel
apt-get install python-setuptools
apt-get install python python-dev
easy_install pip
安装Nginx:
yum install nginx
apt-get install nginx
安装uwsgi、virtualenv
pip install virtualenv
pip install uwsgi
yum install uwsgi-plugin-python
apt-get install uwsgi-plugin-python
设置APP目录和Virtualenv:
首先,建立一个应用目录,以存放虚拟环境和WSGI入口点:
mkdir -p /var/www/demoapp
然后进入此目录,为我们的应用设定虚拟环境:
cd /var/www/demoapp/
使用virtualenv命令建立虚拟环境,我们称此环境为venv:
virtualenv venv
一个新的python环境会在名叫venv的文件夹下设定,然后激活此虚拟环境:
. venv/bin/activate
命令行提示符将会改变,现在你将工作在在虚拟环境中(如上图)。
如果向退出此环境,可以随时使用以下命令
deactivate
在此环境激活状态下,任何的python包会按层次存放在此虚拟环境目录中。他们不会影响系统的python环境,我们现在开始将uWSGI服务安装到我们的环境中。虽然这里叫uwsgi,但还是指uWSGI服务器,而非uwsgi协议。
pip install uwsgi
查看uwsgi可用状态:返回版本号,表示uWSGI服务器可用。
uwsgi --version
还可以同时安装一些需要用到的python依赖包,按自己所需安装:
pip install flask
pip install python-ldap
pip install pycrypto
pip install MySQL-python
建立一个WSGI应用:
我们将使用WSGI规范要求建立一个简单WSGI应用,应用组件必须提供以下特性:
1.它必须提供一个可调用接口(可调用函数)
2.可调用函数(uWSGI)必须将环境变量字典作为参数(类似键值对),并且可以在服务器上可访问。
3.应用程序可调用函数应该返回一个可迭代器,以返回结果给客户端。
4.应用程序应该可以访问web服务器(uWSGI)的可调用接口,并辅上HTTP状态恶化请求头部。
我们的第一个应用程序称作hello.py
vi /var/www/demoapp/hello.py
文件里我们建立一个最简单的WSGI兼容应用程序,与所有python代码一样,请注意缩进:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return ["<h1 style='color:blue'>Hello There!</h1>"]
测试代码,我们运行uWSGI,暂时使用HTTP,并在8080端口监听。并将请求传递给脚本hello
uwsgi --socket 0.0.0.0:8080 --protocol=http -w hello
解释:上面的代码构成一个完整的WSGI应用程序。默认情况,uWSGI将查找可调用应用程序,我们称之“函数应用程序”,它包含两个参数。
第一个参数environ是一个类似环境变量的键值对。
第二个参数是start_response,它是应用内部使用以引用uWSGI上“可调用接口”的名字。
这两个参数名字任意选择,他们用于PEP333规范中定的WSGI交互。
我们的应用程序利用这些信息,并做两件事:
1.它必须调用“可调用接口”,它收到了HTTP状态码以及它想要发回的任何头部。这里,我们发送一个“200 OK”响应,并将Content-Type头部设置成text/html
2.它需要返回一个迭代作为响应主体。我们刚刚使用了一个包含单个HTML字符串的列表。字符串是可迭代的,但在列表中,uWSGI将能通过一次迭代来处理整个字符串。
在整个场景中,该文件可能被用作链接,以连接到你代码的剩余部分。比如。Django项目包含一个wsgi.py文件,该文件将请求从Web服务器(uWSGI)传递到应用(Django)。最简单的WSGI接口始终保持不变,无论真实的应用代码是多么复杂。这就是该接口的优势所在。
现在你可以用CTRL-C结束服务。
也可以停止激活虚拟环境:
deactivate
设定uWSGI配置文件:
在前面例子中我们手动运行了uWSGI服务器,并通过命令行传递给它一些参数,我们可以通过建立配置文件避免这些。uWSGI服务器可以读取许多格式的配置文件,简单期间,我们用.ini格式。
我们叫他demoapp.ini并且放在应用文件夹中:
注意:下文中user都需要改为你的本地用户名!
[uwsgi]
module = hello:application
master = true
processes = 5
uid = user
socket = /run/uwsgi/demoapp.sock
chown-socket = user:nginx
chmod-socket = 660
vacuum = true
die-on-term = true
1.在文件中,我们建立一个名为[uwsgi]的块。这个块中包含我们所有的配置条目。uWSGI服务器需要指导应用程序的“可调用接口”的位置,我们通过module = wsgi:application定义了文件和函数。
2.标记初始的uwsgi进程为master进程,并产出许多工作进程,我们以5个工作进程开始。
3.我们要改变uWSGI与外界沟通的协议。前面我们测试中指定—protocol=http,以便我们在web浏览器中观察。但我们已经安装过Nginx,实现了uwsgi代理机制,它是一个快速二进制协议,uWSGI可以用它来与其他服务通信。uwsgi协议是uWSGI的默认协议,所以省略协议规范,protocol=,WEB服务器将退回使用uwsgi。
由于我们正在设计这个用于Nginx的配置,所以我们也从以前的使用网络端口8080改变成用Unix套接字,以提升速度和安全性。
我们将指定自己的用户名来运行uwsgi服务器,并使其拥有套接字文件,我们将在/run下创建一个目录存放套接字文件,这样uWSGI和Nginx都可以访问它。
我们将调用套接字本身demoapp.sock。我们将权限改为644,以便Nginx可以写入它(我们将使用Nginx会使用的nginx组启动uWSGI我们还添加vacuum选项,它会在进程停止时删除套接字)。
CentOS中nginx安装会产生nginx用户:
Ubuntu中安装会产生www-data用户:
比如我的ubuntu用户名为ubuntu,以下为ubuntu服务器下真实的配置脚本:
[uwsgi]
module = hello:application
master = true
processes = 5
uid = ubuntu
socket = /run/uwsgi/demoapp.sock
chown-socket = ubuntu:www-data
chmod-socket = 660
vacuum = true
die-on-term = true
4.我们需要最后一个选项,因为我们将创建一个systemd文件,以自启动应用程序。Systemd和uWSGI对SIGTERM信号应该对应用做什么有完全不通的释义。为了去除这种差异,以便Systemd可以按预期处理进程,我们添加一个die-on-term选项,uWSGI就会终止进程而非重新加载它。
5.保存和关闭文件,该文件设定与一个upstart脚本一起使用。
设置一个Systemd单元文件以管理APP
我们可以在服务器启动时就运行uWSGI,以使我们的应用一直可用。我们将此服务文件存放在/etc/systemd/system目录下,这是存放用户创建服务文件最优的存放点。
vi /etc/systemd/system/uwsgi.service
注意:下文中user都需要改为你的本地用户名!
[Unit]
Description=uWSGI instance to serve demoapp
[Service]
ExecStartPre=-/usr/bin/bash -c 'mkdir -p /run/uwsgi; chown user:nginx /run/uwsgi'
ExecStart=/usr/bin/bash -c 'cd /var/www/demoapp; source venv/bin/activate; uwsgi –ini demoapp.ini'
[Install]
WantedBy=multi-user.target
1.我们以Unit块开头,在此块中我们只需要放入元数据,比如我们的服务名称。
2.Service块,因为我们要使用虚拟环境,我们的服务启动脚本会更加复杂。我们使用ExecStartPre命令保证socket目录被创建,并被正确的用户组所拥有。在=号后的-号表示允许命令执行失败(当目录已被创建时)。这行命令会被以单个请求形式传给bash。
真实的ExecStart命令会启动uWSGI。我们将传递真实的命令到bash。它允许我们通过一行命令执行一些不同的命令。我们用它来进入我们的应用目录,激活虚拟环境,并运行以我们的ini文件配置启动uWSGI。
3.Install模块将决定我们启动这个服务后接下来发生什么。它定义了在什么状态下这个服务应该自动启动。我们定义它何时启动,multi-user.target表示当服务器在多用户模式时启动此服务。
以下为我在ubuntu服务器上的真实配置:
[Unit]
Description=uWSGI instance to serve demoapp
[Service]
ExecStartPre=-/bin/bash -c 'mkdir -p /run/uwsgi; chown ubuntu:www-data /run/uwsgi'
ExecStart=/bin/bash -c 'cd /var/www/demoapp; source venv/bin/activate; uwsgi --ini demoapp.ini'
[Install]
WantedBy=multi-user.target
启动服务:
sudo systemctl start uwsgi
检测服务运行状态:
systemctl status uwsgi
如果没有错误,将服务设置为自启动:
sudo systemctl enable uwsgi
可以通过以下命令停止服务器:
sudo systemctl stop uwsgi
配置Nginx以代理uWSGI
我们已经拥有的WSGI应用,并验证uWSGI可以读取并服务于它。我们配置了uWSGI的ini配置文件和Systemd服务文件。现在我们的uWSGI进程会监听socket并使用uwsgi协议通信。
我们现在将设置Nginx作为反响代理,Nginx拥有使用uwsgi协议与uWSGI通信的能力。这会比HTTP更加快速。
我们需要修改已有的nginx.conf文件,加入新的server块:
sudo vi /etc/nginx/nginx.conf
server {
listen 80;
server_name localhost;
location / {
include uwsgi_params;
uwsgi_pass unix:/run/uwsgi/demoapp.sock;
}
}
在ubuntu下,新server块在/etc/nginx/conf.d下新建一个配置块demoapp_nginx.conf,内容同上。
vi /etc/nginx/conf.d/demoapp_nginx.conf
我们建立的新块将承载uWSGI代理的配置信息。Server块监听80端口,并响应服务器的DNS名或ip地址。
在location块中,处理所有请求。在这个块中,我们包含一个uwsgi参数,包含在/etc/nginx/uwsgi_params中。我们将流量发送到uWSGI正在监听的socket中。
实际上,我们只需要一个简单的应用。当然也可以进行一些改进来完善应用。例如我们可以在此块之外定义些上游uWSGI服务器,然后将他们传递给它。我们可能包含更多的uWSGI参数,,也可以直接处理来自Nginx的静态文件,并只将董婷请求传给uWSGI实例。
你可以通过以下命令验证nginx配置是有效的:
sudo nginx -t
然后启动nginx服务:
sudo systemctl start nginx
查看服务运行状态:
sudo systemctl status nginx
设置nginx服务为自启动:
sudo systemctl enable nginx
在浏览器中测试:
问题集:
问题1:访问不了80端口,8080端口,但netstat -ntlp显示状态为监听
解决:查看防火墙配置,并加入允许80,8080端口的配置:
iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT
iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT
加入后:
保存iptables配置:
/sbin/iptables-save > /etc/iptables-script
将还原配置脚本加入自启动脚本:
echo "/sbin/iptables-restore /etc/iptables-script" >> /etc/rc.local
将自启动脚本设置为可运行标记:
chmod +x /etc/rc.d/rc.local
重启测试。
问题2:缺少lber.h头的解决方法
sudo apt-get install libsasl2-dev python-dev libldap2-dev libssl-dev
sudo apt-get install -y python-dev libldap2-dev libsasl2-dev libssl-dev
sudo yum install python-devel
sudo yum install openldap-devel
sudo yum install uwsgi-plugin-python
问题3:权限问题
描述:connect() to unix:/run/uwsgi/*.sock failed (13: Permission denied) while connecting to upstream
测试:
uwsgi --ini demoapp.ini
chown出错,造成nginx无法访问sock文件。
解决办法:临时关闭selinux模式。
sudo setenforce Permissive
问题4:
无法启动uwsgi服务:
sudo systemctl --no-pager -l status uwsgi
手动测试uwsgi.service文件中的命令,可以发现ubuntu中的bash目录为/bin/bash
/bin/bash -c 'mkdir -p /run/uwsgi; chown ubuntu:www-data /run/uwsgi'
/bin/bash -c 'cd /var/www/demoapp; source venv/bin/activate; uwsgi --ini demoapp.ini'
错误问题1:
ImportError: No module named site
解决办法:
重新安装venv,flask
错误2:
emperor-i-am-ready-to-accept/write(): Bad file descriptor [core/uwsgi.c line 3527]
解决:
修改/var/www/demoapp/demoapp_uwsgi.ini:
#permissions for the socket file
chmod-socket = 644
错误3:
ImportError: No module named xxxx
解决:
pip install python-ldap
pip install pycrypto
pip install MySQL-python
错误4:
*** Operational MODE: single process ***
*** no app loaded. going in full dynamic mode ***
*** uWSGI is running in multiple interpreter mode ***
解决:
ini中加入:
plugin = python
然后yum install uwsgi-plugin-python