使用Nginx来让Varnish支持HTTP/2

越来越多的公司开始使用HTTP/2来提高他们网站的性能以及用户体验. 开启HTTP/2的方法很简单,但是如何开启在HTTP/2与SSL 当你使用Varnish? 正如我们所了解的,Varnish 4.*不支持SSL, 我们需要找到一个方法来让这些组件协同工作.

HTTP/2

正如你所设想的, 互联网进步我们终于有了新版本的HTTP协议, 它主要的有点是:

-流与复用: 一个HTTP/2连接可以包含多个同时打开的流。请求的多路是由具有用它自己的流相关联的每个HTTP请求/响应交换实现的。流基本上是相互独立的,因此阻塞或失速请求或响应不会阻止上其他流的进展。

-报头压缩: 在HTTP / 1.1中,报头字段不被压缩,他们会不必要地占用带宽和增加延迟。在HTTP /2中,引入了一个新的压缩机制(HPACK)。它消除了冗余的报头字段,限制漏洞已知安全攻击,并具有在有限的环境使用有限的内存要求。

-服务器端推送: HTTP /2允许服务器先发制人发送(或“推”)的响应到客户端相关联地以前客户端发起的请求。当服务器知道客户端将需要具有以充分处理对原始请求的响应可用的那些响应这可能是有用的。

很多现代的浏览器已经支持HTTP/2, 在采取一些简单的操作开启HTTP/2之后,你将会收到预期的收益.

你也许可能能够在官方的文档中获取一些更多的点子:
– Hypertext Transfer Protocol Version 2 (HTTP/2) – RFC 7540

– HPACK: Header Compression for HTTP/2 – RFC 7541

为什么你需要HTTP/2

以上所有的解释都是很好的,但是有一些人喜欢直接看到结果或者直接去感受,直到那时候他们才会受到启发. 我所找到的一个最好的,令人印象深刻的DEMO由Akamai创建. 在Akamai的DEMO中,对比了加载一张相同的图片使用HTTP/1/1和HTTP/2. 如果你的浏览器支持HTTP/2, 我建议你自己体验一下Akamai的DEMO. 此外,为了应对HTTP /2,你必须启用SSL,它会提高你的网站的安全性。

Varnish的经典方案

 

一般Varnish作为前端使用,在用户与Web Server之间,架构看起来像这样:

architecture-basic

当我们有个客户请求了一个页面, 他的请求落到Varnish上(因为Varnish监听80端口), 然后Varnish检查这个对象是否已缓存,然后直接返回给客户不需要请求后端服务器或者请求后端服务器生成这个URL的页面.

现在的问题是 Varnish 4.*不支持浏览器所要求的 HTTP/2 SSL. 我们不能直接要求Varnish来监听443端口然后更改一些配置参数.

Nginx的解决方案

一个可能的方案就是在Varnish前增加Nginx. 它将会负责HTTP/2的请求, SSL以及发送请求到Varnish通过HTTP/1.1. 我们新的架构图将会看起来像这个样子:

architecture-ssl-http2

安装Nginx

最低版本要求为1.9.5(只有从这个版本开始ngx_http_v2_module 模块可用). 我是用的系统是Centos7, 因为Chrome最近移除了对SPDY的支持, 新的H2需要OpenSSL 1.0.2h才支持. Centos 7.x 源内自带的OpenSSL版本比较低并且Nginx不支持HTTP/2(包括Nginx的更新源). 想要使Nginx支持HTTP/2 我们使用以下脚本来编译自己的.src.rpm来增加HTTP/2的支持.

#!/bin/bash
yum -y groupinstall 'Development Tools'
yum -y install wget openssl-devel libxml2-devel libxslt-devel gd-devel perl-ExtUtils-Embed GeoIP-devel

OPENSSL="openssl-1.0.2h"
NGINX="nginx-1.11.3-1"

mkdir -p /opt/lib
wget https://www.openssl.org/source/$OPENSSL.tar.gz -O /opt/lib/$OPENSSL.tar.gz
tar -zxvf /opt/lib/$OPENSSL.tar.gz -C /opt/lib

rpm -ivh http://nginx.org/packages/mainline/centos/7/SRPMS/$NGINX.el7.ngx.src.rpm
sed -i "s|--with-http_ssl_module|--with-http_ssl_module --with-openssl=/opt/lib/$OPENSSL|g" /root/rpmbuild/SPECS/nginx.spec
rpmbuild -ba /root/rpmbuild/SPECS/nginx.spec
rpm -ivh /root/rpmbuild/RPMS/x86_64/$NGINX.el7.centos.ngx.x86_64.rpm

 

SSL证书

另一个我们需要的就是SSL证书, 我们可以使用Let’s Encrpty的免费证书,不过在这里出于演示的目的,我们使用OpenSSL来创建一个证书

openssl req -newkey rsa:2048 -sha256 -keyout jzbk.key -nodes -x509 -days 365 -out jzbk.crt

另一个重要的事情,在创建证书的时候,请输入你的域名当 Common Name 出现的时候. 如果你使用这个证书,你的浏览器将会提示这个证书不可信,出于测试的目的,请忽略这个提示.

配置 Nginx (HTTP/2, SSL)

好了,现在我们将证书文件复制到/etc/nginx下,然后开始配置Nginx

server {
    # 443 - default port for SSL
    listen 443 ssl http2;
    server_name <SERVER NAME>;
    # Specify the certificate files
    ssl_certificate jzbk.crt;
    ssl_certificate_key jzbk.kry;
 
    location / {
        # Set recommended by Nginx version
        proxy_http_version 1.1;
 
        proxy_pass http://127.0.0.1:80;
        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header Host $host;
    }
}

设置Varnish

一个最简单的VCL如下

vcl 4.0;
 
backend local {
    .host = "127.0.0.1";
    .port = "8080";
}

之后请修改使你的Varnish监听80端口.

配置Web Server

WebServer我会选择Nginx, 我们已经设置了Varnish的后端服务器, 下面是一个最简单的Nginx配置Demo

server {
    # Listen the port configured in the Varnish *.vcl file.
    listen 8080;
 
    server_name <THE SAME SERVER NAME>;
    
    # Define trusted addresses that are known to send correct replacement addresses.
    set_real_ip_from   127.0.0.1;
    real_ip_header     X-Forwarded-For;
    real_ip_recursive on;
 
    #...
}

使用 Chrome 来测试

想要测试所有组件都正常工作,你可以安装一个Chrome的插件叫做 HTTP/2 and SPDY indicator

总结

使用Nginx或者Varnish来缓存并不重要, 但是你必须需要配置HTTP/2, 因为它是一个可以轻易实现的目标.

 

原文链接(有少许修改): https://victor.4devs.io/en/architecture/varnish-cache-http2-with-nginx.html

Nginx让带www的域名跳转到根域名上和让不带www的根域名跳转到带www的域名上

在Nginx下有许多让带www域名跳转到不带www的根域名上或者让不带www跳转到www的域名上的方法

下面说一下我正在用的方法,希望对各位能有所帮助.

 

重定向非www域名到www上

单域名

server {
        server_name example.com;
        return 301 $scheme://www.example.com$request_uri;
}

全部域名

server {
        server_name "~^(?!www\.).*" ;
        return 301 $scheme://www.$host$request_uri;
}

 

重定向www域名到根域名上

单域名

server {
        server_name www.example.com;
        return 301 $scheme://example.com$request_uri;
}

全部域名

server {
         server_name "~^www\.(.*)$" ;
         return 301 $scheme://$1$request_uri ;
}

 

我们创建单独的 Server{} 块是Nginx官方推荐的最佳方法来实现跳转.

虽然WordPress下也有插件能在PHP级别上做到跳转,但是出于性能考虑,总是让Nginx来做Nginx可以做到的事.

使用Munin来监控Nginx

前言:

Munin是一款很高效的监控工具,拥有大量的插件. 对于Nginx来言,监控需要一点点的设置. 这一篇小轿车的目的就是手把手的来教你使用Munin来监控nginx请求数和状态.

本文中所有的操作都在Centos 7.1下完成, 理论上其他的发行版本也可以套用(或许需要一点点的修改)

 

正文:

首先,确定你的Nginx有http_stub_status_module模块. 执行nginx -V

[root@jp02 ~]# nginx -V
nginx version: nginx/1.11.3
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabledconfigure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules 
 --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.pid --lock-path=/var/run/nginx.lock 
 --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp 
 --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp 
 --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module 
 --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module
 --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module 
 --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module 
 --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic 
 --with-http_perl_module=dynamic --add-dynamic-module=njs-0.1.0/nginx --with-threads --with-stream --with-stream_ssl_module 
 --with-stream_geoip_module=dynamic --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 
 --with-http_v2_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong 
 --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic'

 

如果你在输出中没有找到–with-http_stub_status_module那你或许需要重新编译你的Nginx,或者安装Epel源中的版本

 

现在,增加nginx status到默认的虚拟主机内

location /nginx_status {
    stub_status on;    # activate stub_status module
    access_log off;    
    allow 127.0.0.1;   # restrict access to local only
    deny all;
}

 

当你完成之后,重新加载你的Nginx配置文件

sudo systemctl reload nginx

 

现在我们来配置Munin-node

使用任意一个你喜欢的文件编辑器,打开/etc/munin/plugin-conf.d/munin-node 增加以下内容(如果不存在)

[nginx_*]
env.url http://localhost/nginx_status

 

如果你的Nginx不是跑在80端口上或者绑定的是其他的域名请自行修改env.url

现在来更新Munin-node的插件

执行 munin-node-configure –suggest –shell | bash -x 将会自动链接Nginx插件.

[root@jp02 ~]# munin-node-configure --suggest --shell | bash -x
# The following plugins caused errors:
# http_loadtime:
# 	Junk printed to stderr
# pgbouncer_connections:
# 	Junk printed to stderr
# pgbouncer_requests:
# 	Junk printed to stderr
# postgres_autovacuum:
# 	Non-zero exit during autoconf (255)
# postgres_bgwriter:
# 	Non-zero exit during autoconf (255)
# postgres_cache_:
# 	Non-zero exit during autoconf (255)
# postgres_checkpoints:
# 	Non-zero exit during autoconf (255)
# postgres_connections_:
# 	Non-zero exit during autoconf (255)
# postgres_connections_db:
# 	Non-zero exit during autoconf (255)
# postgres_locks_:
# 	Non-zero exit during autoconf (255)
# postgres_oldest_prepared_xact_:
# 	Non-zero exit during autoconf (255)
# postgres_prepared_xacts_:
# 	Non-zero exit during autoconf (255)
# postgres_querylength_:
# 	Non-zero exit during autoconf (255)
# postgres_scans_:
# 	Non-zero exit during autoconf (255)
# postgres_size_:
# 	Non-zero exit during autoconf (255)
# postgres_transactions_:
# 	Non-zero exit during autoconf (255)
# postgres_tuples_:
# 	Non-zero exit during autoconf (255)
# postgres_users:
# 	Non-zero exit during autoconf (255)
# postgres_xlog:
# 	Non-zero exit during autoconf (255)
# proc:
# 	In family 'auto' but doesn't have 'autoconf' capability
# redis_:
# 	Junk printed to stderr
# slony_lag_:
# 	Junk printed to stderr
+ ln -s /usr/share/munin/plugins/nginx_request /etc/munin/plugins/nginx_request
+ ln -s /usr/share/munin/plugins/nginx_status /etc/munin/plugins/nginx_status

 

最后,重启Munin-node

sudo systemctl restart munin-node

 

稍等片刻之后即可在Munin的页面中看到Nginx的图表

Nginx报错sched_setaffinity() failed

最近升级了VPS, 在启动Nginx之后发现nginx_error.log不为空出现sched_setaffinity() 的错误:

2016/08/14 15:47:39 [alert] 9443#0: sched_setaffinity() failed (22: Invalid argument)

Google之后发现,是worker_cpu_affinity指令设置错误导致的问题.

worker_cpu_affinity命令是设置Nginx进程与CPU进行绑定的.可以充分的利用多核/线程的性能

双核机器根据互联网上的说明设置如下,

worker_processes  2;  
worker_cpu_affinity 01 10;

 

根据测试,worker_cpu_affinity设置为01 10会出现报错.

正确设置

worker_processes  2;  
worker_cpu_affinity 00000001 00000010;

 

修改nginx.conf之后重启nginx就没有报错了.

Nginx全局变量释义

$args 此变量与请求行中的参数相等

$content_length 等于请求行的“Content_Length”的值。

$content_type 等同与请求头部的”Content_Type”的值

$document_root 等同于当前请求的root指令指定的值

$document_uri 与$uri一样

$host 与请求头部中“Host”行指定的值或是request到达的server的名字(没有Host行)一样

$limit_rate 允许限制的连接速率

$request_method 等同于request的method,通常是“GET”或“POST”

$remote_addr 客户端ip

$remote_port 客户端port

$remote_user 等同于用户名,由ngx_http_auth_basic_module认证

$request_filename 当前请求的文件的路径名,由root或alias和URI request组合而成

$request_body_file

$request_uri 含有参数的完整的初始URI

$query_string 与$args一样

$server_protocol 等同于request的协议,使用“HTTP/1.0”或“HTTP/1.1”

$server_addr request到达的server的ip,一般获得此变量的值的目的是进行系统调用。为了避免系统调用,有必要在listen指令中指明ip,并使用bind参数。

$server_name 请求到达的服务器名

$server_port 请求到达的服务器的端口号

$uri 等同于当前request中的URI,可不同于初始值,例如内部重定向时或使用index

本文出自 “学习要永恒” 博客

NGINX自动切割日志的脚本

#! /bin/bash

nginx=/usr/local/nginx/sbin/nginx #NGINX的位置
Date=$(date -d -1day +"%Y%m%d") #获取昨天日期
logs_dir=/var/logs/ #log目录
bak_dir=/var/logs/$Date #log备份目录

#创建今日备份目录
mkdir $bak_dir

#先把现有的log文件挪到备份目录临时存放
cd $logs_dir
echo "moving logs"
/bin/mv *.log $bak_dir
sleep 3

#重建nginx log
echo "rebuild logs"
echo "nginx -s reopen"
$nginx -s reopen

Nginx+iptables屏蔽访问Web页面过于频繁的IP(防DDOS,恶意访问,采集器)

通过分析nginx的日志来过滤出访问过于频繁的IP地址,然后添加到nginx的blockip.conf,并重启nginx

脚本如下:

#!/bin/sh
nginx_home = /Data/app_1/nginx
log_path = /Data/logs

/usr/bin/tail -n50000 $log_path/access.log \
|awk '$8 ~/aspx/{print $2,$13}' \
|grep -i -v -E "google|yahoo|baidu|msnbot|FeedSky|sogou" \
|awk '{print $1}'|sort|uniq -c |sort -rn \
|awk '{if($1>150)print "deny "$2";"}'> $nginx_home/conf/vhosts/blockip.conf

/bin/kill -HUP `cat $nginx_home/nginx.pid`

也可以通过分析nginx日志过滤出频繁访问IP,直接用iptables屏蔽这个IP,就用不着重启nginx了,并且屏蔽效果更好。

单个IP的命令是

iptables -I INPUT -s 124.115.0.199 -j DROP

封IP段的命令是

iptables -I INPUT -s 124.115.0.0/16 -j DROP

封整个段的命令是

iptables -I INPUT -s 194.42.0.0/8 -j DROP

封几个段的命令是

iptables -I INPUT -s 61.37.80.0/24 -j DROP
iptables -I INPUT -s 61.37.81.0/24 -j DROP

刚才不小心搞的自己都连不上了-_-!!!

iptables -I INPUT -p tcp –dport 80 -s 124.115.0.0/24 -j DROP

以后用这种只封80端口就行了,不会出问题!

iptables -I INPUT -p tcp –dport 80 -s 124.115.0.0/24 -j DROP

iptables -I INPUT -s 61.37.81.0/24 -j DROP

解封

iptables -F

清空

iptables -D INPUT [编号]