nginx反向代理
实验前提:
1、nginx反向代理服务器系统平台为RHEL 6.4(64bit),后面2个httpd服务器系统平台为RHEL 5.8(32bit)。
2、nginx反向代理服务器地址为
nginx反向代理服务器IP:192.168.108.230
上游httpd服务器IP:192.168.108.199(apache1) 192.168.108.201(apache2)
3、确保3台服务器时间同步
实验结构拓扑:
一、nginx作为反向代理服务器
Nginx通过proxy模块实现反向代理功能。在作为web反向代理服务器时,nginx负责接收客户请求,并能够根据URI、客户端参数或其它的处理逻辑将用户请求调度至上游服务器上(upstream server)。nginx在实现反向代理功能时的最重要指令为proxy_pass,它能够将location定义的某URI代理至指定的上游服务器(组)上。如下面的示例中,location的/web将被替换为上游服务器上的/newweb。
不过这个/newweb必须在上游服务器(即我们常说的后端服务器)的根目录下创建。如果这个/newweb是个目录,还需要在这个目录下创建默认主页(如:index.html)。将nginx作为web代理服务器时,还需要在配置文件中添加如下信息:
location /web {
proxy_pass http://www.xsl.com:8080/newweb;
}
这表示如果用户访问根目录下/web/下的默认主页时,实际访问的是上游服务器根目录下/newweb/下的默认主页。
添加完成之后,还需要在上游服务器中创建/newweb目录,
# mkdir /www/xsl.com/newweb
这里的/www/xsl.com是我的上游httpd服务器的根目录,这里我是基于虚拟主机的方式来配置的。如果大家是采用的默认配置,则根目录为/var/www/html/,然后在这个目录下创建相应的目录和文件即可。
#vim /www/xsl.com/newweb/index.html
node1.xsl.com
测试,在浏览器上输入http://192.168.108.230/web,测试结果如下:
不过,这种处理机制中有两个例外。一个是如果location的URI是通过模式匹配定义的,其URI将直接被传递至上游服务器,因此不能为其指定转换的另一个URI。例如下面示例中的/bbs将被代理为http://www.xsl.com/bbs。
location ~ ^/bbs {
proxy_pass http://www.xsl.com;
}
注意:如果在location中基于正则表达式来匹配url的话,那么proxy_pass指令后面不能接指定的uri。如上例所示。
第二个例外是,如果在loation中使用的URI重定向,那么nginx将使用重定向后的URI处理请求,而不再考虑上游服务器上定义的URI。
如下面所示的例子中,传送给上游服务器的URI为http://host/index.php?page=
location / {
rewrite /(.*)$ http://host/index.php?page=$1 ;
proxy_pass http://localhost:8080/index;
}
如果location写成这种格式的话,那么uri被重写完成后,依然会传给上游服务器处理。
如下图例子中,uri被重写为/index.php?page=
location / {
rewrite /(.$) /index.php?page=$1;
proxy_pass http://localhost:8080/index;
}
二、upstream模块
与proxy模块结合使用的模块中,最常用的当属upstream模块。upstream模块可定义一个新的上下文,它包含了一组upstream服务器,这些服务器可能被赋予了不同的权重、不同的类型甚至可以基于维护等原因被标记为down。
upstream模块常用的指令有:
ip_hash:基于客户端IP地址完成请求的分发,它可以保证来自于同一个ip的请求始终被转发至同一个upstream服务器;
keepalive:每个worker进程为发送到upstream服务器的连接所缓存的个数;
least_conn:最少连接调度算法;
server:定义一个upstream服务器的地址,还可包括一系列可选参数,如:
weight:权重;如果定义了多个server和weight,则用户的每一个请求会负载均衡的被上游服务器进行响应。这种负载均衡是基于加权轮调的方式进行的。
max_fails:最大失败连接次数,失败连接的超时时长由fail_timeout指定;
fail_timeout:等待请求的目标服务器发送响应的超时时长;
backup:用于fallback的目的,所有服务均故障时才启动此服务器;
down:手动标记其后端的上游服务器不再处理任何请求;
例如:在配置文件中添加如下将信息,注意upstream的配置要在http内server外
upstream webserver {
server 192.168.108.199 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.108.201 weight=1;
}
location /up {
proxy_pass http://webserver; 这里接的是组名
}
添加完成之后,还需要在后端的2个上游服务器中创建up目录和默认主页。
192.168.108.199上的配置
# mkdir /www/xsl.com/up
# vim /www/xsl.com/up/index.html
up server1 node1.xsl.com
然后在重启httpd服务器
# service httpd restart
192.168.108.201上的配置
# mkdir /www/xsl.com/up
# vim /www/xsl.com/up/index.html
up server2 node2.xsl.com
然后在重启httpd服务器
# service httpd restart
测试,在浏览器上方输入http://192.168.108.230/up,第一次测试结果如下:
然后刷新该页面,显示结果如下:
发现,用户的http请求均衡的被上游服务器进行响应。
当后端的所有服务器都down后,应该返回一个错误页面或提示页面给客户端,这样给用户看起来比较亲切。
需要在配置文件中添加如下信息:
server 127.0.0.1:8080 backup; 这一行信息添加在相应的upstream中。
server {
listen 8080;
server_name localhost;
root /nginx/err ;
index index.html ;
}
默认情况下,upstream使用的的复制均衡算法为轮调,有时候需要将同一个客户端的请求重定向到同一个服务器进行响应,以便重用session信息。为此,我们可以指定其算法为ip_hash,只要客户端的源ip相同,则以后该客户端所有的响应都由之前响应过的服务器进行响应。
ip_hash不可以和backup一起使用,它配置在upstream上下文中。
upstream模块的负载均衡算法主要有三种,轮调(round-robin)、ip哈希(ip_hash)和最少连接(least_conn)三种。
此外,upstream模块也能为非http类的应用实现负载均衡,如下面的示例定义了nginx为memcached服务实现负载均衡之目的。
upstream memcachesrvs {
server 172.16.100.6:11211;
server 172.16.100.7:11211;
}
server {
location / {
set $memcached_key "$uri?$args";
memcached_pass memcachesrvs;
error_page 404 = @fallback;
}
location @fallback {
proxy_pass http://127.0.0.1:8080;
}
}
三、定义请求报文头部的源地址
默认情况下,当nginx作为前端代理服务器时,用户的请求是通过nginx反向代理服务器调度到后端的上游服务器。因此,当nginx反向代理器收到请求后,会重新对请求的报文进行封装,然后在封装后的报文交给后端的上游服务器。因此,当后端的上游服务器接收请求时,则认为请求的源地址为nginx服务器的地址。
对于需要统计用户访问的网站而言,如果请求报文的源地址为nginx服务器的地址的话是无法进行统计的。对于请求的源地址,我们可以通过查看后端httpd服务器的访问日志来显示。如:
192.168.108.230 - - [24/Mar/2015:23:03:47 +0800] "GET /up/ HTTP/1.0" 304 - "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)"
192.168.108.230 - - [24/Mar/2015:23:03:47 +0800] "GET /up/ HTTP/1.0" 304 - "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)"
192.168.108.230 - - [24/Mar/2015:23:03:47 +0800] "GET /up/ HTTP/1.0" 304 - "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)"
在这里192.168.108.230是nginx反向代理服务器的地址,因此,对于这种现象是无法统计用户的访问量的。
还好,在proxy模块中有一个命令proxy_set_header可以用来修改请求报文的源地址的。如果需要修改请求报文的真实源ip,可以在nginx反向代理服务器的配置文件中添加如下信息:
proxy_set_header X-Real-IP $remote_addr; 这行命令添加在特定的location中即可。其中$remote_addr表示请求报文的源地址,后面X-Real-IP是一个自定义参数,这个参数的值就是$remote_addr,它会将该值传递给后端的上游服务器。
如:
location / {
root /www/htdoc;
index index.html index.htm;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
在nginx代理服务器上添加完成之后,要想在upstream服务器上显示的是请求报文的真实ip地址,,还需要在httpd的主配置文件修改如下这行信息:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
将这行中的"%h"修改为%{X-Real-IP}i即可。如:
LogFormat "%{X-Real-IP}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
然后再在后端的服务器上查看httpd的相关日志信息,显示内容如下:
192.168.106.12 - - [26/Mar/2015:18:53:39 +0800] "GET /up/ HTTP/1.0" 200 34 "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0; {D9D54F49-E51C-445e-92F2-1EE3C2313240}; 2345Explorer)"
192.168.106.12为客户端的ip地址,因此,此信息表明请求报文的源ip设置成功。
四、rewrite模块指令
nginx的重写模块是一个简单的正则表达式匹配与一个虚拟堆叠机结合。依赖于PCRE库,因此需要安装pcre。根据相关变量重定向和选择不同的配置,从一个location跳转到另一个location,不过这样的循环最多可以执行10次,超过后nginx将返回500错误。同时,重写模块包含set指令,来创建新的变量并设其值,这在有些情景下非常有用的,如记录条件标识、传递参数到其他location、记录做了什么等等。
rewrite模块包括如下几个指令:
rewrite
if
break
return
set
rewrite指令
rewrite可以实现uri重写,即将当前匹配的uri重写为另一个uri。rewrite指令根据配置文件中的顺序来执行。
1、rewrite语法格式:
语法:rewrite regex replacement flag。
rewrite可以用在server, location, if当中。通常rewrite用在location当中,并使用break标识符。也可以用在if上下文中。
2、重写规则
任何重写规则的第一部分都是一个正则表达式。可以使用括号来捕获,后续可以根据位置来将其引用,位置变量值取决于捕获正则表达式中的顺序,$1引用第一个括号中的值,$2引用第二个括号中的值,以此类推。
重写规则的第二部分是URI。URI可能包含正则表达式中的捕获的位置参数或这个级别下的nginx任何配置变量。注意:如果替换的字符串以http://开头,请求将被重定向,并且不再执行多余的rewrite指令。
第三部分也就是尾部的标记(flag)。
在rewrite语句后面还可以添加flag,这些flag可以是这些值:
last:本次重写完成之后,重新搜索location与更改后的URL匹配。这种flag是使用最多的,不过容易导致循环检查。不过循环次数最多也就10次。并返回状态码为500的错误。
break:本次重写完成后,不在执行后面的rewrite操作,直接执行后续操作。这种flag可以解决循环问题。
redirect:返回302临时重定向,如果替换字段是以http://开头则被重定向。
permanent:返回301永久重定向。
如:
location /p_w_picpath {
index index.html;
rewrite ^/p_w_picpath/bbs/(.*)$ http://192.168.108.201/jgp/$1;
}
^/p_w_picpath/bbs/(.*)$ 表示uri以/p_w_picpath开头,如果uri不是"/"的话,那么^后面就必须加uri(如/p_w_picpath)。
if判断语句(最好用在location中)
在location中使用if语句可以实现条件判断,其通常有一个return语句,且一般与有着last或break标记的rewrite规则一同使用。但其也可以按需要使用在多种场景下,需要注意的是,不当的使用可能会导致不可预料的后果。
if语句中的判断条件
正则表达式匹配:
~ :与指定正则表达式模式匹配时返回“真”,判断匹配与否时区分字符大小写;
~* :与指定正则表达式模式匹配时返回“真”,判断匹配与否时不区分字符大小写;
!~ :与指定正则表达式模式不匹配时返回“真”,判断匹配与否时区分字符大小写;
!~* :与指定正则表达式模式不匹配时返回“真”,判断匹配与否时不区分字符大小写;
文件及目录匹配判断:
-f, !-f:判断指定的路径是否为存在且为文件;
-d, !-d:判断指定的路径是否为存在且为目录;
-e, !-e:判断指定的路径是否存在,文件或目录均可;
-x, !-x:判断指定路径的文件是否存在且可执行;
字符串比较
使用=或!=运算符
关键字:
break:终止退出。
return:直接返回状态码。可使用值包括:204,400,402-406,408,410,411,413,416以及500-504。也可以发送非标准的444代码-未发送任何头信息下结束连接。
如:
location / {
if ($request_method == “PUT”) {
proxy_pass http://upload.xsl.com:8080;
}
if ($request_uri ~ "\.(jpg|gif|jpeg|png)$") {
proxy_pass http://p_w_picpathservers;
break;
}
}
upstream p_w_picpathservers {
server 192.168.108.199:80 weight 2;
server 192.168.108.201:80 weight 3;
}
set指令用来创建变量并进行赋值的。如:
set $a "hello" :表示创建变量$a,且该值为"hello"。
五、nginx的防盗链功能
有时候我们常常看到某些uri请求首部中包含referer这样的首部,referer表示当前uri是从哪个页面跳转过来的。之所以会产生这种情况是因为当前的uri被此前的页面盗用了。uri被盗用可能会增加本地网络的流量,有些时候我们需要禁止uri资源被盗链。在nginx中,提供了一种防盗链的机制,这种机制通过valid_referers指令来完成的。
nginx的防盗链功能通常需要2个步骤来完成。
(1)、定义访问方式,即表示允许通过哪些方式来访问uri。需要借助valid_referers指令来完成。
valid_referer指令格式为:
valid_referer {none|block|server_names}。默认值为none。
none:表示直接在浏览器输入网址来完成访问的。
block:表示被防火墙标记过来的。
server_names:表示这个域内的主机都可以访问,也可以指定一个特定的主机。
(2)、判断不合法的引用并对其处理。
通过借助if语句来完成判断并进行出来。
图片进行防盗链的事例如下:
在没有设置图片防盗链规则时,nginx的配置文件中添加如下内容:
location ~ /.*html$ {
root /html/b;
}
location ~* \.(gif|png|gif)$ {
root /tmp;
}
创建相应的目录及其文件
# mkdir -pv /html/b
# vim /html/b/2.html
hello,B
访问http://www.xsl.com/2.html,显示的结果是:
没有设置图片防盗链规则时,图片可以正常浏览。
设置图片防盗链规则,添加如下内容:
valid_referers none block 127.0.0.1;
并在location ~* \.(gif|png|gif)$ {root /tmp;}中添加一个if语句,其该location最终的配置为:
location ~* \.(gif|png|gif)$ {
root /tmp;
if ($invalid_referer) {
return 403;
}
}
$invalid_referer表示不合法的访问方式,即不属于valid_referers中定义的方式。
然后重载nginx
# service nginx reload
测试访问http://www.xsl.com/2.html,显示结果如下:
该页面的图片没有正常显示出来。
抓包获取的状态码也是为403
六、nginx代理服务器实现读写分离
在这里我们使用的写操作为put方法,由于put方法是是基于WebDAV技术的。WebDAV (Web-based Distributed Authoring and Versioning) 一种基于 HTTP 1.1协议的通信协议。它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,使应用程序可直接对Web Server直接读写,并支持写文件锁定(Locking)及解锁(Unlock),还可以支持文件的版本控制。
因此,需要在后端的httpd服务器中加载dav模块。如
LoadModule dav_module modules/mod_dav.so
LoadModule dav_fs_module modules/mod_dav_fs.so
加载完成之后,还需要在根目录
dav on
然后,编辑nginx的配置文件,修改文件内容为如下:
location / {
proxy_pass http://192.168.108.199/;
if ($request_method = "PUT") {
proxy_pass http://192.168.108.201;
}
}
然后还需要在192.18.108.201上设置apache用户对Apache的根目录具有写权限,如
#setfacl -m u:apache:rwx /www/xsl.com
然后再nginx服务器上上传文件
curl -T /etc/issue http://192.168.108.230
测试访问http://192.168.108.230/issue和访问http://192.168.108.199/issue即可发现读写分离以实现。这里不在贴图了。
七、nginx的cache功能
nginx做为反向代理时,能够将来自upstream的响应缓存至本地,并在后续客户端请求同样内容时直接从本地构造响应报文。
nginx的缓存空间包括共享内存空间和磁盘存储空间。其中共享内存空间存储的是键(key)和缓存对象的元数据。磁盘存储空间主要存储的就是数据。
如果想为nginx设定cahce功能,需要添加如下信息:
http {
proxy_cache_path /nginx/cache/web levels=1:2 keys_zone=cachename:20m miax_size=1G;
注意proxy_cache_path: 不能定义在server{}上下文中;
server {
location / {
proxy_cache cachename;
add_header X-Via $server_addr;
add_header X-Cache $upstream_cache_status;
proxy_cache_vaild 200 1m;
proxy_cache_vaild any 10m;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
}
}
}
测试访问http://www.xsl.com是否命中。不过对于第一次访问时,其结果肯定是不命中的
# curl -I http://www.xsl.com
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 17 Dec 2015 01:53:19 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 19
Connection: keep-alive
Last-Modified: Sat, 28 Nov 2015 22:10:53 GMT
ETag: "e06bc-13-525a114dea60d"
X-Via: 192.168.108.230
X-Cache: MISS
Accept-Ranges: bytes
不过第二次访问时,只要缓存是有效的,将直接从本地缓存中构建响应报文返回。
# curl -I http://www.xsl.com
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 17 Dec 2015 01:56:07 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 19
Connection: keep-alive
Last-Modified: Sat, 28 Nov 2015 22:11:15 GMT
ETag: "e06bd-13-525a1162497fe"
X-Via: 192.168.108.230
X-Cache: HIT
Accept-Ranges: bytes
另外常用的三种缓存:
open_log_cache:日志缓存
open_file_cache:文件缓存
fastcgi_cache: fastcgi缓存