使用nginx+lua(openresty)实现waf功能

一、了解WAF

1.1 什么是WAF

Web应用防护系统(也称:网站应用级入侵防御系统 。英文:Web Application Firewall,简称: WAF)。利用国际上公认的一种说法:Web应用 防火墙 是通过执行一系列针对HTTP/HTTPS的 安全策略 来专门为Web应用提供保护的一款产品。

1.2 WAF的功能

  • 支持IP白名单和黑名单功能,直接将黑名单的IP访问拒绝。
  • 支持URL白名单,将不需要过滤的URL进行定义。
  • 支持User-Agent的过滤,匹配自定义规则中的条目,然后进行处理(返回403)。
  • 支持CC攻击防护,单个URL指定时间的访问次数,超过设定值,直接返回403。
  • 支持Cookie过滤,匹配自定义规则中的条目,然后进行处理(返回403)。
  • 支持URL过滤,匹配自定义规则中的条目,如果用户请求的URL包含这些,返回403。
  • 支持URL参数过滤,原理同上。
  • 支持日志记录,将所有拒绝的操作,记录到日志中去

1.3 WAF的特点

  • 异常检测协议
    Web应用防火墙会对HTTP的请求进行异常检测,拒绝不符合HTTP标准的请求。并且,它也可以只允许HTTP协议的部分选项通过,从而减少攻击的影响范围。甚至,一些Web应用防火墙还可以严格限定HTTP协议中那些过于松散或未被完全制定的选项。

未分类

  • 增强的输入验证
    增强输入验证,可以有效防止网页篡改、信息泄露、木马植入等恶意网络入侵行为。从而减小Web服务器被攻击的可能性。
  • 及时补丁
    修补Web安全漏洞,是Web应用开发者最头痛的问题,没人会知道下一秒有什么样的漏洞出现,会为Web应用带来什么样的危害。WAF可以为我们做这项工作了——只要有全面的漏洞信息WAF能在不到一个小时的时间内屏蔽掉这个漏洞。当然,这种屏蔽掉漏洞的方式不是非常完美的,并且没有安装对应的补丁本身就是一种安全威胁,但我们在没有选择的情况下,任何保护措施都比没有保护措施更好。
  • 基于规则的保护和基于异常的保护
    基于规则的保护可以提供各种Web应用的安全规则,WAF生产商会维护这个规则库,并时时为其更新。用户可以按照这些规则对应用进行全方面检测。还有的产品可以基于合法应用数据建立模型,并以此为依据判断应用数据的异常。但这需要对用户企业的应用具有十分透彻的了解才可能做到,可现实中这是十分困难的一件事情。
  • 状态管理
    WAF能够判断用户是否是第一次访问并且将请求重定向到默认登录页面并且记录事件。通过检测用户的整个操作行为我们可以更容易识别攻击。状态管理模式还能检测出异常事件(比如登陆失败),并且在达到极限值时进行处理。这对暴力攻击的识别和响应是十分有利的。
  • 其他防护技术
    WAF还有一些安全增强的功能,可以用来解决WEB程序员过分信任输入数据带来的问题。比如:隐藏表单域保护、抗入侵规避技术、响应监视和信息泄露保护。

1.3 WAF与网络防火墙的区别

网络防火墙作为访问控制设备,主要工作在OSI模型三、四层,基于IP报文进行检测。只是对端口做限制,对TCP协议做封堵。其产品设计无需理解HTTP会话,也就决定了无法理解Web应用程序语言如HTML、SQL语言。因此,它不可能对HTTP通讯进行输入验证或攻击规则分析。针对Web网站的恶意攻击绝大部分都将封装为HTTP请求,从80或443端口顺利通过防火墙检测。
  
一些定位比较综合、提供丰富功能的防火墙,也具备一定程度的应用层防御能力,如能根据TCP会话异常性及攻击特征阻止网络层的攻击,通过IP分拆和组合也能判断是否有攻击隐藏在多个数据包中,但从根本上说他仍然无法理解HTTP会话,难以应对如SQL注入、跨站脚本、cookie窃取、网页篡改等应用层攻击。
  
web应用防火墙能在应用层理解分析HTTP会话,因此能有效的防止各类应用层攻击,同时他向下兼容,具备网络防火墙的功能。

二、使用nginx配置简单实现403和404

2.1 小试身手之rerurn 403

修改nginx配置文件在server中加入以下内容

   set $block_user_agent 0;
if ( $http_user_agent ~ "Wget|AgentBench"){
   set $block_user_agent 1;
}
if ($block_user_agent = 1) {
   return 403 ;
}

通过其他机器去wget,结果如下

未分类

2.2 小试身手之rerurn 404

在nginx配置文件中加入如下内容,让访问sql|bak|zip|tgz|tar.gz的请求返回404

location ~* ".(sql|bak|zip|tgz|tar.gz)$"{
      return 404
    }

在网站根目录下放一个tar.gz

[root@iZ28t900vpcZ www]# tar zcvf abc.tar.gz wp-content/

通过浏览器访问结果如下,404已生效

未分类

三、深入实现WAF

3.1 WAF实现规划

分析步骤如下:解析HTTP请求==》匹配规则==》防御动作==》记录日志

具体实现如下:

  • 解析http请求:协议解析模块
  • 匹配规则:规则检测模块,匹配规则库
  • 防御动作:return 403 或者跳转到自定义界面
  • 日志记录:记录到elk中,画出饼图,建议使用json格式

未分类

3.2 安装nginx+lua

由于nginx配置文件书写不方便,并且实现白名单功能很复杂,nginx的白名单也不适用于CC攻击,所以在这里使用nginx+lua来实现WAF,如果想使用lua,须在编译nginx的时候配置上lua,或者结合OpenResty使用,此方法不需要编译nginx时候指定lua

3.2.1 编译nginx的时候加载

环境准备:Nginx安装必备的Nginx和PCRE软件包。

[root@nginx-lua ~]# cd /usr/local/src 
[root@nginx-lua src]# wget http://nginx.org/download/nginx-1.9.4.tar.gz
[root@nginx-lua src]# wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.37.tar.gz

其次,下载当前最新的luajit和ngx_devel_kit (NDK),以及春哥编写的lua-nginx-module

[root@nginx-lua src]# wget http://luajit.org/download/LuaJIT-2.0..tar.gz
[root@nginx-lua src]# wget https://github.com/simpl/ngx_devel_kit/archive/v0.2.19.tar.gz
[root@nginx-lua src]# wget https://github.com/openresty/lua-nginx-module/archive/v0.9.16.tar.gz

最后,创建Nginx运行的普通用户

[root@nginx-lua src]# useradd -s /sbin/nologin -M www

解压NDK和lua-nginx-module

[root@openstack-compute-node5 src]# tar zxvf v0.2.19.tar.gz
[root@openstack-compute-node5 src]# tar zxvf v0.9.16.tar.gz

安装LuaJIT Luajit是Lua即时编译器

[root@openstack-compute-node5 src]# tar zxvf LuaJIT-2.0.3.tar.gz 
[root@openstack-compute-node5 src]# cd LuaJIT-2.0.3
[root@openstack-compute-node5 LuaJIT-2.0.3]# make && make install

安装Nginx并加载模块

[root@openstack-compute-node5 src]# tar zxvf nginx-1.9.4.tar.gz 
[root@openstack-compute-node5 src]# cd nginx-1.9.4
[root@openstack-compute-node5 nginx-1.9.4]# export LUAJIT_LIB=/usr/local/lib
[root@openstack-compute-node5 nginx-1.9.4]# export LUAJIT_INC=/usr/local/include/luajit-2.0
[root@openstack-compute-node5 nginx-1.9.4]# ./configure --prefix=/usr/local/nginx --user=www --group=www     --with-http_ssl_module --with-http_stub_status_module --with-file-aio --with-http_dav_module --add-module=../ngx_devel_kit-0.2.19/ --add-module=../lua-nginx-module-0.9.16/ --with-pcre=/usr/local/src/pcre-8.37 
[root@openstack-compute-node5 nginx-1.5.12]# make -j2 && make install
[root@openstack-compute-node5 ~]# ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2   #一定创建此软连接,否则报错

安装完毕后,下面可以测试安装了,修改nginx.conf 增加第一个配置

location /hello {
                default_type 'text/plain';
                content_by_lua 'ngx.say("hello,lua")';
        }
[root@openstack-compute-node5 ~]# /usr/local/nginx-1.9.4/sbin/nginx –t
[root@openstack-compute-node5 ~]# /usr/local/nginx-1.9.4/sbin/nginx

效果如下

未分类

3.2.3 Openresty部署

安装依赖包

[root@iZ28t900vpcZ ~]#yum install -y readline-devel pcre-devel openssl-devel

下载并编译安装openresty

[root@iZ28t900vpcZ ~]#cd /usr/local/src
[root@iZ28t900vpcZ src]#wget https://openresty.org/download/ngx_openresty-1.9.3.2.tar.gz
[root@iZ28t900vpcZ src]#tar zxf ngx_openresty-1.9.3.2.tar.gz
[root@iZ28t900vpcZ src]#cd ngx_openresty-1.9.3.2
[root@iZ28t900vpcZ ngx_openresty-1.9.3.2]# ./configure --prefix=/usr/local/openresty-1.9.3.2 --with-luajit --with-http_stub_status_module --with-pcre --with-pcre-jit
[root@iZ28t900vpcZ ngx_openresty-1.9.3.2]#gmake && gmake install
ln -s /usr/local/openresty-1.9.3.2/ /usr/local/openresty

测试openresty安装

[root@iZ28t900vpcZ ~]#vim /usr/local/openresty/nginx/conf/nginx.conf
server {
    location /hello {
            default_type text/html;
            content_by_lua_block {
                ngx.say("HelloWorld")
            }
        }
}

测试并启动nginx

[root@iZ28t900vpcZ ~]#/usr/local/openresty/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/openresty-1.9.3.2/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/openresty-1.9.3.2/nginx/conf/nginx.conf test is successful
[root@iZ28t900vpcZ ~]#/usr/local/openresty/nginx/sbin/nginx

3.2.4 WAF部署

在github上克隆下代码

[root@iZ28t900vpcZ ~]#git clone https://github.com/unixhot/waf.git
[root@iZ28t900vpcZ ~]#cp -a ./waf/waf /usr/local/openresty/nginx/conf/

修改Nginx的配置文件,加入(http字段)以下配置。注意路径,同时WAF日志默认存放在/tmp/日期_waf.log

#WAF
    lua_shared_dict limit 50m;  #防cc使用字典,大小50M
    lua_package_path "/usr/local/openresty/nginx/conf/waf/?.lua";
    init_by_lua_file "/usr/local/openresty/nginx/conf/waf/init.lua";
    access_by_lua_file "/usr/local/openresty/nginx/conf/waf/access.lua";
[root@openstack-compute-node5 ~]# /usr/local/openresty/nginx/sbin/nginx –t
[root@openstack-compute-node5 ~]# /usr/local/openresty/nginx/sbin/nginx

根据日志记录位置,创建日志目录

[root@iZ28t900vpcZ ~]#mkdir /tmp/waf_logs
[root@iZ28t900vpcZ ~]#chown nginx.nginx /tmp/waf_logs

3.3 学习模块

3.3.1 学习配置模块

WAF上生产之前,建议不要直接上生产,而是先记录日志,不做任何动作。确定WAF不产生误杀

config.lua即WAF功能详解

[root@iZ28t900vpcZ waf]# pwd
/usr/local/nginx/conf/waf
[root@iZ28t900vpcZ waf]# cat config.lua
--WAF config file,enable = "on",disable = "off" 
--waf status    
config_waf_enable = "on"   #是否开启配置
--log dir 
config_log_dir = "/tmp/waf_logs"    #日志记录地址
--rule setting 
config_rule_dir = "/usr/local/nginx/conf/waf/rule-config"                                                                  
                         #匹配规则缩放地址
--enable/disable white url 
config_white_url_check = "on"  #是否开启url检测
--enable/disable white ip 
config_white_ip_check = "on"   #是否开启IP白名单检测
--enable/disable block ip 
config_black_ip_check = "on"   #是否开启ip黑名单检测
--enable/disable url filtering 
config_url_check = "on"      #是否开启url过滤
--enalbe/disable url args filtering 
config_url_args_check = "on"   #是否开启参数检测
--enable/disable user agent filtering 
config_user_agent_check = "on"  #是否开启ua检测
--enable/disable cookie deny filtering 
config_cookie_check = "on"    #是否开启cookie检测
--enable/disable cc filtering 
config_cc_check = "on"   #是否开启防cc攻击
--cc rate the xxx of xxx seconds 
config_cc_rate = "10/60"   #允许一个ip60秒内只能访问10此
--enable/disable post filtering 
config_post_check = "on"   #是否开启post检测
--config waf output redirect/html 
config_waf_output = "html"  #action一个html页面,也可以选择跳转
--if config_waf_output ,setting url 
config_waf_redirect_url = "http://www.baidu.com" 
config_output_html=[[  #下面是html的内容
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<meta http-equiv="Content-Language" content="zh-cn" /> 
<title>网站防火墙</title> 
</head> 
<body> 
<h1 align="center"> # 您的行为已违反本网站相关规定,注意操作规范。
</body> 
</html> 
]] 

3.4 学习access.lua的配置

[root@iZ28t900vpcZ waf]# pwd
/usr/local/openresty/nginx/conf/waf
[root@iZ28t900vpcZ waf]# cat access.lua 
require 'init'
function waf_main()
    if white_ip_check() then
    elseif black_ip_check() then
    elseif user_agent_attack_check() then
    elseif cc_attack_check() then
    elseif cookie_attack_check() then
    elseif white_url_check() then
    elseif url_attack_check() then
    elseif url_args_attack_check() then
    --elseif post_attack_check() then
    else
        return  
    end
end
waf_main()

书写书序:先检查白名单,通过即不检测;再检查黑名单,不通过即拒绝,检查UA,UA不通过即拒绝;检查cookie;URL检查;URL参数检查,post检查;

3.5 启用WAF并测试

3.5.1模拟sql注入即url攻击

显示效果如下

未分类

日志显示如下,记录了UA,匹配规则,URL,客户端类型,攻击的类型,请求的数据

未分类

3.5.2 使用ab压测工具模拟防cc攻击

[root@linux-node3 ~]# ab -c 100 -n 100 http://www.chuck-blog.com/index.php
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking www.chuck-blog.com (be patient).....done
Server Software:        openresty
Server Hostname:        www.chuck-blog.com
Server Port:            80
Document Path:          /index.php
Document Length:        0 bytes
Concurrency Level:      100
Time taken for tests:   0.754 seconds
Complete requests:      10
Failed requests:        90 #config.lua中设置的,60秒内只允许10个请求
Write errors:           0
Non-2xx responses:      90
Total transferred:      22700 bytes
HTML transferred:       0 bytes
Requests per second:    132.65 [#/sec] (mean)
Time per request:       753.874 [ms] (mean)
Time per request:       7.539 [ms] (mean, across all concurrent requests)
Transfer rate:          29.41 [Kbytes/sec] received
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       23   69  20.2     64     105
Processing:    32  180 144.5    157     629
Waiting:       22  179 144.5    156     629
Total:         56  249 152.4    220     702
Percentage of the requests served within a certain time (ms)
  50%    220
  66%    270
  75%    275
  80%    329
  90%    334
  95%    694
  98%    701
  99%    702
  100%    702 (longest request)

3.5.3 模拟ip黑名单
将请求ip放入ip黑名单中

[root@iZ28t900vpcZ rule-config]# echo “1.202.193.133” >>/usr/local/openresty/nginx/conf/waf/rule-config/blackip.rule

3.5.4 模拟ip白名单
将请求ip放入ip白名单中,此时将不对此ip进行任何防护措施,所以sql注入时应该返回404

[root@iZ28t900vpcZ rule-config]# echo “1.202.193.133” >>/usr/local/openresty/nginx/conf/waf/rule-config/whiteip.rule

3.5.5 模拟URL参数检测
浏览器输入www.chuck-blog.com/?a=select * from table
详细规定在arg.rule中有规定,对请求进行了规范

[root@iZ28t900vpcZ rule-config]# /usr/local/openresty/nginx/conf/waf/rule-config/cat args.rule 
../
:$
${
select.+(from|limit)
(?:(union(.*?)select))
having|rongjitest
sleep((s*)(d*)(s*))
benchmark((.*),(.*))
base64_decode(
(?:fromW+information_schemaW)
(?:(?:current_)user|database|schema|connection_id)s*(
(?:etc/W*passwd)
into(s+)+(?:dump|out)files*
groups+by.+(
xwork.MethodAccessor
    (?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)(
xwork.MethodAccessor
    (gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data):/
java.lang
    $_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)[
    <(iframe|script|body|img|layer|div|meta|style|base|object|input)
(onmouseover|onerror|onload)=
[root@iZ28t900vpcZ rule-config]# pwd
/usr/local/openresty/nginx/conf/waf/rule-config

四、防cc攻击利器之httpgrard

4.1 httpgrard介绍

HttpGuard是基于openresty,以lua脚本语言开发的防cc攻击软件。而openresty是集成了高性能web服务器Nginx,以及一系列的Nginx模块,这其中最重要的,也是我们主要用到的nginx lua模块。HttpGuard基于nginx lua开发,继承了nginx高并发,高性能的特点,可以以非常小的性能损耗来防范大规模的cc攻击。

4.2 httpgrard防cc特效

  • 限制访客在一定时间内的请求次数
  • 向访客发送302转向响应头来识别恶意用户,并阻止其再次访问
  • 向访客发送带有跳转功能的js代码来识别恶意用户,并阻止其再次访问
  • 向访客发送cookie来识别恶意用户,并阻止其再次访问
  • 支持向访客发送带有验证码的页面,来进一步识别,以免误伤
  • 支持直接断开恶意访客的连接
  • 支持结合iptables来阻止恶意访客再次连接
  • 支持白名单功能
  • 支持根据统计特定端口的连接数来自动开启或关闭防cc模式
  • 详见github地址,在后续的博文中会加入此功能

五、WAF上线

  • 初期上线只记录日志,不开启WAF,防止误杀
  • WAF规则管理使用saltstack工具
  • 要知道并不是有了WAF就安全,存在人为因素

CentOS7系统安装uWSGI Nginx运行Django应用

1. 基础环境配置

先安装好基础环境

cd ~
sudo yum groupinstall "Development tools"
sudo yum install python34 python34-devel python34-pip
pip3 install virtualenv
virtualenv python34
source python34/bin/activate

测试python环境

python --version
pip --version

如果是python3.4的环境,至此python环境配置好了,如果有错,多用一下Google解决。

2. Nginx安装

参考Nginx官网的安装文档,建议新手使用yum安装,后续升级维护方便。

  • 首先要安装Nginx仓库,sudo vi /etc/yum.repos.d/nginx.repo添加以下内容:
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=1
enabled=1
  • 下载Nginx的key文件,wget https://nginx.org/keys/nginx_signing.key
  • 安装key文件,sudo rpm –import nginx_signing.key
  • 安装Nginx,sudo yum install nginx
  • 测试Nginx,sudo nginx

3. uWSGI安装

从此往下都需要在Python34的虚拟环境下,检查自己是否在Python34虚拟环境下,不在的话执行source python34/bin/activate确认进入虚拟环境,执行pip install uwsgi安装uWSGI,测试uWSGI是否安装好。执行vi test.py输入一下内容保存:

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"]

开启uWSGI服务,uwsgi –http :8000 –wsgi-file test.py,打开浏览器,输入(你的IP):8000看到Hello World代表web client到uWSGI到Python的连接正常。

4. Django配置

先执行pip install -r requirements.txt安装好Python包,正式部署前要配置好Django项目的配置(settings.py),修改一下项目:

DEBUG = False
ALLOWED_HOSTS = ['127.0.0.1','localhost','(你的域名)']
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

然后记得执行一下语句:

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser # 创建超级用户
python manage.py collectstatic

然后执行python manage.py runserver测试Django项目运行是否正常。

5. uWSGI到Django配置

uwsgi --http :8000 --module (你的Djnago项目名).wsgi

打开浏览器测试(8000端口),如果Django项目运行正常,表示Web Client到uWSGI到Django运行正常。

vi uwsgi.ini添加以下内容并保存

[uwsgi]
socket = :8001
chdir= (你的Django项目地址)
wsgi-file = (你的Django项目地址下wsgi.py所在位置)
touch-reload = ~/reload # 如果reload文件有更改,重启uWSGI
module=(你的Django项目名称).wsgi
master=true
processes = 2 # 进程数
threads = 4 # 线程数
chmod-socket    = 664
chown-socket = (centos用户名):(centos组)
vacuum=true
pidfile = ~/tmp/uwsgi.pid #pid 文件位置

执行uWSGI –ini uwsgi.ini &运行uWSGI,如果Django项目有更改的话可以执行touch ~/reload重启uWSGI。

6. 配置Nginx

执行 sudo vi /etc/nginx/nginx.conf修改添加一下内容

# 注意,这只是nginx.conf中的一部分,其他内容不熟悉的话不要修改。

http {
    upstream django {
        server 127.0.0.1:8001;
    }

    server {
        listen       80;
        server_name  (你的域名地址);
        if ($host != 'www.willtunner.me' ) {
            rewrite ^/(.*)$ http://www.willtunner.me/$1 permanent;
        }   # 此处修改自动添加www

        charset utf-8;
        access_log  logs/host.access.log  main;

        location / {
            uwsgi_pass  django;
            include     /etc/nginx/uwsgi_params;
        }

        location /static/ {
            alias (你的Django项目static地址);
        }

        location /media/ {
            alias (你的Django项目media地址);
        }

        location ~ .*.txt$ {
            root (robots.txt所在地址);
        }
    }
}

当然Nginx虚拟主机配置最好使用include语法放置在其他文件夹下,多个虚拟主机比较好管理,这里我只有一个虚拟主机,故直接在这里修改了。

改好了之后执行sudo nginx -s stop和sudo nginx重新加载Nginx的配置。

没有出错的话,至此从Web Client到Nginx到uWSGi到Django的通路便建立好了。网站可以正常访问了。

Flask服务部署(Nginx Gunicorn Gevent)

Flask 项目部署

我们开发好了一个flask项目, 需要部署到线上服务器, 那我们的线上服务应该如何部署呢

基本的架构如下

未分类

Nginx

在开发环境, 我们一般直接运行Python服务, 启动了某个端口(一般是5000端口), 然后通过该端口进行开发调试

但线上环境一般不会直接这样提供服务, 一般的线上服务需要通过 Nginx 将外部请求转发到Python服务

这样有什么好处

  • 隐藏python服务, 避免直接将python服务暴露出去
  • 提高web服务的连接处理能力(Nginx)
  • 作为反向代理, 提升python整体服务处理能力

我们可以配置的Nginx配置如下

upstream flask_servers {
    server 127.0.0.1:9889;
}

server {
    listen 80;
    server_name dev.simple-cms.com;

    access_log  /data/logs/nginx/simple_cms_access.log main;
    error_log /data/logs/nginx/simple_cms_error.log debug;

    location / {
        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_redirect off;
        proxy_pass  http://flask_servers;
    }

    location ^~ /static {
        root /data/www;
    }
}

如果有多个python服务器, 可在upstream下继续添加其他的python服务列表

Gunicorn + Gevent

Gunicorn

Guicorn 是一个python的一个 WSGI 的web sever, 什么是WSGI

Web Server <=====WSGI=====> Python APP

WSGI 其实是一种网关协议, 用以把http请求参数转换为python服务可以读取的内容, 这样在python的框架里能根据请求参数进行不同的业务处理了

Gunicorn 就是这么一种WSGI的实现, 他也是个web server, 可以直接提供对外的web服务, 但在一般的部署中, 他只是作为python服务的运行的容器, 运行和管理python应用程序

通过这个容器, 从Nginx转发的请求就能转发到某个python app了

除此之外, Gunicorn 还提供了多个运行参数, 常用的如下

-w 设置启动`python app` worker进程的数量
-k 运行模式(sync, gevent等等)
-b gunicorn 启动绑定的host和port
--max-requests 最大处理量, 单woker进程如果处理了超过该数量的请求, 该woker会平滑重启

Gevent

单进程直接运行Python app服务时, 当有两个并发请求过来时, 进程只能先处理一个请求, 等第一个请求处理完成后, 才能处理第二个, 势必影响用户的体验

那么单进程的时候, 如何能提高并发处理能力,

大多数情况下, 我们的服务中, 导致性能低下的原因是I/O, 比如对数据库的读写, 发送Http请求等等, 当进程进行I/O的时候, 是不占用CPU时间的, 这个时候, CPU可以被腾出来处理其他请求

Gevent就是完成这个工作的

幸运的是, 我们不需要再代码中自己实现这部分功能, Gunicorn 实现了Gevent模式的运行方式(-k 参数指定), 允许你的Python APP 更高性能的处理业务

通过 Gunicorn + Gevent, 我们可以如下启动

gunicorn --max-requests 300 --pid /tmp/flask.pid --timeout 3 -k gevent -w 2 -b 127.0.0.1:9889 run:app

从Nginx代理的请求, 就可以转发到Gunicorn启动的9889端口上了

supervisor

上面我们通过Gunicorn+Gevent 启动了Python服务, 在某些情况下, 我们的服务会停掉(系统掉电重启, 误杀, 不知道停了 🙁 ),

我们不可能时刻都看着线上服务, 在这种情况下, 我们希望系统能自动拉起停掉的服务, 保证线上服务的稳定

supervisor 就是干这个的, supervisor会守护配置好的进程, 在进程停止后重新启动服务进程, 并保留日志

比如Nginx服务的配置可以如下

[program:nginx]
command = /usr/local/bin/nginx -g 'daemon off;' -c /usr/local/etc/nginx/nginx.conf
autostart = true
startsecs = 5
autorestart = true
startretries = 3
user = root

其中command 就是我们nginx服务的启动命令,

同理, gunicorn启动的supervisor 配置如下

[program:gunicorn]
directory=/path/to/flask_app
command = /path/to/gunicorn --max-requests 3000 --pid /tmp/flask.pid --timeout 3 -k gevent -w 2 -b 127.0.0.1:9889 run:app autostart = true
startsecs = 5
autorestart = true
startretries = 3
user = www-data

以上配置好好, 执行

sudo supervisorctl -c /path/to/supervisord.ini update

就可以让supervisor 守护我们的gunicorn 服务了

状态查看

sudo supervisorctl -c /path/to/supervisord.ini status

可以看到

gunicorn             RUNNING   pid 35640, uptime 0:00:14

状态是running

总结

以上, 我们实现了线上python服务的基础架构部署, 主要是针对python服务的部署, 基本遵循了文首的架构图

以上是我们在项目中实践的架构部署, 欢迎各位一起交流

nginx apache webbench压力测试比较

简介

Nginx (“engine x”) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器;
作为一款轻量级的Web服务器,具有占有内存少,并发能力强等优势,是高连接并发场景下Apache的不错的替代品;
本篇主要介绍Nginx作为Web服务器时,相对于Apache的性能优势;
下一篇将会介绍Nginx作为方向代理服务器的实现;

重要特点

非阻塞:数据复制时,磁盘I/O的第一阶段是非阻塞的;
事件驱动:通信机制采用epoll模型,支持更大的并发连接;
master/worker结构:一个master进程,生成一个或多个worker进程;

基础架构

未分类
Nginx如何实现高并发:
I/O模型采用异步非阻塞的事件驱动机制,由进程循环处理多个准备好的事件,如epoll机制;
Nginx与Apache对高并发处理上的区别:
对于Apache,每个请求都会独占一个工作线程,当并发量增大时,也会产生大量的工作线程,导致内存占用急剧上升,同时线程的上下文切换也会导致CPU开销增大,导致在高并发场景下性能下降严重;
对于Nginx,一个worker进程只有一个主线程,通过事件驱动机制,实现循环处理多个准备好的事件,从而实现轻量级和高并发;

部署配置

安装

yum -y groupinstall “Development tools”
yum -y groupinstall “Server Platform Development”
yum install gcc openssl-devel pcre-devel zlib-devel
groupadd -r nginx
useradd -r -g nginx -s /sbin/nologin -M nginx
tar xf nginx-1.4.7.tar.gz
cd nginx-1.4.7
mkdir -pv /var/tmp/nginx
./configure 
--prefix=/usr 
--sbin-path=/usr/sbin/nginx 
--conf-path=/etc/nginx/nginx.conf 
--error-log-path=/var/log/nginx/error.log 
--http-log-path=/var/log/nginx/access.log 
--pid-path=/var/run/nginx/nginx.pid 
--lock-path=/var/lock/nginx.lock 
--user=nginx 
--group=nginx 
--with-http_ssl_module 
--with-http_flv_module 
--with-http_stub_status_module 
--with-http_gzip_static_module 
--http-client-body-temp-path=/var/tmp/nginx/client/ 
--http-proxy-temp-path=/var/tmp/nginx/proxy/ 
--http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ 
--http-uwsgi-temp-path=/var/tmp/nginx/uwsgi 
--http-scgi-temp-path=/var/tmp/nginx/scgi 
--with-pcre
make && make install

配置

    vi /etc/init.d/nginx # 配置服务脚本
    #!/bin/sh
    #
    # nginx - this script starts and stops the nginx daemon
    #
    # chkconfig: - 85 15
    # description: Nginx is an HTTP(S) server, HTTP(S) reverse 
    # proxy and IMAP/POP3 proxy server
    # processname: nginx
    # config: /etc/nginx/nginx.conf
    # config: /etc/sysconfig/nginx
    # pidfile: /var/run/nginx.pid
    # Source function library.
    . /etc/rc.d/init.d/functions
    # Source networking configuration.
    . /etc/sysconfig/network
    # Check that networking is up.
    [ "$NETWORKING" = "no" ] && exit 0
    nginx="/usr/sbin/nginx"
    prog=$(basename $nginx)
    NGINX_CONF_FILE="/etc/nginx/nginx.conf"
    [ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
    lockfile=/var/lock/subsys/nginx
    make_dirs() {
    # make required directories
    user=`nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=([^ ]*).*/1/g' -`
    options=`$nginx -V 2>&1 | grep 'configure arguments:'`
    for opt in $options; do
    if [ `echo $opt | grep '.*-temp-path'` ]; then
    value=`echo $opt | cut -d "=" -f 2`
    if [ ! -d "$value" ]; then
    # echo "creating" $value
    mkdir -p $value && chown -R $user $value
    fi
    fi
    done
    }
    start() {
    [ -x $nginx ] || exit 5
    [ -f $NGINX_CONF_FILE ] || exit 6
    make_dirs
    echo -n $"Starting $prog: "
    daemon $nginx -c $NGINX_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
    }
    stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
    }
    restart() {
    configtest || return $?
    stop
    sleep 1
    start
    }
    reload() {
    configtest || return $?
    echo -n $"Reloading $prog: "
    killproc $nginx -HUP
    RETVAL=$?
    echo
    }
    force_reload() {
    restart
    }
    configtest() {
    $nginx -t -c $NGINX_CONF_FILE
    }
    rh_status() {
    status $prog
    }
    rh_status_q() {
    rh_status >/dev/null 2>&1
    }
    case "$1" in
    start)
    rh_status_q && exit 0
    $1
    ;;
    stop)
    rh_status_q || exit 0
    $1
    ;;
    restart|configtest)
    $1
    ;;
    reload)
    rh_status_q || exit 7
    $1
    ;;
    force-reload)
    force_reload
    ;;
    status)
    rh_status
    ;;
    condrestart|try-restart)
    rh_status_q || exit 0
    ;;
    *)
    echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
    exit 2
esac
    chmod  x /etc/init.d/nginx # 复***务脚本执行权限
    vi /etc/nginx/nginx.conf # 编辑主配置文件
    worker_processes 2;
    error_log /var/log/nginx/nginx.error.log;
    pid /var/run/nginx.pid;
    events {
    worker_connections 1024;
    }
    http {
    include mime.types;
    default_type application/octet-stream;
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
    sendfile on;
    keepalive_timeout 65;
    server {
    listen 80;
    server_name xxrenzhe.lnmmp.com;
    access_log /var/log/nginx/nginx.access.log main;
    location / {
    root /www/lnmmp.com;
    index index.php index.html index.htm;
    }
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    root /www/lnmmp.com;
    }
    location ~ .php$ {
    root /www/lnmmp.com;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    }
    }
    }
    vi /etc/nginx/fastcgi_params # 编辑fastcgi参数文件
    fastcgi_param GATEWAY_INTERFACE CGI/1.1;
    fastcgi_param SERVER_SOFTWARE nginx;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_param REQUEST_METHOD $request_method;
    fastcgi_param CONTENT_TYPE $content_type;
    fastcgi_param CONTENT_LENGTH $content_length;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param SCRIPT_NAME $fastcgi_script_name;
    fastcgi_param REQUEST_URI $request_uri;
    fastcgi_param DOCUMENT_URI $document_uri;
    fastcgi_param DOCUMENT_ROOT $document_root;
    fastcgi_param SERVER_PROTOCOL $server_protocol;
    fastcgi_param REMOTE_ADDR $remote_addr;
    fastcgi_param REMOTE_PORT $remote_port;
    fastcgi_param SERVER_ADDR $server_addr;
    fastcgi_param SERVER_PORT $server_port;
    fastcgi_param SERVER_NAME $server_name;

启动服务

service nginx configtest # 服务启动前先验证配置文件是否正确
service nginx start
ps -ef |grep nginx # 检查nginx进程,尤其是worker进程是否与worker_processes值一致
ss -antupl |grep 80 # 检查服务端口是否启动

性能测试
测试说明
每次测试都进行3次,最后数据取平均值;
对比测试中的Apache采用event的MPM机制,最大化提高Apache的并发性能;
每次测试后,都需重新启动服务(httpd或nginx),以防止多次测试数据不准;
测试工具:webbench
优点:比ab能更好的模拟并发请求,最大支持模拟30000并发连接;
测试方法

# 安装webbench
wget http://blog.s135.com/soft/linux/webbench/webbench-1.5.tar.gz
tar xf webbench-1.5.tar.gz
cd webbench-1.5
make && make install
# 测试
webbench -c 100 -t 30 http://172.16.25.112/nginx.html # 测试静态文件访问
webbench -c 20 -t 30 http://172.16.25.112/test_mem.php # 测试动态文件访问

测试数据
未分类
未分类

分析趋势图

静态文件访问趋势图
未分类
动态文件访问趋势图
未分类

总结

综合上面测试得出的趋势图可以看出:
1、静态文件测试时,低并发(200以下)情况下,Nginx和Apach的处理能力相当(2000pages/sec左右),当并发数超过200后,则Apache的处理能力开始下降,而Nginx保持稳定;同时随着并发量的增大,Apache令人诟病的内存占用和负载开始急剧上升,与此同时,Nginx在内存占用和负载方面的略微提升则可以忽略不计了;
2、动态文件测试时,低并发(100以下)情况下,Nginx和Apache的处理能力相当(650pages/sec左右),但Nginx的内存占用和负载峰值只有Apache的50%左右;在高并发情况下(100以上),Apach的动态处理能力开始下滑,当并发达到500时,开始出现失败的请求,说明此时已达到的Apache的处理上限了,而反观Nginx,虽然处理动态请求会消耗更多的内存,但其处理能力随着并发量的上升而上升,即使并发1000动态请求,也未达到其处理能力上限;
3、故不管是在静态文件请求还是动态文件请求方面,Nginx的性能都是强势优于Apache的;虽然可以通过系统调优的方式提高Apache的处理性能,但和Nginx相比,还是不足以打动技术狂热份子的吧,哈哈!

Nginx解决惊群现象

惊群现象:所有的工作进程都在等待一个socket,当socket客户端连接时,所有工作线程都被唤醒,但最终有且仅有一个工作线程去处理该连接,其他进程又要进入睡眠状态。

Nginx通过控制争抢处理socket的进程数量和抢占ngx_accept_mutex锁解决惊群现象。只有一个ngx_accept_mutex锁,谁拿到锁,谁处理该socket的请求。

如果当前进程的连接数>最大连接数*7/8,则该进程不参与本轮竞争。

while(true)
{
    //工作进程抢占锁,抢占成功的进程将ngx_accept_mutex_held变量置为1。拿到锁,意味着socket被放到本进程的epoll中了,如果没有拿到锁,则socket会被从epoll中取出。  
    if(pthread_mutex_trylock(&ngx_accept_mutex))
    {
        ngx_accept_mutex_held = 1;
    }
    else
    {
        //设置time时间,使得没有拿到锁的worker进程,去拿锁的频繁更高
        timer = 500;
        ngx_accept_mutex_held = 0;
    }

     //拿到锁的话,置flag为NGX_POST_EVENTS,这意味着ngx_process_events函数中,任何事件都将延后处理,会把accept事件都放到ngx_posted_accept_events链表中,epollin|epollout事件都放到ngx_posted_events链表中  
    if (ngx_accept_mutex_held) 
    {   
        flags |= NGX_POST_EVENTS;
    }

    //继续epoll_wait等待处理事件
    int num = epoll_wait(epollfd, events, length, timer);
    for(int i=0; i<num; ++i)
    {
        ......
        //如果是读事件
        if (revents & EPOLLIN)
        {
            //有NGX_POST_EVENTS标志的话,就把accept事件放到ngx_posted_accept_events队列中,把正常的事件放到ngx_posted_events队列中延迟处理
            if (flags & NGX_POST_EVENTS)
            {
                queue = rev->accept ? 
                    &ngx_posted_accept_events:&ngx_posted_events;

                ngx_post_event(rev, queue);
            }
            else//处理
            {
                rev->handler(rev);
            }
        }

        //如果是写事件
        if (revents & EPOLLOUT)
        {
            //同理,有NGX_POST_EVENTS标志的话,写事件延迟处理,放到ngx_posted_events队列中 
            if (flags & NGX_POST_EVENTS) 
            {
                ngx_post_event(rev, &ngx_posted_events);
            }
            else//处理
            {
                rev->handler(rev);
            }
        }

      //先处理新用户的连接事件
      ngx_event_process_posted(cycle, &ngx_posted_accept_events);

      //释放处理新连接的锁
      if(ngx_accept_mutex_held)
      {
          pthread_mutex_unlock(&ngx_accept_mutex);
      }

      //再处理已建立连接的用户读写事件
      ngx_event_process_posted(cycle, &ngx_posted_events);
    }

Nginx HTTP2配置教程

前言

从 2015 年 5 月 14 日 HTTP/2 协议正式版的发布到现在已经快有一年了,越来越多的网站部署了 HTTP2,HTTP2 的广泛应用带来了更好的浏览体验,只要是 Modern 浏览器都支持,所以部署 HTTP2 并不会带来太多困扰。

虽然 h2 有 h2c (HTTP/2 Cleartext) 可以通过非加密通道传输,但是支持的浏览器初期还是比较少的,所以目前部署 h2 还是需要走加密的,不过由于 Let’s Encrypt 大力推行免费证书和证书的廉价化,部署 h2 的成本并不高。

介绍

HTTP 2.0即超文本传输协议 2.0,是下一代HTTP协议。是由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis (httpbis)工作小组进行开发。是自1999年http1.1发布后的首个更新。

HTTP/2 协议是从 SPDY 演变而来,SPDY 已经完成了使命并很快就会退出历史舞台(例如 Chrome 将在「2016 年初结束对 SPDY 的支持」;Nginx、Apache 也已经全面支持 HTTP/2 ,并也不再支持 SPDY)。

一般的大家把 HTTP2 简称为 h2,尽管有些朋友可能不怎么愿意,但是这个简称已经默认化了,特别是体现在浏览器对 HTTP2 都是这个简写的。

配置

普通的 HTTPS 网站浏览会比 HTTP 网站稍微慢一些,因为需要处理加密任务,而配置了 h2 的 HTTPS,在低延时的情况下速度会比 HTTP 更快更稳定!

现在电信劫持事件频发,网站部署了 HTTPS 加密后可以杜绝大部分劫持,但不是完全。像电子商务行业对 HTTPS 加密可是标配啊,因此部署 h2 更是势在必行。

证书

这里是 免费和便宜 SSL 证书介绍 ,大家可以从这里购买或者申请免费的 SSL 证书,免得 Chrome 弹出红色的页面令人不悦,从而拒绝了大多数访客。

Web 服务器

说明

默认编译的 Nginx 并不包含 h2 模块,我们需要加入参数来编译,截止发文,Nginx 1.9 开发版及以上版本源码需要自己加入编译参数,从软件源仓库下载的则默认编译。 Tengine 可以同时部署 h2 和 SPDY 保证兼容性,Nginx 则是一刀切不再支持 SPDY。

安装/编译

如果你编译的 Nginx 不支持,那么在 ./configure 中加入:–with-http_v2_module ,如果没有 SSL 支持,还需要加入 –with-http_ssl_module

然后 make && make install 即可。

配置

主要是配置 Nginx 的 server 块, 。
修改相关虚拟机的 .conf 文件,一般在 /usr/local/nginx/conf/vhost/ 或者 /etc/nginx/conf/,具体参考你的环境指导,不懂请回复。

server {
listen 443 ssl http2 default_server;
server_name www.mf8.biz;

ssl_certificate /path/to/public.crt;
ssl_certificate_key /path/to/private.key;

注:将 server_name www.mf8.biz; 中的 www.mf8.biz 替换为你的域名。

然后通过 /usr/local/nginx/sbin/nginx -t 或者 nginx -t 来检测是否配置正确,然后重启 Nginx ,即可。

检验

在 Chrome 浏览器上可以通过,HTTP/2 and SPDY indicator 来检验,如果地址栏出现蓝色的闪电就是 h2

也可以在 chrome://net-internals/#http2 中检查。注意版本要新,姿势要帅!

配置进阶

大家都知道去年的心血漏洞将 SSL 推到了风口浪尖,所以单单支持了 h2 ,我们任然需要对 SSL 做一些安全的优化!

配置赫尔曼密钥

openssl dhparam -out dhparam.pem 2048 // 在 ssh 运行, openssl 生成 2048 位的密钥而不是当作参数写入 nginx.conf 文件。

ssl_dhparam /path/to/dhparam.pem; //在 .conf 中配置

禁止不安全的 SSL 协议,使用安全协议

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

禁止已经不安全的加密算法

ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4';

缓解 BEAST 攻击

ssl_prefer_server_ciphers on;```

**启用 HSTS**

此举直接跳过 301 跳转,还降低了中间人攻击的风险!配置在 .conf 中即可

`add_header Strict-Transport-Security max-age=15768000;`

**301 跳转**

80 端口跳转到 443 端口
```nginx
server {
 listen 80;
 add_header Strict-Transport-Security max-age=15768000;
 return 301 https://www.yourwebsite.com$request_uri;
}

缓存连接凭据

ssl_session_cache shared:SSL:20m;
ssl_session_timeout 60m;

OCSP 缝合

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/cert/trustchain.crt;
resolver 233.5.5.5 233.6.6.6 valid=300s;

欢迎你的补充!

通过 www.ssllabs.com 即可检验 HTTPS 配置的安全性和实用性!

Nginx上传大文件超时出错解决方法

Nginx上传大文件出现出错超时

上传文件大小:1.4G
上传所使用的组件:swfupload
swfupload返回-220错误码,对应的是错误提示是IO_ERROR。
IO_ERROR #当读取和发送文件时出现了一些错误。通常发生在服务器非预期地关闭了终端连接的时候。

解决:

修改Nginx读取超时间与发请求超时间.

nginx配置参数如下:

fastcgi_connect_timeout 300; #连接超时
fastcgi_read_timeout 600;    #读超时
fastcgi_send_timeout 600;    #写超时

#选项注解:

fastcgi_read_timeout #FastCGI进程向Nginx进程发送response,整个过程的超时时间
fastcgi_send_timeout #Nginx进程向FastCGI进程发送request,整个过程的超时时间

nginx结合jwplayer实现视频流媒体点播

一、nginx简介:

Nginx是一个网页服务器,它能反向代理HTTP, HTTPS, SMTP, POP3, IMAP的协议链接,以及一个负载均衡器和一个HTTP缓存。

Nginx是一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。与旧版本(<=2.2)的Apache不同,nginx不采用每客户机一线程的设计模型,而是充分使用异步逻辑,削减了上下文调度开销,所以并发服务能力更强。整体采用模块化设计,有丰富的模块库和第三方模块库,配置灵活。 在Linux操作系统下,nginx使用epoll事件模型,得益于此,nginx在Linux操作系统下效率相当高。同时Nginx在OpenBSD或FreeBSD操作系统上采用类似于epoll的高效事件模型kqueue。下图是实现的最终效果。

未分类

未分类

二、流媒体简介:

流媒体(Streaming media)是指将一连串的媒体数据压缩后,经过网络分段发送数据,在网络上即时传输影音以供观赏的一种技术与过程,此技术使得数据包得以像流水一样发送;如果不使用此技术,就必须在使用前下载整个媒体文件。流传输可发送现场影音或预存于服务器上的视频,当观看者在收看这些影音档时,影音数据在送达观赏者的电脑后立即由特定播放软件播放(如Windows Media Player,Real Player,或QuickTime Player)。

未分类

三、安装步骤:

1、 安装nginx

(1). 安装依赖包

yum -y install gcc openssl-devel pcre-devel httpd-tools gcc-c++

未分类

(2). 下载nginx

下载地址:http://nginx.org/download/nginx-1.10.2.tar.gz

wget http://nginx.org/download/nginx-1.10.2.tar.gz

(3). 解压nginx

tar xf nginx-1.10.2.tar.gz

(4). 进入nginx目录

cd nginx-1.10.2/

(5).创建nginx用户

useradd nginx

未分类

(6). 检查配置文件

> ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx
> --with-http_ssl_module --with-http_mp4_module --with-http_flv_module

未分类

(7). 编译并安装

make && make install

未分类

(8). 建立快捷方式

ln -s /usr/local/nginx/sbin/nginx /usr/sbin/

(9). 启动nginx服务

nginx

(10). 检查nginx监听信息

netstat -anptu | grep nginx

未分类

(11). 修改配置

vim /usr/local/nginx/conf/nginx.conf

配置内容

worker_processes  1;
events {
    worker_connections  65535;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        location ~ .flv$ {
            flv;
            limit_conn addr 4;
            limit_rate 1024k;
        }
        location ~ .mp4$ {
            mp4;
            limit_conn addr 4;
            limit_rate 1024k;
        }
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root html;
        }
    }
}

未分类

(12). 刷新配置

nginx -s reload #重启nginx,刷新配置

未分类

(13). 浏览器检测

未分类

2、 部署代码

(1). 上传代码、jwplayer、视频

jwplayer下载官网:https://www.jwplayer.com/

> scp -r index.html jwplayer qyz.flv
> [email protected]:/usr/local/nginx/html

未分类

html代码

未分类

(2). 浏览器检测点播效果

输入地址:http://192.168.4.1,点击播放

未分类

Nginx技术原理介绍

Nginx Inc. 联合创始人 Andrew Alexeev 在 AOSA 上发表的文章 [1] ,涵盖 了 Nginx 项目想要解决的问题、程序整体架构、配置文件结构、请求处理流程和开发 Nginx 过程中的心得体会等等话题。本节对该文中提到的关键点进行摘录和整理。

一些信息

  • 发音:( engine x );
  • 俄罗斯软件工程师 Igor Sysoev 于 2004 年发布的开源 Web 服务器软件;
  • 目前,在开源 Web 服务器按市场占有率排列的名单中,Nginx 位列第二名(Apache HTTPD 第一名)(据 2015年8月10日 w3techs 的统计数字,Nginx 已经跃居榜首);
  • Nginx 专注于高性能、高并发和低内存占用;
  • 除基本的 Web 服务功能外,Nginx 还提供了诸如「负载均衡」、「缓存」、「访问控 制」、「带宽控制」、「HTTP/TCP/UDP 反向代理」等功能,它还能高效得和其它各类 应用结合使用;
  • Nginx 使用 C 语言开发,除了 libc, zlib, PCRE 和 OpenSSL 外, 不依赖其它开发库。
  • Nginx 是一个「模块化的」、「基于事件驱动的」、「异步的」、「单线程的」、「非 阻赛的」的 Web 服务器;
  • Nginx 使用 C 语言风格的配置文件;

技术原理

事件驱动 vs. 多进程/线程

  • 1995 年首次发布的 Apache HTTPD,在 2012 年 2.4 稳定版之前,在 Linux 平台上只 能选择 mpm-prefork [6] 或者 mpm-worker [7] 作为并发模式。在这两种工作模式里,Apache 会为每个 HTTP 请求分配独立的进程或 者线程,请求处理完成后,进程或线程才会被回放释放。2.4 版增加了 mpm-event 模式 [8],虽然使用了 epoll, kqueue 等事件驱动机制, 但是请求处理依然由线程完成。
  • 在上世纪90年代,由于操作系统、计算机硬件和当时互联网状态的限制,Apache 选择 上述的并发模型是合理的,并且它也提供了相当丰富的功能和特性。但是,这些特点的 代价是,在每个连接会使用更多的内存资源,不能很好地支持网站非线性伸缩 (nonlinear scalability)。
  • 创建进程和线程(新的堆、栈和执行上下文)内存和 CPU 等资源消耗大;
  • 过多的上下文切换(context switching)会造成 thread thrashing ;
  • 随着硬件、操作系统和网络带宽的发展,它们不再是制约网站规模增长的瓶径:
  • C10K Problem [2]
  • Why threads are a bad idea (for most purposes) – 1995 [4] vs. Why events are a bad idea (for high-concurrency servers) – 2003 [3]
  • 各操作系统对事件驱动的支持
  • Unix-like/Windows – select/poll – 未考证,很早
  • FreeBSD/NetBSD/OpenBSD/OS X – kqueue – 2000
  • Windows/AIX/solaris 10 – IOCP – Windows NT 3.5
  • Linux – epoll – 2.5.44, 2003 年前,2001 年初稿 [5]
  • It’s time for web servers to handle ten thousand clients simultaneously.
  • Lighttpd [10]
  • Nginx [9]
  • libevent [11]
  • libuv [12]

并发模型

以解决 C10K 问题 [2] 为使命的 Nginx,在底层架构上选择了事件驱动机制。事件 驱动机在需要对并发连接和吞吐率提供非线性伸缩 (nonlinear scalability) 的场景中比 多进程/多线程更适合:负载持续上升时,CPU 和内存占用依然在可控的范围内;在普 通硬件配置上,Nginx 就可以服务数以万计的并发连接。

  • 多路复用(multiplexting)、事件通知(event notification);
  • 每个使用单线程的工作进程(worker)通过高效的 runloop 处理数以千记的并发连 接;
  • 工作进程从共享 listening socket ,接收新连接并将其加入到自己的 runloop 中。新连接的分发由操作系统决定(thundering herd 问题 [13]);
  • 对工作进程个数选择的一般建议:
  • CPU 密集型业务,比如,启用了 SSL、数据解压缩等场景下,工作进程个数由 CPU 核 数决定;
  • 磁盘 IO 密集型业务,工作进程个数可以设为 CPU 核数的 1.5 倍到 2 倍;

实现细节

  • Ngnix 会根据不同操作系统的特点进行不同的优化,以提高 IO 性能;

  • 工作进程中的 runloop 是 Nginx 代码中最复杂的部分,它的实现基于异步任务处 理思想。Nginx 通过使用模块化、事件通知、大量的回调函数和仔细调整的计时器等 手段实现异步操作。

  • 基本原则就是尽量使用非阻塞操作;
  • 读写性能不足的硬盘的操作是 Nginx 中唯一可能发生阻塞的地方;

  • Nginx 谨慎地使用系统调用实现了诸如内存池( pool )、片内存分配器 ( slab memory allocator )等高效的数据结构,再加上选取的并发模型,可以有 效节省对 CPU、内存等系统资源的占用。即使在极高的负载下,Nginx 的 CPU 使用率通 常也是很低的。

  • 在某种程度上,Nginx 借鉴了 Apache 的内存分配机制:
  • 它尽可能在内部通过传递指针的方式地减少内存拷贝;
  • Nginx 为每个连接创建一个内存池,为该连接生命期内的各种操作分配内存。比 如,连接内存池可以用来存储请求包头和包体数据,可以用来创建请求结构体 ( ngx_http_request_t )。Nginx 在关闭连接时,释放连接内存池;
  • Nginx 还会为请求分配请求内存池,为该请求生命期内的各种操作分配内存。比 如,与该请求相关的变量值,解析后的请求包头信息等。Nginx 在请求处理完毕后 ,释放请求内存池;
  • Nginx 使用 slab memory allocator 管理共享内存。共享内存一般用来存储 acept mutex, cache metadata, SSL session cache 等需要在进程间共 享的数据。同时,Nginx 使用 mutex semaphore 等加锁机制保证共享数据的 安全。

  • Nginx 提供了针对阻塞磁盘 IO 的优化措施: sendfile, AIO, thread pool (1.7.11+);

  • 除了 master 进程和 worker 进程,Nginx 还会启动 cache loader 进程 和 cache manager 进程,这些进程之间使用共享内存作为主要通信方式。

  • master 进程的主要工作如下:
  • 读取和验证配置文件;
  • 创建、绑定和并闭 socket;
  • 启动和停止 worker 进程,并按照配置的工作进程个数维护工作进程;
  • 在不中断服务的前提下,重新加载配置文件;
  • 实现 Nginx 二进制热切换;
  • 根据信号重新打开日志文件;
  • 编译内嵌 Perl 脚本;
  • worker 进程负责接收和处理客户端连接、反向代理客户端请求和响应过滤 等等 Nginx 的对外提供其它功能;
  • cache loader 进程负责遍历和检查磁盘缓存数据,并使用获取到的缓存元数据为 Nginx 建立内存数据库,加快运行期间的缓存查找速度;
  • 每个被缓存的响应都作为独立文件存储于文件系统中;
  • 用户可以通过配置文件控制缓存目录的分级结构;
  • 从 upstream 得到的响应数据会先存储到临时文件中,随后将响应数据发送给 客户端后,Nginx 将此临时文件挪到缓存目录下;
  • cache manager 进程长驻后台,并负责维护缓存数据的有效性,清理过期缓存数 据;

  • try_files 配置项和配置文件变量 (variables) 是 Nginx 独有的特性;

The try_files directive was initially meant to gradually replace
conditional ``if`` configuration statements in a more proper way, and it
was designed to quickly and efficiently try/match against different
URI-to-content mappings...adopt its use whenever applicable.
Variables in Nginx were developed to provide an additional
even-more-powerful mechanism to control run-time configuration of a web
server. Variables are optimized for quick evaluation and are internally
pre-compiled to indices. Evaluation is done on command; i.e., the value
of a variable is typically calculated only once and cached for the
lifetime of a particular request. Variables can be used with different
configuration directives, providing additional flexibility for describing
conditional request process behavior.

模块分类

Nginx 是一个模块化/插件化的程序,1.9.11 版本之前,Nginx 只支持静态链接模块 代码,从 1.9.11 版本之后,才引入动态加载模块的功能。

Nginx 代码由一个核心模块(core module)和很多功能模块组成(funcional module), 核心模块实现了 Web 服务器的基础、Web 和 Mail 反向代理功能等,为 Nginx 提供了 底层网络协议支持,构造了必要的运行环境,并保证其它不同功能模块的无封结合。而 其它模块则负责提供应用协议相关的特性。

在核心模块和功能模块之间,还有像 http, mail 这样的中层模块(upper-level module),它们为功能模块提供了核心模块和其它底层功能的抽像。这些模块也根据应用 层协议决定「事件」的处理顺序,同时,它们和核心模块结合,维护其它相关功能模块的 调用顺序。

功能模块分为: event module, phase handler, output filter, variable handler, protocol, upstream, 和 balancer 。

Nginx 为模块提供了很多不同的调用时机,模块初始化时可以为不同的时机注册不同的 回调函数。时机到来时,Nginx 就会调用相应的回调函数。Nginx 提供的调用时机有:

* Before the configuration file is read and processed.
* For each configuration directive for the the location and the server
  where it appears
* When the main configuration is initialized.
* When the server is initialized.
* When the server configuration is merged with the main configuration.
* When the location configuration is initialized or merged with its parent
  server configuration.
* When the master process starts or exits.
* When a new worker process starts or exits.
* When handling a request.
* When filtering the response header and the body.
* When picking, initiating and reinitiating a request to an upstream
  server.
* When process the response from and upstream server.
* When finishing an interation with an upstream server.

请求处理

  • 一个 HTTP 请求的处理流程如下:
1. Client sends HTTP request.
2. Nginx core choose the appropriate phase handler based on the configured
location matching the request.
3. If configured to do so, a load balancer picks an upstream server for
proxying.
4. Phase handler does its job and passes each outout buffer to the first filter.
5. First filter passes the outout to the second filter (and so on).
6. Final response is send to the client.
  • Nginx 将 HTTP 请求的处理过程分成了几个阶段,每个阶段都有相应的处理函数( phase handler )。阶段处理函数由配置文件 location {} 中的配置项决定。 HTTP 请求包头接受完毕后,Nginx 为该请求匹配合适的虚拟主机,随后请求会再交由 如下几个阶段的处理函数处理:
1. server rewrite phase
2. location phase
3. location rewrite phase (which can bring the request back to the previous phase)
4. access control phase
5. try_files phase
6. content phase (content handler first, then content phase handler)
7. log phase
  • Nginx 为 HTTP 请求生成响应数据后,将响应数据交给 filter 模块处理。 filter 模块可以分别对响应头和响应体进行处理。据此, filter 模块 可以分为 header filter 和 body filter 两大类;

  • header filter 主要工作步骤如下:

1. Decide whether to operate on this response.
2. Operate on the response
3. Call the next filter.
  • body filter 用于操作响应包体。比如,使用 gzip 压缩响应数据, 使用 chunked 编码响应数据等;

  • 两个特殊的 filter : copy filter 负责填充用于存放响应数据的内存 缓冲区(比如,使用临时文件的数据); postpone filter 负责子请求 (subrequest)相关的处理。

  • 子请求 (subrequest)是 Nginx 的强大特性之一,它是请求处理逻辑中非常重 要的一环。通过子请求机制,Nginx 可以:

  • 将不同 URL 的请求响应数据返回给当前请求(即主请求);

  • 将多个子请求的响应数据合并,返回给当前请求;

  • 将子请求再分裂为更多的子请求(sub-subrequest),子子请求( sub-sub-subrequest),形成子请求的层次关系;

  • 使用子请求读取磁盘文件、访问 Upstream 服务器等;

  • 在 filter 中,根据当前请求的响应数据触发子请求,例如:

For example, the SSI module uses a filter to parse the contents of the
returned document, and then replaces ``include`` directives with the
contents of specified URLs. Or, it can be an example of making a filter
that treats the entire contents of a document as a URL to be retrieved,
and then appends the new document to the URL itself.

最后

从上面的描述可以看出,Nginx 各模块结合紧密,模块逻辑复杂,使用 C 开发模块难度 较高,开发效率较低。幸运的是, lua-nginx-module, stream-lua-nginx-module 等第三方模块和官方 ngx-http-js-module 模块出现,降低的模块开发的门槛。开发 者借助它们,甚至可以开发运行于 Nginx 内部的业务应用了。

Lession learned:

There is always room for improvement.

It is worth avoiding the dilution of development efforts on something that
is neither the developer's core competence or the target application.

Ubuntu系统使用Nginx uWSGI部署Flask应用

我职业生涯的大部分都在使用微软的架构,最近我决定走出技术的舒适区,步入开源软件世界。我现在日常工作的项目是一个RESTful服务,这个服务需要在主流硬件上运行,且能够按照需要进行水平拓展。为完成这项工作我决定使用Flask和Nginx。Flask是一个轻量级的Python Web框架,Nginx是一个非常稳定的Web服务器,它们在廉价硬件平台上工作良好。

在这篇文章中我将指导你完成使用Nginx服务器托管Flask应用的安装、配置过程。我所使用的操作系统是Ubuntu 13.04。

前提条件

在我们开始安装Nginx及其他所需软件之前先安装一些前提软件。首先,我们需要PIP与virtualenv:

sudo apt-get install python-setuptools
sudo easy_install pip
sudo pip install virtualenv

使用apt-get安装Nginx的话,我们需要添加Nginx库到apt-get source中:

sudo add-apt-repository ppa:nginx/stable

注意:如果“add-apt-repository”命令在你的Ubuntu版本中不存在的话,你需要安装“software-properties-common”包,使用命令:sudo apt-get software-properties-common(感谢get_with_it在评论中提到)

升级已有的包,确保系统上有uWSGI所需的编译器和工具:

sudo apt-get update && sudo apt-get upgrade
sudo apt-get install build-essential python python-dev

Nginx

安装并运行Nginx:

sudo apt-get install nginx
sudo /etc/init.d/nginx start

Nginx是一个提供静态文件访问的web服务,然而,它不能直接执行托管Python应用程序,而uWSGI解决了这个问题。让我们先安装uWSGI,稍候再配置Nginx和uWSGI之间的交互。

sudo pip install uwsgi

里程碑 #1

打开浏览器访问你的服务器,你应该能看到Nginx欢迎页:

示例应用

我们将托管的应用是经典的“Hello, world!”。这个应用只有一个页面,已经猜到页面上将有什么内容了吧。将所有应用相关的文件存放在/var/www/demoapp文件夹中。下面创建这个文件夹并在其中初始化一个虚拟环境:

sudo mkdir /var/www
sudo mkdir /var/www/demoapp

由于我们使用root权限创建了这个文件夹,它目前归root用户所有,让我们更改它的所有权给你登录的用户(我的例子中是ubuntu)

sudo chown -R ubuntu:ubuntu /var/www/demoapp/

创建并激活一个虚拟环境,在其中安装Flask:

cd /var/www/demoapp
virtualenv venv
. venv/bin/activate
pip install flask

使用下面的代码创建hello.py文件:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080)

里程碑 #2

让我们执行我们刚创建的脚本:

python hello.py

现在你可以通过浏览器访问你服务器的8080端口,看,应用生效了:

注意:因为80端口已被Nginx使用,这里我使用8080端口。

现在应用是由Flask内置的web服务托管的,对于开发和调试这确实是个不错的工具,但不推荐在生产环境中使用。让我们配置Nginx来挑起这个重担吧。

配置Nginx

首先删除掉Nginx的默认配置文件:

sudo rm /etc/nginx/sites-enabled/default

注意:如果你安装了其他版本的Nginx,默认配置文件可能在/etc/nginx/conf.d文件夹下。

创建一个我们应用使用的新配置文件/var/www/demoapp/demoapp_nginx.conf:

server {
    listen      80;
    server_name localhost;
    charset     utf-8;
    client_max_body_size 75M;

    location / { try_files $uri @yourapplication; }
    location @yourapplication {
        include uwsgi_params;
        uwsgi_pass unix:/var/www/demoapp/demoapp_uwsgi.sock;
    }
}

将刚建立的配置文件使用符号链接到Nginx配置文件文件夹中,重启Nginx:

sudo ln -s /var/www/demoapp/demoapp_nginx.conf /etc/nginx/conf.d/
sudo /etc/init.d/nginx restart

里程碑 #3

访问服务器的公共ip地址,你会看到一个错误:

别担心,这个错误是正常的,它代表Nginx已经使用了我们新创建的配置文件,但在链接到我们的Python应用网关uWSGI时遇到了问题。到uWSGI的链接在Nginx配置文件的第10行定义:

uwsgi_pass unix:/var/www/demoapp/demoapp_uwsgi.sock;

这代表Nginx和uWSGI之间的链接是通过一个socket文件,这个文件位于/var/www/demoapp/demoapp_uwsgi.sock。因为我们还没有配置uWSGI,所以这个文件还不存在,因此Nginx返回“bad gateway”错误,让我们马上修正它吧。

配置uWSGI

创建一个新的uWSGI配置文件/var/www/demoapp/demoapp_uwsgi.ini:

[uwsgi]
#application's base folder
base = /var/www/demoapp

#python module to import
app = hello
module = %(app)

home = %(base)/venv
pythonpath = %(base)

#socket file's location
socket = /var/www/demoapp/%n.sock

#permissions for the socket file
chmod-socket    = 666

#the variable that holds a flask application inside the module imported at line #6
callable = app

#location of log files
logto = /var/log/uwsgi/%n.log

创建一个新文件夹存放uWSGI日志,更改文件夹的所有权:

sudo mkdir -p /var/log/uwsgi
sudo chown -R ubuntu:ubuntu /var/log/uwsgi

里程碑 #4

执行uWSGI,用新创建的配置文件作为参数:

uwsgi --ini /var/www/demoapp/demoapp_uwsgi.ini

接下来访问你的服务器,现在Nginx可以连接到uWSGI进程了:

我们现在基本完成了,唯一剩下的事情是配置uWSGI在后台运行,这是uWSGI Emperor的职责。

uWSGI Emperor

uWSGI Emperor (很拉风的名字,是不?) 负责读取配置文件并且生成uWSGI进程来执行它们。创建一个初始配置来运行emperor – /etc/init/uwsgi.conf:

description "uWSGI"
start on runlevel [2345]
stop on runlevel [06]
respawn

env UWSGI=/usr/local/bin/uwsgi
env LOGTO=/var/log/uwsgi/emperor.log

exec $UWSGI --master --emperor /etc/uwsgi/vassals --die-on-term --uid www-data --gid www-data --logto $LOGTO

最后一行运行uWSGI守护进程并让它到/etc/uwsgi/vassals文件夹查找配置文件。创建这个文件夹,在其中建立一个到链到我们刚创建配置文件的符号链接。

sudo mkdir /etc/uwsgi && sudo mkdir /etc/uwsgi/vassals
sudo ln -s /var/www/demoapp/demoapp_uwsgi.ini /etc/uwsgi/vassals

同时,最后一行说明用来运行守护进程的用户是www-data。为简单起见,将这个用户设置成应用和日志文件夹的所有者。

sudo chown -R www-data:www-data /var/www/demoapp/
sudo chown -R www-data:www-data /var/log/uwsgi/

注意:我们先前安装的Nginx版本使用“www-data”这个用户来运行Nginx,其他Nginx版本的可能使用“Nginx”这个替代用户。

由于Nginx和uWSGI都由同一个用户运行,我们可以在uWSGI配置中添加一个安全提升项。打开uWSGI配置文件,将chmod-socket值由666更改为644:

...
#permissions for the socket file
chmod-socket    = 644

现在我们可以运行uWSGI了:

sudo start uwsgi

最后,Nginx和uWSGI被配置成启动后立即对外提供我们的应用服务。

问题解决

如果出现错误的话,第一个检查的地方是日志文件。Nginx默认将错误信息写到/var/log/nginx/errors.log文件。

我们已经配置了uWSGI emperor将日志写到/var/log/uwsgi/emperor.log。这个文件夹还包含着每个配置应用的单独日志。我们的例子是 – /var/log/uwsgi/demoapp_uwsgi.log。

静态文件

如果你的应用提供静态文件的话,将下面的规则添加到demoapp_nginx.conf文件:

location /static {
    root /var/www/demoapp/;
}

上面配置的结果就是所有在/var/www/demoapp/static文件夹中的文件将由提供Nginx对外服务(谢谢Bastianh指出)

托管多个应用

如果你想在一台服务器上托管多个Flask应用,为每个应用创建一个单独的文件夹,像我们前面所做的一样,创建Nginx及uWSGI配置文件到应用文件夹的符号链接。

使用Distribute部署应用

使用distribute部署Flask应用的话,首先,按照Flask文档里的步骤将应用转化成package,然后复制distribute通用安装包到服务器上,使用虚拟环境中的Python来安装它。如下:

python setup.py install

最后且同样重要的是,uwsgi配置里应用属性的值要设置成包含Flask应用的包的名称。