Nginx(四)Nginx的配置

概览

nginx配置文件路径

不同安装方式,nginx的文件存放路径也有所不同。

源码编译安装方式:在安装目录下的conf目录下,比如我的安装目录是/usr/local/nginx,那么配置文件就在/usr/local/nginx/conf目录下。

yum安装方式:在/etc/nginx/目录(主配置文件)与/etc/nginx/conf.d目录下。

主要模块

nginx.conf由多个块组成,最外面的块是main,main包含Events和HTTP,HTTP包含upstream和多个server,server又包含多个location:

  • main块:配置影响nginx全局的指令。主要包括配置nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。
  • events块:配置影响nginx服务器或与用户的网络连接。主要包括配置每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
  • http块:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入(include),mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。
  • server块:用于配置服务器主机的一些参数,比如主机名、监听端口、重定向url等;一个http中可以有多个server。
  • upstream块:主要用于负载均衡配置,设置一系列的后端服务器以及负载均衡的策略。
  • location块:配置请求的路由匹配,以及各种页面的处理情况。

这几个有如下关系:server继承main,location继承server,upstream既不会继承其他设置也不会被继承。

注意项:

  1. 关于配置文件的引入,通过 include 引入一个文件,这个文件里的内容将被当做一段文字嵌套到引入的位置。在main模块和http模块都可以引入外部的文件

示例

如下是一个配置文件的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
########### 每个指令必须有分号结束。#################

user nobody; #安全问题,建议用nobody,不要用root.

worker_processes 2; #worker数和服务器的cpu数相等是最为适宜

worker_cpu_affinity 0001 0010 0100 1000 #work绑定cpu(4 work绑定4cpu)

#worker_cpu_affinity 0000001 00000010 00000100 00001000 #work绑定cpu (4 work绑定8cpu中的4个)


#error_log path(存放路径) level(日志等级)path表示日志路径,level表示日志等级,
#具体如下:[ debug | info | notice | warn | error | crit ]
#从左至右,日志详细程度逐级递减,即debug最详细,crit最少,默认为crit。

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

pid logs/nginx.pid; #nginx的pid(进程id)存放路径


events {
#这个值是表示每个worker进程所能建立连接的最大值,所以,一个nginx能建立的最大连接数,应该是worker_connections * worker_processes。
#当然,这里说的是最大连接数,对于HTTP请求本地资源来说,能够支持的最大并发数量是worker_connections * worker_processes,
#如果是支持http1.1的浏览器每次访问要占两个连接,
#所以普通的静态访问最大并发数是: worker_connections * worker_processes /2,
#而如果是HTTP作为反向代理来说,最大并发数量应该是worker_connections * worker_processes/4。
#因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端服务的连接,会占用两个连接。

worker_connections 1024;

#这个值是表示nginx要支持哪种多路io复用。
#一般的Linux选择epoll, 如果是(*BSD)系列的Linux使用kquene。
#windows版本的nginx不支持多路IO复用,这个值不用配。
use epoll;

# 当一个worker抢占到一个链接时,是否尽可能的让其获得更多的连接,默认是off 。
multi_accept on;

# 默认是on ,开启nginx的抢占锁机制。
accept_mutex on;
}


http {
#当web服务器收到静态的资源文件请求时,依据请求文件的后缀名在服务器的MIME配置文件中找到对应的MIME Type,再根据MIME Type设置HTTP Response的Content-Type,然后浏览器根据Content-Type的值处理文件。

include mime.types;

#如果 不能从mime.types找到映射的话,用以下作为默认值
default_type application/octet-stream;


#日志位置
access_log logs/host.access.log main;

#一条典型的accesslog:
#101.226.166.254 - - [21/Oct/2013:20:34:28 +0800] "GET /movie_cat.php?year=2013 HTTP/1.1" 200 5209 "http://www.baidu.com";; "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; MDDR; .NET4.0C; .NET4.0E; .NET CLR 1.1.4322; Tablet PC 2.0); 360Spider"
#1)101.226.166.254:(用户IP)
#2)[21/Oct/2013:20:34:28 +0800]:(访问时间)
#3)GET:http请求方式,有GET和POST两种
#4)/movie_cat.php?year=2013:当前访问的网页是动态网页,movie_cat.php即请求的后台接口,year=2013为具体接口的参数
#5)200:服务状态,200表示正常,常见的还有,301永久重定向、4XX表示请求出错、5XX服务器内部错误
#6)5209:传送字节数为5209,单位为byte
#7)"http://www.baidu.com":refer:即当前页面的上一个网页
#8)"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; MDDR; .NET4.0C; .NET4.0E; .NET CLR 1.1.4322; Tablet PC 2.0); 360Spider": agent字段:通常用来记录操作系统、浏览器版本、浏览器内核等信息

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';



#开启从磁盘直接到网络的文件传输,适用于有大文件上传下载的情况,提高IO效率。
sendfile on;

#一个请求完成之后还要保持连接多久, 默认为0,表示完成请求后直接关闭连接。
#keepalive_timeout 0;
keepalive_timeout 65;

#开启或者关闭gzip模块
#gzip on ;

#设置允许压缩的页面最小字节数,页面字节数从header头中的Content-Length中进行获取。
#gzip_min_lenth 1k;

# gzip压缩比,1 压缩比最小处理速度最快,9 压缩比最大但处理最慢(传输快但比较消耗cpu)
#gzip_comp_level 4;

#匹配MIME类型进行压缩,(无论是否指定)"text/html"类型总是会被压缩的。
#gzip_types types text/plain text/css application/json application/x-javascript text/xml


#动静分离
#服务器端静态资源缓存,最大缓存到内存中的文件,不活跃期限
open_file_cache max=655350 inactive=20s;

#活跃期限内最少使用的次数,否则视为不活跃。
open_file_cache_min_uses 2;

#验证缓存是否活跃的时间间隔
open_file_cache_valid 30s;




upstream myserver {

######## 可选多种负载均衡的调度算法 ########
# 1、轮询(默认)
# 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
# 2、指定权重
# 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
#3、IP绑定 ip_hash
# 每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
#4、fair(第三方)
#按后端服务器的响应时间来分配请求,响应时间短的优先分配。
#5、url_hash(第三方)
#按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

#ip_hash;
server 192.168.161.132:8080 weight=1;
server 192.168.161.132:8081 weight=1;

#fair

#hash $request_uri
#hash_method crc32

}

server {
#监听端口号
listen 80;

#服务名
server_name 192.168.161.130;

#字符集
#charset utf-8;

##### 匹配算法 #####
#location [=|~|~*|^~] /uri/ { … }
# = 精确匹配
# ~ 正则匹配,区分大小写
# ~* 正则匹配,不区分大小写
# ^~ 关闭正则匹配

##### 匹配原则 #####
# 1、所有匹配分两个阶段,第一个叫普通匹配,第二个叫正则匹配。
# 2、普通匹配,首先通过“=”来匹配完全精确的location。
# 2.1、 如果没有精确匹配到, 那么按照最大前缀匹配的原则,来匹配location。
# 2.2、 如果匹配到的location有^~,则以此location为匹配最终结果,如果没有那么会把匹配的结果暂存,继续进行正则匹配。
# 3、正则匹配,依次从上到下匹配前缀是~或~*的location, 一旦匹配成功一次,则立刻以此location为准,不再向下继续进行正则匹配。
# 4、如果正则匹配都不成功,则继续使用之前暂存的普通匹配成功的location.


location / { # 匹配任何查询,因为所有请求都已 / 开头。但是正则表达式规则和长的块规则将被优先和查询匹配。

#定义服务器的默认网站根目录位置
root html;

#默认访问首页索引文件的名称
index index.html index.htm;

proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

#反向代理路径
proxy_pass http://myserver;

#反向代理的超时时间
proxy_connect_timeout 10;

proxy_redirect default;

}

location /images/ { # 匹配任何已 /images/ 开头的任何查询并且停止搜索。任何正则表达式将不会被测试。
root images ;
}

location ^~ /images/jpg/ { # 匹配任何已 /images/jpg/ 开头的任何查询并且停止搜索。任何正则表达式将不会被测试。
root images/jpg/ ;
}

location ~*.(gif|jpg|jpeg)$ {

#所有静态文件直接读取硬盘
root pic ;

#expires定义用户浏览器缓存的时间为3天,如果静态页面不常更新,可以设置更长,这样可以节省带宽和缓解服务器的压力
expires 3d; #缓存3天
}


#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}


}

实操

如下是一个使用Nginx做转发和负载均衡的实例操作。

  1. 环境准备。Linux虚拟机ip:192.168.128.1,在虚拟机上部署biz-user-service服务分别运行在8011和8012两个端口。

  2. 本地host配置中增加

    1
    192.168.128.1	user.lzumetal.com

    这样当在本机访问user.lzumetal.com这个域名时,将会访问到192.168.128.1的80端口。(当然在实际应用中则是将二级域名解析到对应的nginx服务器上。)

  3. 在虚拟机的nginx配置文件中增加upstream的配置:

    1
    2
    3
    4
    5
    upstream userservice {
    ip_hash;
    server localhost:8011 weight=1 max_fails=1 fail_timeout=60s;
    server localhost:8012 weight=1 max_fails=1 fail_timeout=60s;
    }

    负载均衡使用ip hash的方式。

  4. 在虚拟机的nginx配置文件中增加server和location的配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    server {
    listen 80;
    server_name user.lzumetal.com;

    location / {
    proxy_pass http://userservice/; #引用upstream
    root html;
    index index.html index.htm;
    }
    }

    nginx将请求会转发到upstream为userservice的配置下。

  5. 在postman中多次访问biz-user-service服务的接口:

    接口响应正常,再查看服务的日志,看到请求都进入到了8012端口的服务里。

    而这也是ip hash策略的必然结果。

详解

配置文件里的一些配置项,在注释中已经说明的比较详细了,这里只对upstream和server里的配置做一些分析。

location中proxy_pass路径匹配的问题

location中是否已/结尾的区别
如下,如果test后不带/,表示会拦截如:/test/test/xxx/test000这一类请求,只要是以test开头的请求都会被拦截。

1
2
3
location /test {
proxy_pass http://127.0.0.1:8080;
}

如果test后带/,表示只会拦截/test或者/test/xxx等相关请求,

1
2
3
location /test/ {
proxy_pass http://127.0.0.1:8080;
}

proxy_pass中是否带uri的区别

  1. 目标地址中不带uri。即proxy_pass的参数形如http://127.0.0.1:8090。 表示只代理域名,此时新的目标url中,匹配的uri部分不做修改,原来是什么样就是什么样,会加到proxy_pass的参数上。(拼接
  2. 目标地址中带uri。即proxy_pass的参数形如http://127.0.0.1:8090/abc/xyz/,此时新的目标url中,匹配的uri部分将会被修改为该参数中的uri。(替换

下面以访问url http://192.168.10.1/proxy/test.html 为例。对proxy_pass设置不同的值,分析会访问到真正的url是什么。

1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1:81; //不带uri,实际访问到http://127.0.0.1:81/proxy/test.html。
}

1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1:81/; //带uri,实际访问到http://127.0.0.1:81/test.html。
}
1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1:81/abc/; //带uri,实际访问到http://127.0.0.1:81/abc/test.html。
}
1
2
3
location /proxy/ {
proxy_pass http://127.0.0.1:81/abc; //带uri,实际访问到http://127.0.0.1:81/abctest.html。
}

location中的proxy_set_header

X-Forwarded-For
在使用nginx做反向代理时,我们为了记录整个的代理过程,我们往往会在配置文件中做如下配置:

1
2
3
4
5
6
location / {
//省略...
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://1xx.xxx.xxx.xxx;

}

下面测试一下请求经过三层代理的情况。如下图

环境准备:

  • win10 在/etc/hosts文件中添加192.168.247.131 http://test.proxy.com
  • centos5.8,ip:192.168.247.131,安装nginx,把所有请求转发到192.168.247.132
  • centos6-1, ip:192.168.247.132,安装nginx,把所有请求转发到192.168.247.133
  • centos6-2, ip:192.168.247.133,安装nginx,把所有请求转发到云服务器
  • 在云服务器上的日志中打印http header中的X-Forwarded-For信息

3台nginx配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#centos5.8,ip:192.168.247.131 ,nginx.conf
location / {
root html;
index index.html index.htm index.php;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.247.132;
}

====================================================
#centos6-1,ip:192.168.247.132 ,nginx.conf
location / {
root html;
index index.html index.htm index.php;
#proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.247.133;
}

=======================================================
#centos6-2,ip:192.168.247.133 ,nginx.conf
location / {
root html;
index index.html index.htm index.php;  
#proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://123.206.96.111;
}

云服务器方便起见在日志中设置打印$http_x_forwarded_for,进行观察

1
2
3
log_format  main  '$http_x_forwarded_for|$http_x_real_ip|$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

基于上面的配置在win10浏览器输入:”http://test.proxy.com" 查看云服务器日志打印结果如下:

1
192.168.247.1, 192.168.247.131, 192.168.247.132|192.168.247.1|101.254.182.6 - - [22/May/2017:18:20:27 +0800] "GET /admin/login/?next=%2Fadmin%2F HTTP/1.0" 200 623 "http://test.proxy.com/admin/login/?next=%2Fadmin%2F" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36" "192.168.247.1, 192.168.247.131, 192.168.247.132"

结论:192.168.247.1, 192.168.247.131, 192.168.247.132 为$http_x_forwarded_for内容,显然记录了代理过程,其中192.168.247.1是客户端ip。

proxy_pass中配置upstream和ip+端口

proxy_pass中设置请求转发的url时,既可以使用upsteam的方式,也可以使用ip+端口的方式。

  1. proxy_pass + upstream

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    upstream foo.example.com {
    server 127.0.0.1:8001;
    }

    server {
    listen 80;
    server_name localhost;

    location /foo {
    proxy_pass http://foo.example.com;
    }
    }

    访问 http://localhost/foo ,proxy模块会将请求转发到127.0.0.1的8001端口上。

  2. 只有proxy_pass,没有upstream

    1
    2
    3
    4
    5
    6
    7
    8
    server {
    listen 80;
    server_name localhost;

    location /foo {
    proxy_pass http://foo.example.com;
    }
    }

    实际上是隐式创建了upstream,upstream名字就是foo.example.com。upstream模块利用本机设置的DNS服务器(或/etc/hosts),将foo.example.com解析成IP,访问http://localhost/foo,proxy模块会将请求转发到解析后的IP上。

    如果本机未设置DNS服务器,或者DNS服务器无法解析域名,则nginx启动时会报类似如下错误:

    1
    nginx: [emerg] host not found in upstream "foo.example.com" in /path/nginx/conf/nginx.conf:110
  3. proxy_pass 直接指定IP加端口号

    1
    2
    3
    4
    5
    6
    7
    8
    server {
    listen 80;
    server_name localhost;

    location /foo {
    proxy_pass http://127.0.0.1:8001/;
    }
    }

    实际上是隐式创建了upstream,proxy_pass会将请求转发到127.0.0.1的8001端口上。

------ 本文完 ------