目录
1、nginx软件安装
2、nginx proxy负载均衡搭建过程
3、不同域名站点分离
4、WEB日志客户端IP记录
5、根据扩展名实现服务器的动静分离
6、http proxy参数
7、Location指令
8、upstream参数
9、nginx负载均衡调度算法
#####################################################
本文内容来自《老男孩linux运维实战培训》学生―MR杨
欢迎广大运维同仁一起交流linux/unix网站运维技术!
QQ:10286460
E-mail:[email protected]
#####################################################
1.1硬件准备
4台虚拟机,一台做负载均衡,两台做RS,一台做客户端
hostname |
IP |
功能 |
nginx-1 |
10.0.0.239 |
nginx代理与负载均衡服务器 |
RS1 |
10.0.0.237 |
真实WEB服务器 |
RS2 |
10.0.0.240 |
真实WEB服务器 |
Client |
10.0.0.238 |
客户端 |
1.2软件准备
centos 5.8 x86_64
nginx-1.2.3.tar.gz
pcre-8.30.tar.gz
1.3下载地址
wgethttp://nginx.org/download/nginx-1.2.9.tar.gz
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.30.tar.gz
我使用的源包目录为/usr/local/src/
1.4安装pcre
tar zxf pcre-8.30.tar.gz
cd pcre-8.30/
./configure
make && make install
cd ../
安装pcre库是为了兼容nginx rewrite
1.5安装nginx
useradd nginx -M -s /sbin/nologin
tar -zxf nginx-1.2.9.tar.gz
cd nginx-1.2.9
./configure --user=nginx --group=nginx --prefix=/application/nginx-1.2.9 --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module
make && make install
安装完成后,检查语法,发现有错误!
[root@nginx-1 nginx-1.2.9]# /application/nginx-1.2.9/sbin/nginx -t
/application/nginx-1.2.9/sbin/nginx: error while loading sharedlibraries: libpcre.so.1: cannot open shared object file: No such file ordirectory
[root@nginx-1 nginx-1.2.9]#echo "/usr/local/lib" >>/etc/ld.so.conf #配置lib库,解决上述错误
[root@nginx-1 nginx-1.2.9]#ldconfig #使ld.so.conf修改生效
[root@nginx-1 nginx-1.2.9]#/application/nginx-1.2.9/sbin/nginx -t#检查语法,成功!
nginx: the configuration file/application/nginx-1.2.9/conf/nginx.conf syntax is ok
nginx: configuration file/application/nginx-1.2.9/conf/nginx.conf test is successful
[root@nginx-1 nginx-1.2.9]#ln -s /application/nginx-1.2.9 /application/nginx #增加软链接,方便升级
[root@nginx-1 nginx-1.2.9]#ll -d /application/nginx #查看软链接是否成功,如果闪动就没成功
lrwxrwxrwx 1 root root 24 Sep 8 18:42 /application/nginx ->/application/nginx-1.2.9
[root@nginx-1 nginx-1.2.9]# echo 'export PATH=$PATH:/application/nginx/sbin' >>/etc/profile#将Nginx命令加入系统全局变量,启动nginx
[root@nginx-1 nginx-1.2.9]#source /etc/profile #使全局变量修改生效
[root@nginx-1 nginx-1.2.9]#nginx #启动nginx
[root@nginx-1 nginx-1.2.9]#echo'/application/nginx/sbin/nginx' >> /etc/rc.local #加入开机自启动
[root@nginx-1nginx-1.2.9]#netstat -plnt |grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1352/nginx
1.6测试:
2.1nginx proxy配置
把下面内容添加到nginx.conf中,位置在最后一个大括号的前面。
upstream webserver {
server 10.0.0.240:80 weight=3;
server 10.0.0.237:80 weight=3;
}
server {
listen 80;
server_name www.etiantian.org;
location / {
proxy_pass http://webserver;
}
}
参数解释:
upstream webserver { } 定义真实服务器组
server {} 定义一个服务配置
listen 80 监听80端口
server_name 监听的地址或IP
location / 匹配server_name后的url或IP
proxy_pass 代理参数,后接upstream定义的服务器组
注:nginx proxy默认使用的是rr算法
修改完后,检查语法重启
[root@nginx-1 conf]# nginx -t
nginx: the configuration file /application/nginx-1.2.9/conf/nginx.confsyntax is ok
nginx: configuration file/application/nginx-1.2.9/conf/nginx.conf test is successful
[root@nginx-1 conf]# nginx -s reload
2.2真实服务器配置
两台操作一样,不需要动nginx.conf配置文件
[root@RS2 nginx]# echo 240-www >/application/nginx/html/index.html
[root@RS1 nginx]# echo 237-www >/application/nginx/html/index.html
2.3客户端测试:
[root@Client ~]# echo "10.0.0.239 www.etiantian.org" >>/etc/hosts
[root@Client~]# curl www.etiantian.org
240-www
[root@Client ~]# curl www.etiantian.org
237-www
[root@Client ~]# curl www.etiantian.org
240-www
[root@Client ~]# curl www.etiantian.org
237-www
[root@Client ~]# curl www.etiantian.org
240-www
[root@Client ~]# curl www.etiantian.org
237-www
当前已经可以进行负载均衡,但还存在一些问题。
2.4问题描述1:
访问blog与bbs时,请求没有转发到真实服务器,而返回是的代理服务器本地的web内容
复现问题:
2.4.1在真实服务器上配置创建blog,bbs站点目录
[root@RS2 nginx]# mkdir/data/html/blog -p
[root@RS2 nginx]# mkdir/data/html/bbs -p
[root@RS2 nginx]# echo240-blog > /data/html/blog/index.html
[root@RS2 nginx]# echo240-bbs > /data/html/bbs/index.html
2.4.2新增站点配置
把下面内容添加到240的nginx.conf中,位置在最后一个大括号前
server {
listen 80;
server_name blog.etiantian.org; #指定匹配域名
location / {
root /data/html/blog; #指定站点目录
index index.html index.htm;
}
}
server {
listen 80;
server_name bbs.etiantian.org; #指定匹配域名
location / {
root /data/html/bbs; #指定站点目录
index index.html index.htm;
}
}
配置完后检查语法重启
[root@RS2 nginx]#/application/nginx/sbin/nginx -t
nginx: the configuration file/application/nginx-1.2.9/conf/nginx.conf syntax is ok
nginx: configuration file/application/nginx-1.2.9/conf/nginx.conf test is successful
[root@RS2 nginx]# /application/nginx/sbin/nginx-s reload
2.4.3客户端测试
修改hosts解析文件如下
[root@Client ~]# tail -1 /etc/hosts
10.0.0.239 www.etiantian.org blog.etiantian.org bbs.etiantian.org
测试:
[root@Client ~]# curl www.etiantian.org
240-www
[root@Client ~]# curl bbs.etiantian.org
239-www
[root@Client ~]# curl blog.etiantian.org
239-www
可以看出上面返回的都是nginx proxy本地的数据
2.4.4找到问题:
查看nginx proxy的配置文件,发现只有匹配www.etiantian.org的会被代理到webserver组中的地址,其它的都匹配上localhost;访问本地的web站点(下面红色显示)。
[root@nginx-1 conf]# grep -v "#"nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
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;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
upstream webserver {
server 10.0.0.240:80 weight=3;
server 10.0.0.237:80 weight=3;
}
server {
listen 80;
server_name www.etiantian.org;
location / {
proxy_pass http://webserver;
}
}
}
2.4.5解决方法
1)修改nginx proxy配置文件:
2)把www.etiantian.org改为localhosts
3)把上面红色字体的server定义注释,本地不需要提供服务,只留下代理配置就OK
修改后的配置如下:
[root@nginx-1 conf]# grep -v "#"nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream webserver {
server 10.0.0.240:80 weight=3;
server 10.0.0.237:80 weight=3;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://webserver;
}
}
}
[root@nginx-1 conf]# nginx -t #检查语法
nginx: the configuration file/application/nginx-1.2.9/conf/nginx.conf syntax is ok
nginx: configuration file/application/nginx-1.2.9/conf/nginx.conf test is successful
[root@nginx-1 conf]# nginx -s reload
继续在客户端测试
[root@Client ~]# curl www.etiantian.org
237-www
[root@Client ~]# curl blog.etiantian.org
240-www
[root@Client ~]# curl bbs.etiantian.org
237-www
可看出上面请求已经转发到真实服务器上去了。因为localhost是指的本机IP,只要目的IP是本机端口80的就代理到真实服务器上。
上步测试为什么都是www站点?
3.1直接访问测试
修改hosts文件,直接访问是正常的
[root@Client ~]# tail -1 /etc/hosts
10.0.0.240 www.etiantian.org blog.etiantian.org bbs.etiantian.org
[root@Client ~]# curl bbs.etiantian.org
240-bbs
[root@Client ~]# curl blog.etiantian.org
240-blog
[root@Client ~]# curl www.etiantian.org
240-www
直接访问结果是正确的,但为什么通过nginx代理过去的请求,不能区分blog,bbs,www站点呢?原因是:nginx代理转发请求的时候,不携带原始url内容,所以匹配默认www站点了。
3.2添加proxy_set_header参数
修改nginx代理上的nginx.conf文件,在proxy_pass下添加红色内容
[root@nginx-1 conf]# grep -v "#" nginx.conf|grep proxy
proxy_pass http://webserver;
proxy_set_header Host $host;
/application/nginx/sbin/nginx -t
/application/nginx/sbin/nginx -s reload
3.3客户端测试:
[root@Client ~]# curl bbs.etiantian.org
240-bbs
[root@Client ~]# curl bbs.etiantian.org
237-bbs
访问已经正常!
4.1日志查看
日志路径在/application/nginx/logs/下
[root@RS2 nginx]# tail -1 logs/access.log
10.0.0.239 - - [09/Sep/2013:19:48:16 +0800]"GET / HTTP/1.0" 200 9 "-" "curl/7.15.5(x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3libidn/0.6.5"
发现只记录了nginx代理的地址,没有客户机的地址,不方便统计客户IP访问次数!
4.2在nginx proxy配置文件添加X-Forwarded-For参数
在nginx proxy服务器nginx.conf里添加红色参数
[root@nginx-1 nginx]# grep -v "#"conf/nginx.conf|grep proxy
proxy_pass http://webserver;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
注:X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。
4.3修改真实服务器的nginx.conf上修改日志参数
log_format main '$remote_addr - $remote_user[$time_local] "$request" '
'$status $body_bytes_sent"$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" ';
access_log logs/access.log main;
取消上面内容前面的#号,不然系统还会按默认的日志格式输出
检查语法并重启,只要修改了配置文件,就需要重启服务
/application/nginx/sbin/nginx -t
/application/nginx/sbin/nginx -s reload
4.4日志WEBIP测试:
[root@Client ~]# curl bbs.etiantian.org
240-bbs
[root@RS2 nginx]# tail logs/access.log
10.0.0.239 - - [09/Sep/2013:20:32:11 +0800]"GET / HTTP/1.0" 200 8 "-" "curl/7.15.5(x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3libidn/0.6.5"
10.0.0.239 - - [09/Sep/2013:20:33:01 +0800]"GET / HTTP/1.0" 200 8 "-" "curl/7.15.5(x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3libidn/0.6.5"
10.0.0.239 - - [09/Sep/2013:20:35:51 +0800]"GET / HTTP/1.0" 200 8 "-" "curl/7.15.5(x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3libidn/0.6.5" "10.0.0.238"
已经在日志中加入了真实客户端地址!
模拟RS1为图片存储服务器,RS2为动态内容存储服务器,以bbs域名举例
修改nginx proxy上面配置,添加下列配置(红色部分)
upstream webserver {
server 10.0.0.240:80 weight=3;
server 10.0.0.237:80 weight=3;
}
upstream static_pools {
server 10.0.0.240:80 weight=3;
}
upstream dynamic_pools {
server 10.0.0.237:80 weight=3;
}
server {
listen 80;
server_name blog.etiantian.org;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
proxy_pass http://static_pools;
include proxy.conf;
}
location ~ .*\.(php|php3|php5)$ {
proxy_pass http://dynamic_pools;
include proxy.conf; #当参数比较多,且参数相同时,可使用包含文件功能,使配置文件显得简洁,该路径配置使用相对路径
}
location / {
proxy_pass http://webserver;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
#server {
#listen 80;
#server_name blog.etiantian.org;
#location / {
#proxy_pass http://webserver;
#proxy_set_header Host $host;
#proxy_set_header X-Forwarded-For $remote_addr;
#}
#}
}
proxy.conf文件里参数配置
[root@nginx-1 conf]# vi /application/nginx/conf/proxy.conf
client_max_body_size 300m;
client_body_buffer_size 128k;
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
proxy_buffer_size 16k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
参数解释:
#允许客户端请求的最大的单个文件字节数
client_max_body_size 300m;
#缓冲区代理缓冲用户端请求的最大字节数可以理解为先保存到本地再传给用户
client_body_buffer_size 128k;
#跟后端服务器连接的超时时间_发起握手等候响应超时时间
proxy_connect_timeout 600;
#连接成功后_等候后端服务器响应时间_其实已经进入后端的排队之中等候处理
proxy_read_timeout 600;
#后端服务器数据回传时间_就是在规定时间之内后端服务器必须传完所有的数据
proxy_send_timeout 600;
#代理请求缓存区_这个缓存区间会保存用户的头信息以供Nginx进行规则处理_一般只要能保存下头信息即可
proxy_buffer_size 16k;
#同上告诉Nginx保存单个用的几个Buffer 最大用多大空间
proxy_buffers 4 32k;
#如果系统很忙的时候可以申请更大的proxy_buffers 官方推荐*2
proxy_busy_buffers_size 64k;
#获取url头部信息,用于真实服务器区分域名
proxy_set_header Host$host;
#用于真实服务器获取客户端IP信息
proxy_set_headerX-Forwarded-For $remote_addr;
模拟动态静态服务器
1)查看blog站点目录
[root@RS2 conf]# cat /application/nginx/conf/nginx.conf|grep bbs
server_name bbs.etiantian.org;
root /data/html/bbs;
#RS1路径也一样
2)模拟动态和静态站点目录下文件
[root@RS2 conf]# mkdir /data/html/bbs/soft -p
[root@RS2 conf]# echo 240-static-blog> /data/html/bbs/soft/3011.jpg
#图片放在RS2上
[root@RS1 conf]# mkdir /data/html/bbs/soft -p
[root@RS1 conf]# echo 237-dynamic-blog> /data/html/bbs/soft/3011.php
#php文件放在RS1上
3)客户端测试
[root@Client ~]# tail -1/etc/hosts
10.0.0.239 www.etiantian.org blog.etiantian.org bbs.etiantian.org #bbs域名解析到nginx proxy上
[root@Client ~]# curl bbs.etiantian.org/soft/3011.jpg
240-static-blog
[root@Client ~]# curl bbs.etiantian.org/soft/3011.php
237-dynamic-bbs
[root@Client ~]# curl bbs.etiantian.org/soft/3011.php
237-dynamic-bbs
[root@Client ~]# curl bbs.etiantian.org/soft/3011.jpg
240-static-blog
[root@Client ~]# curl bbs.etiantian.org
240-bbs
[root@Client ~]# curl bbs.etiantian.org
237-bbs
#域名访问还是分担的,但是对于.jpg或.php结尾的后缀,都会转交到专门的服务器上,由此就可以实现动静分离。这是LVS不能实现的。
proxy_next_upstream
语法:proxy_next_upstream [error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_404|off]
确定在何种情况下请求将转发到下一个服务器。转发请求只发生在没有数据传递到客户端的过程中。
proxy_connect_timeout
后端服务器连接的超时时间_发起握手等候响应超时时间
proxy_read_timeout
连接成功后_等候后端服务器响应时间_其实已经进入后端的排队之中等候处理(也可以说是后端服务器处理请求的时间)
proxy_send_timeout
后端服务器数据回传时间_就是在规定时间之内后端服务器必须传完所有的数据
proxy_pass
这个指令设置被代理服务器的地址和被映射的URI
proxy_pass
这个指令设置被代理服务器的地址和被映射的URI
proxy_set_header Host $host;
nginx均衡器转发到真实服务器时,不携带请求的url,设置该参数时,就会携带原始url
proxy_set_headerX-Forwarded-For $remote_addr;
当服务端需要获取客户机ip时,需要加上该选项,会在访问日志中有客户IP记录
语法: location [=|~|~*|^~] /uri/ { …}
解释:
= |
精确匹配,如果找到,立即停止搜索,并立即处理请求(优先级最高) |
^~ |
表示uri以某个常规字符串开头,理解为匹配url路径即可。nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格)。 |
~ |
区分大小写的正则匹配 |
~* |
不区分大小写的正则匹配 |
/ |
通用匹配,任何请求都会匹配到。 |
@ |
指定一个命名的location,一般只用于内部重定向请求 |
匹配顺序 |
首先匹配 =,其次匹配^~, 其次是按文件中顺序的正则匹配,最后是交给 / 通用匹配。当有匹配成功时候,停止匹配,按当前匹配规则处理请求。 |
每个设备的状态参数如下:
参数 |
描述 |
weight |
默认为1,weight越大,负载的权重就越大 |
backup |
热备配置(RS高可用),当前面激活的RS都失败后会自动启用热备RS |
max_fails |
最大尝试失败的次数,默认为1,0表示禁止偿试。企业场景,2-3次比较合理。 |
fail_timeout |
失败超时时间,默认是10s,常规业务2-3s比较合理 |
down |
表示这个server暂时不参与负载 |
注:如果使用ip_hash负载时,后面的状态不能是weight和backup。
1)轮询(默认)
每个请求按时间顺序注意分配到不同的机器,相当于LVS中rr算法,如果后端服务器宕机(默认情况下只检测80端口,如果后端报502,404,403,503,还是会直接返回给用户),则会跳过该服务器,将请求分配给下一个服务器。
默认算法
2)weight(权重)
在指定的轮询的基础上加上权重(默认是rr+weight),权重轮询和访问成正比,权重越大,转发的请求也就越多。可以根据服务器的配置和性能指定权重值大小,可以有效解决新旧服务器分配问题。
示例:
upstream bakend {
server 192.168.0.14weight=10;
server 192.168.0.15 weight=10;
}
3)ip_hash
每个请求按访问的Ip的hash结果分配,当新的请求到达时,先将其客户端ip通过哈希算法哈希出一个值,在随后请求客户端Ip的哈希值只要相同,就会被分配至同一个服务器,该调度算法可以解决session问题,但有时会导致分配不均即,无法保证负载均衡。提示:必须是最前端的服务器,后端也必须直接接应用服务器
示例:
upstream engine {
server 192.168.18.211:80;
server 192.168.18.212:80;
server 192.168.18.213:80 down;
ip_hash;
}
4)fair(第三方)
按照后端服务器的响应时间来分配请求,响应时间短的优先分配。
示例:
upstream backend {
server server1;
server server2;
fair;
}
5)url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法。
示例:
upstream engine {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
url_hash在缓存服务器组中将非常有用