NGINX专题-(1)配置uWSGI和Nginx服务Python应用

引用1:https://www.digitalocean.com/community/tutorials/how-to-set-up-uwsgi-and-nginx-to-serve-python-apps-on-centos-7

引用2:http://www.oschina.net/translate/serving-flask-with-nginx-on-ubuntu?cmp
引用3:http://jayveehe.github.io/2015/01/26/nginx-flask/

引用4:https://www.digitalocean.com/community/tutorials/how-to-configure-nginx-as-a-web-server-and-reverse-proxy-for-apache-on-one-ubuntu-14-04-droplet

相关概念:

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

image

命令行提示符将会改变,现在你将工作在在虚拟环境中(如上图)。

如果向退出此环境,可以随时使用以下命令

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用户:

image

Ubuntu中安装会产生www-data用户:

image

比如我的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

image

配置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

image

然后启动nginx服务:

sudo systemctl start nginx

查看服务运行状态:

sudo systemctl status nginx

image

设置nginx服务为自启动:

sudo systemctl enable nginx

在浏览器中测试:

image

问题集:

问题1:访问不了80端口,8080端口,但netstat -ntlp显示状态为监听

image

解决:查看防火墙配置,并加入允许80,8080端口的配置:

image

iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT
iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT

加入后:

image

保存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头的解决方法

image

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

image

image

image

chown出错,造成nginx无法访问sock文件。

解决办法:临时关闭selinux模式。

sudo setenforce Permissive

问题4:

无法启动uwsgi服务:

sudo systemctl --no-pager -l status uwsgi

image

手动测试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'

image

错误问题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

原文地址:https://www.cnblogs.com/shenerguang/p/8628434.html