前言:在网站架构当中,使用varnish缓存服务器为网站做缓存,后端再使用lnmp架构已经是很平常的事情了,但是,在很多种情况下,后端的nginx服务器,无法获取到用户的真实ip.日志中总是记录着前端的varnish服务器ip。这也让一些站长很烦恼。其实nginx有这种功能的。下面我就来讲解一下,如何让nginx在使用varnish做缓存的情况下获取到用户的真实ip。
我们先弄两台测试机器
 
   
  1. A:192.168.1.151 服务: varnishd 
  2. B:192.168.1.150 服务:  nginx 
一,我们先安装好这两个服务。
1..在A服务器上安装varnishd
 
   
  1. # yum install pcre* 
  2. # wget  http://repo.varnish-cache.org/source/varnish-3.0.0.tar.gz 
  3. # tar -xvf varnishd-3.0.0.tar.gz 
  4. # cd varnish-3.0.0 
  5. # ./configure --prefix=/usr/local/varnishd 
  6. # make&&make install 
  7.   
2,在B服务器上安装nginx
 
   
  1. # yum install pcre* 
  2. # wget  http://soft.vpser.net/web/nginx/nginx-1.0.10.tar.gz 
  3. # cd nginx-1.0.10/ 
  4. # ./configure --user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-ipv6 --with-http_realip_module 
  5. # make && make install 
这里在nginx安装时,添加了一个 --with-http_realip_module 的模块来获取真实ip.
这样我们就在两台服务器上分别安装好了这两个服务。
二.关于一些基本配置
这里我直接在varnishd配置文件里,加上一个缓存的网站  www.yaozhibingceshi.com  ,配置文件如下:
 
   
  1. #Cache for linuxtone sites  
  2. #backend vhost  
  3. backend  wwwyaozhibingceshicom {  
  4. .host = "www.yaozhibingceshi.com";  
  5. .port = "80";  
  6. }  
  7. #acl  
  8. acl purge {  
  9.   "localhost";  
  10.   "127.0.0.1";  
  11.   "192.168.0.0"/24;  
  12. }  
  13. sub vcl_recv {  
  14.         if (req.http.Accept-Encoding) {  
  15.             if (req.url ~ "\.(jpg|png|gif|jpeg|flv)$" ) {  
  16.                 remove req.http.Accept-Encoding;  
  17.                 remove req.http.Cookie;  
  18.             } else if (req.http.Accept-Encoding ~ "gzip") {  
  19.                 set req.http.Accept-Encoding = "gzip";  
  20.             } else if (req.http.Accept-Encoding ~ "deflate") {  
  21.                 set req.http.Accept-Encoding = "deflate";  
  22.             } else {  
  23.                 remove req.http.Accept-Encoding;  
  24.             }  
  25.         }  
  26.            if (req.http.host ~  "(.*)yaozhibingceshi.com") {  
  27.                        set req.backend = wwwyaozhibingceshicom;  
  28.                  }  
  29.             else {  
  30.                         error 404 "This website is maintaining or not exist!";  
  31.                 }  
  32.   if (req.request == "PURGE") {  
  33.      if (!client.ip ~purge) {  
  34.        error 405 "Not Allowed";  
  35.    }  
  36. #.dd.....  
  37.    return(lookup);  
  38.   }  
  39. #...GET...url...jpg,png,gif. ..cookie  
  40.   if (req.request == "GET"&& req.url ~ "\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm|gz|tgz|bz2|tbz|mp3|ogg|mp4|flv|f4v|pdf)$") {  
  41.         unset req.http.cookie;  
  42.   }  
  43. #..GET...url.php....cache....  
  44.   if (req.request =="GET"&&req.url ~ "\.php($|\?)"){  
  45.         return (pass);  
  46.   }  
  47. #   }  
  48. #........pipe..  
  49.     if (req.request != "GET" &&  
  50.       req.request != "HEAD" &&  
  51.       req.request != "PUT" &&  
  52.       req.request != "POST" &&  
  53.       req.request != "TRACE" &&  
  54.       req.request != "OPTIONS" &&  
  55.       req.request != "DELETE") {  
  56.         return (pipe);  
  57.     }  
  58. #..GET .HEAD.....  
  59.     if (req.request != "GET" && req.request != "HEAD") {  
  60.         return (pass);  
  61.     }  
  62.     if (req.http.Authorization) {  
  63.         return (pass);  
  64.     }  
  65.     return (lookup);  
  66. }  
  67. #..url+host hash......  
  68. sub vcl_hash {  
  69.     hash_data(req.url);  
  70.     if (req.http.host) {  
  71.         hash_data(req.http.host);  
  72.     } else {  
  73.         hash_data(server.ip);  
  74.     }  
  75.     return (hash);  
  76. }  
  77. # .....purge .....  
  78. sub vcl_hit {  
  79.    if (req.request == "PURGE") {  
  80.        set obj.ttl = 0s;  
  81.        error 200 "Purged";  
  82.     }  
  83.     return (deliver);  
  84. }  
  85. sub vcl_fetch {  
  86.           if (req.url ~ "\.(jpeg|jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|ico|swf|flv|dmg|js|css|html|htm)$") {  
  87.                    set beresp.ttl = 2d;  
  88.                    set berespberesp.http.expires = beresp.ttl;  
  89.                    set beresp.http.Cache-Control = "max-age=172800";  
  90.                    unset beresp.http.set-cookie;  
  91.           }  
  92.           if (req.url ~ "\.(dmg|js|css|html|htm)$") {  
  93.                    set beresp.do_gzip = true;  
  94.           }  
  95.           if (beresp.status == 503) {  
  96.                          set beresp.saintmode = 15s;  
  97.           }  
  98. }  
  99. sub vcl_deliver {  
  100.         set resp.http.x-hits = obj.hits ;  
  101.         if (obj.hits > 0) {  
  102.                 set resp.http.X-Cache = "HIT You!";  
  103.         } else {  
  104.                 set resp.http.X-Cache = "MISS Me!";  
  105.         }  
  106. }  
并在A服务器的host文件里绑定  www.yaozhibing.com 为 192.168.1.150
 
   
  1. # vim /etc/hosts 
  2. 192.168.1.150 www.yaozhibingceshi.com
启动varnish
 
 
   
  1. # /usr/local/varnishd/etc/varnish/vcl.conf -s malloc,10M -T 127.0.0.1:2000 -a 0.0.0.0:80 
B服务器上的Nginx 可参考网上一些配置,都是大同小异的,
我在这里添加一个虚拟主机:
 
   
  1. # vim /usr/local/nginx/conf/nginx.conf
  2. server 
  3.         {       listen    80; 
  4.                 server_name www.yaozhibingceshi.com; 
  5.                 index  index.php index.html index.htm; 
  6.                 root  /home/yaozhibing; 
  7.  
  8.   log_format  wwwlogs  '$remote_addr - $http_x_real_ip - $http_X_Forwarded_For - $remote_user [$time_local] "$request" '; 
  9.     access_log  /home/yaozhibingceshi.log  wwwlogs; 
我们在日志文件里定义了 $http_real_ip,  $http_X_forwarded_for的值,其实这两个值是一样的。 http_real_ip是指用户的真实ip。$http_X_forwarded_for是指通过上一级代理之前的ip。如果有多级代理,这个值里面就有很多的ip.我们这里只有一级代理,所以。这里的$http_X_forwarded_for 指的也是用户的真实ip.
好,我们来监控一下日志。看能不能获取到这些值。
很明显这些值,是获取不到的,只能获取到前端varnishd服务器 192.168.1.151的ip.
三,配置varnishd,让nginx获取到真实用户的ip.
那么现在我们来整一下varnish的配置。让varnish把$http_real_ip和$http_X_forwarded_for值传给nginx.
 我们在A服务器的varnish配置文件里的sub vcl_recv 里面加入以下这段:
 
   
  1. remove req.http.X-real-ip; 
  2. set req.http.X-real-ip = client.ip; 
  3. set req.http.X-Forwarded-For = client.ip; 
意思就是获取用户的真实ip 即 client.ip 并赋值给  http.X-real-ip 和 http.X-Forwarded-For。
现在的varnishd配置文件为: 
 
 
   
  1. #Cache for linuxtone sites   
  2. #backend vhost   
  3. backend  wwwyaozhibingceshicom {  
  4. .host = "www.yaozhibingceshi.com";  
  5. .port = "80";  
  6. }  
  7. #acl   
  8. acl purge {  
  9.   "localhost";  
  10.   "127.0.0.1";  
  11.   "192.168.0.0"/24;  
  12. }  
  13. sub vcl_recv {  
  14.   
  15.  
  16. #此处为添加内容 
  17. remove req.http.X-real-ip;  
  18. set req.http.X-real-ip = client.ip;  
  19. set req.http.X-Forwarded-For = client.ip;  
  20.   
  21.  
  22.         if (req.http.Accept-Encoding) {  
  23.             if (req.url ~ "\.(jpg|png|gif|jpeg|flv)$" ) {  
  24.                 remove req.http.Accept-Encoding;  
  25.                 remove req.http.Cookie;  
  26.             } else if (req.http.Accept-Encoding ~ "gzip") {  
  27.                 set req.http.Accept-Encoding = "gzip";  
  28.             } else if (req.http.Accept-Encoding ~ "deflate") {  

  29. #以下略。。  
我们再来看一下nginx的访问日志。看有没有获取到用户真实ip.
这下我们获取到了我本机的真实ip。192.168.1.5,但是我们从定义的日志格式来看,这个值应该是 http.X_Forwarded_For 的值。当有多级代理的时候,这个值不能代表用户的真实ip.但是我在varnishd确实把http_real_ip传过来了啊,为什么不能显示呢。这下,我们用要用到nginx的 http_realip_modul这个模块了。接下来,我们在nginx里,做一下设置,来获取 real_ip.
四:修改nginx配置文件,来获取用户真实ip.
  Nginx 的http_realip_modul很好用。在nginx里定义一下从哪获取值。获取哪个值。就OK 了,
我们把虚拟主机的配置文件修改成下面这样的:
 
   
  1. server 
  2.         { 
  3.                 listen    80; 
  4.                 server_name www.yaozhibingceshi.com; 
  5.                 index  index.php index.html index.htm; 
  6.                 root  /home/yaozhibing; 
  7. location / { 
  8.   set_real_ip_from   192.168.1.151; 
  9.   real_ip_header     X-Real-ip; 
  10.    log_format  wwwlogs  '$remote_addr - $http_x_real_ip - $http_X_Forwarded_For - $remote_user [$time_local] "$request" '; 
  11.     access_log  /home/yaozhibingceshi.log  wwwlogs; 
也就是在虚拟机主机的配置文件里,添加了:
 
   
  1. location / { 
  2.   set_real_ip_from   192.168.1.151; 
  3.   real_ip_header     X-Real-ip; 
set_real_ip_from 是定义获取的源,就是从哪里获取值
real_ip_header  是定义获取哪个值。
我们来重启一下nginx,并监控一下日志。
我们看到,http.X_real_ip 和 http.X_Forwarded_For的值。都能正常显示了。
如果不想看到上级代理的值。在nginx配置文件里把 log_format 的 $remote_addr去掉就可以了。
日志还能添加更多内容,这里我们只看ip,所以把日志简化写的。

如果不清楚请加QQ:410018348 共同探讨。