nginx 基础备忘
Nginx 是一个高性能的HTTP和反向代理服务器,Nginx 专为性能优化而开发,支持 Epoll 模型,能经受高负载的考验,有报告表明能支持高达 50,000个并发连接数。Nginx 具有很高的稳定性,其它HTTP服务器当遇到访问的峰值,或者有人恶意发起慢速连接时,也很可能会导致服务器物理内存耗尽频繁交换,失去响应只能重启服务器.例如当前Apache一旦上到200个以上进程,web响应速度就明显非常缓慢了。而Nginx采取了分阶段资源分配技术,使得它的CPU与内存占用率非常低。Nginx官方表示保持10,000个没有活动的连接,它只占2.5M内存,所以类似DOS这样的攻击对Nginx来说基本上是毫无用处的。Nginx支持热部署,它的启动特别容易, 并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动。
# 配置文件
- 全局块:配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。
- event 块:配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。epoll
- http 块:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。
- http.server 块:配置虚拟主机的相关参数,一个http中可以有多个server。
- http.server.location 块:配置请求的路由,以及各种页面的处理情况。
#user nobody; # nginx用户及组,如果用户和组名一样可只写一个
worker_processes 1; # 定义了nginx对外提供web服务时的worker进程数。
# 最优值取决于许多因素,包括(但不限于)CPU核的数量、存储数据的硬盘 数量及负载模式。
# 不能确定的时候,将其设置为可用的CPU核心数将是一个好的开始(设置为“auto”将尝试自动检测它)
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024; # 每个进程的最大连接数
}
http {
include mime.types; # 文件扩展名与文件类型映射表
default_type application/octet-stream;
server_tokens off; # 隐藏软件版本号
#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; # 定义日志文件
sendfile on; # 开启高效文件传输模式
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65; # 长连接超时时间
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html; # $document_root目录
index index.html index.htm;
}
#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;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
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
# 虚拟主机
虚拟主机技术可以将网络上一台计算机分成多个虚拟主机,每个虚拟主机可以独立对外提供www服务,这样就以实现一台主机对外提供多个web服务
虚拟主机可以使得多个网站运行在同一台服务器上,从而节省硬件资源和成本。
同时,虚拟主机也提供了更灵活的配置方式,可以针对不同的网站设置不同的Nginx选项和限制
每个虚拟主机之间是独立的,互不影响。
# 基于域名
基于域名的虚拟主机:所有主机的套接字完全一样,仅以客户端访问的域名进行区分
# 虚拟主机 1
server {
listen 80 default_server; # default_server:默认主机
server_name www.zc.com;
access_log /www/zc/logs/access.log main;
error_log /www/zc/logs/error.log;
location / {
root /www/zc;
index index.html index.htm;
}
}
# 虚拟主机 2
server {
listen 80;
server_name blog.zc.com;
access_log /www/blog/logs/access.log main;
error_log /www/blog/logs/error.log;
location / {
root /www/blog;
index index.html index.htm;
}
}
# 虚拟主机 3
server {
listen 80;
server_name cloud.zc.com;
access_log /www/cloud/logs/access.log main;
error_log /www/cloud/logs/error.log;
location / {
root /www/cloud;
index index.html index.htm;
}
}
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
# 基于IP
每个主机套接字中的IP 不一样,以IP来区分
解析时将不同的域名解析到对应的IP上,要求服务器要有多个IP
server {
listen 192.168.10.11:80;
server_name web.11.com;
location / {
root /web11/html;
index index.html index.htm;
}
}
server {
listen 192.168.10.12:80;
server_name web.12.com;
location / {
root /web12/html;
index index.html index.htm;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 基于端口
每个主机套接字中的端口不一样,以端口来区分,在访问时要带端口
server {
listen 1111;
server_name web.11.com;
location / {
root /www/11/html;
index index.html index.htm;
}
}
server {
listen 1212;
server_name web.12.com;
location / {
root /www/12/html;
index index.html index.htm;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 访问控制
用户认证
location / {
root html;
index index.html index.htm;
auth_basic "user&pass"; # 基本认证
auth_basic_user_file /usr/local/nginx/passwd.db; # 存放用户名和密码的文件
}
2
3
4
5
6
访问控制
方法一:deny/allow
控制逻辑: 从上到下,匹配到即执行相应的策略
location / {
root html;
index index.html index.htm;
allow 192.168.10.0/24; # 仅允许这个 ip 段访问
deny all;
}
2
3
4
5
6
方法二:黑/白名单
可以使用geo模块定义一个访问控制的黑/白名单
geo指令可以根据客户端的IP地址来给变量定义不同的值,语法如下:
geo $black_list {
default 0;
192.168.10.11 1;
127.0.0.1 1;
}
server {
listen 80;
server_name www.qf.com;
root /usr/share/nginx/html;
if ($black_list) { # 禁止 192.168.10.11 和 127.0.0.1 访问
return 403;
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
速度限制
我们可以利用 geo 和 map 方便的实现分段限速:
...
geo $limit {
default 2;
192.168.10.11 1;
192.168.10.13 0;
}
map $limit $rate {
2 128k;
1 1m;
0 0;
}
server {
...
location /download/ {
autoindex on; # 当目录中没有 index文件时列出文件列表, 默认为 off
limit_rate $rate;
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
limit_rate_after指令:
就是在下载多少内容后再以limit_rate限制的速率下载,多用于浏览视频
location /flv/ {
limit_rate_after 500k;
limit_rate 50k;
}
2
3
4
# 限流
流量限制可以用作安全目的,比如可以减慢暴力密码破解的速率。通过将传入请求的速率限制为真实用户的典型值,并标识目标URL地址(通过日志),还可以用来抵御 DDOS 攻击。
更常见的情况,该功能被用来保护上游应用服务器不被同时太多用户请求所压垮。
# nginx限流配置
ngx_http_limit_req_module
模块提供限制请求处理速率能力, 该模块使用了漏桶算法(leaky bucket)。
漏桶算法思路很简单: 水加入到漏桶里(请求入队),漏桶以固定的速度出水(处理请求),当水加的过快,则会直接溢出(拒绝请求),可以看出漏桶算法能强行限制数据的传输速率。
我们可以使用 limit_req_zone
和 limit_req
两个指令,限制单个IP的请求处理速率。
配置方法:
配置
limit_req_zone
Syntax: limit_req_zone key zone=name:size rate=rate [sync]; Default: — Context: http
1
2
3例如:在http块中进行以下配置
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
1说明:
key :定义限流对象,$binary_remote_addr 是一种key,表示基于 remote_addr(客户端IP) 来做限流,binary_ 的目的是压缩内存占用量。
zone:定义共享内存区来存储访问信息, one:10m 表示一个大小为10M,名字为one的内存区域。 1M能存储16000 IP地址的访问信息,10M可以存储16W IP地址访问信息。
rate: 用于设置最大访问速率,rate=10r/s 表示每秒最多处理10个请求。
Nginx 实际上以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制:每100毫秒处理一个请求。
这意味着,100毫秒内,一个请求处理完后,若后续又有请求到达,将拒绝处理该请求。
limit_req
Syntax: limit_req zone=name [burst=number] [nodelay | delay=number]; Default: — Context: http, server, location
1
2
3例: 在http/server/location块加入以下配置:
server { ... location /search/ { root /www; index index.html; limit_req zone=one; } ... }
1
2
3
4
5
6
7
8
9处理突发流量
上面例子限制 10r/s,如果有时正常流量突然增大,超出的请求将被拒绝,无法处理突发流量,可以结合 burst 参数使用来解决该问题。
如: limit_req zone=one burst=20;
1burst 译为突发、爆发,表示在超过设定的处理速率后能额外处理的请求数。当 rate=10r/s 时,将1s拆成10份,即每100ms可处理1个请求。
# 限制连接数
ngx_http_limit_conn_module
提供了限制连接数的能力,利用 limit_conn_zone
和 limit_conn
两个指令即可。
官方例子:
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
...
limit_conn perip 10;
limit_conn perserver 100;
...
}
2
3
4
5
6
7
8
说明:
limit_conn perip 10
作用的key 是 $binary_remote_addr
,表示限制单个IP同时最多能持有10个连接。
limit_conn perserver 100
作用的key是 $server_name
,表示虚拟主机(server) 同时能处理并发连接的总数。
需要注意的是:只有当 request header 被后端server处理后,这个连接才进行计数。
# 第三方模块
Nginx 的第三方模块丰富多样,扩展了它的功能和灵活性。以下是一些常用的 Nginx 第三方模块:
- ngx_http_substitutions_filter_module:允许在响应内容中进行字符串替换,可以用来修改返回的 HTML 内容。
- ngx_http_headers_more_module:扩展了对 HTTP 头的控制,可以添加、修改或删除请求和响应的头信息。
- ngx_brotli:支持 Brotli 压缩算法,这是一种比 gzip 更高效的压缩方式。
- ngx_pagespeed:由 Google 开发,用于自动优化网页性能,减少页面加载时间。
- ngx_cache_purge:用于手动清除 Nginx 缓存的模块,可以通过 HTTP 请求清理特定 URL 的缓存。
- ngx_http_auth_ldap_module:用于通过 LDAP 进行用户认证,适用于集成 LDAP 用户验证的场景。
- ngx_http_lua_module:允许使用 Lua 脚本扩展 Nginx 的功能,可以编写自定义逻辑和处理复杂的请求。
- ngx_http_geoip_module:基于 GeoIP 数据库,通过 IP 地址定位用户的地理位置,可用于做地理位置相关的内容分发。
- ngx_http_redis_module:支持将 Redis 作为后端缓存服务器的模块,可以与 Redis 集成来实现缓存功能。
- ngx_http_image_filter_module:支持图像处理功能,如调整大小、裁剪和转换图像格式,适合动态处理图像的场景。
# location
http.server.location 块:配置请求的路由,以及各种页面的处理情况。
location用于匹配客户请求uri
语法:
location [ = | ~ | ~* | ^~ ] uri { ... }
参数 | 解释 |
---|---|
空 | location 后没有参数直接跟着 标准 URI,表示前缀匹配,代表跟请求中的 URI 从头开始匹配。 |
= | 用于标准 URI 前,要求请求字符串与其精准匹配,成功则立即处理,nginx停止搜索其他匹配。 |
^~ | 用于标准 URI 前,并要求一旦匹配到就会立即处理,不再去匹配其他的那些个正则 URI,一般用来匹配目录 |
~ | 用于正则 URI 前,表示 URI 包含正则表达式, 区分大小写 |
~* | 用于正则 URI 前, 表示 URI 包含正则表达式, 不区分大小写 |
@ | @ 定义一个命名的 location,@ 定义的locaiton名字一般用在内部定向,例如error_page, try_files命令中。它的功能类似于编程中的goto。 |
=
表示精准匹配,比如location = /test { return 200 "hello"; } # /test ok # /test/ not ok # /test2 not ok # /test/2 not ok
1
2
3
4
5
6
7
8~
表示区分大小写的正则匹配,比如:location ~ ^/test$ { [ configuration ] } # /test ok # /Test not ok # /test/ not ok # /test2 not ok
1
2
3
4
5
6
7
8~*
表示不区分大小写的正则匹配location ~* ^/test$ { [ configuration ] } # /test ok # /Test ok # /test/ not ok # /test2 not ok
1
2
3
4
5
6
7
8^~
表示 uri 以某个字符串开头location ^~ /images/ { [ configuration ] } # /images/1.gif ok
1
2
3
4
5
当你不使用这些语法的时候,只写 uri 的时候:
/
表示通用匹配:location / { [ configuration ] } # /index.html ok location /test { [ configuration ] } # /test ok # /test2 ok # /test/ ok
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
匹配顺序
location 的定义分为两种:
- 前缀字符串(prefix string)
- 正则表达式(regular expression),具体为前面带
~*
和~
修饰符的
检查使用前缀字符串的 locations,在使用前缀字符串的 locations 中选择最长匹配的,并将结果进行储存
如果符合带有
=
修饰符的 URI,则立刻停止匹配如果符合带有
^~
修饰符的 URI,则也立刻停止匹配。然后按照定义文件的顺序,检查正则表达式,匹配到就停止
当正则表达式匹配不到的时候,使用之前储存的前缀字符串
简单总结就是优先级从高到低依次为(序号越小优先级越高):
1. location = # 精准匹配
2. location ^~ # 带参前缀匹配
3. location ~ # 正则匹配(区分大小写)
4. location ~* # 正则匹配(不区分大小写)
5. location /a # 普通前缀匹配,优先级低于带参数前缀匹配。
6. location / # 任何没有匹配成功的,都会匹配这里处理
2
3
4
5
6
# alias
alias
指令用于定义指定路径的替换路径。
如下面配置:
location /i/ {
alias /data/w3/images/;
}
2
3
“/i/top.gif” 将由 /data/w3/images/top.gif 文件来响应。
root 与 alias 的区别
当我们这样设置 root
的时候:
location /i/ {
root /data/w3;
}
2
3
当请求 /i/top.gif
,/data/w3/i/top.gif
会被返回。
当我们这样设置 alias
的时候:
location /i/ {
alias /data/w3/images/;
}
2
3
当请求 /i/top.gif
,/data/w3/images/top.gif
会被返回。
乍一看两者很像,但细一看,就能看出两者的区别,root 是直接拼接 root
+ location
而 alias 是用 alias
替换 location
,所以 root 中最后的路径里有 /i/
,而 alias 中最后的路径里没有 /i/
。
server 和 location 中的 root
http {
server {
listen 80;
server_name www.yayujs.com;
root /home/www/website/; # <--
location / {
root /home/www/ts/; # <--
index index.html;
}
}
}
2
3
4
5
6
7
8
9
10
11
如果两者都出现,是怎样的优先级呢?
简单的来说,就是就近原则,如果 location 中能匹配到,就是用 location 中的 root 配置,忽略 server 中的 root,当 location 中匹配不到的时候,则使用 server 中的 root 配置。
# URI 重写
Rewrite 是Nginx提供的一个重要基本功能,其在Web服务器产品中几乎是必备的功能,用于实现URL的重写。
URL重写是指将一个URL请求重新写成网站可以处理的另一个URL的过程。
rewrite的主要功能是实现URL地址的重定向。Nginx的rewrite功能需要PCRE软件的支持,即通过perl兼容正则表达式语句进行规则匹配的。
rewrite和location的功能有点相像,都能实现跳转,主要区别在于rewrite常用于同一域名内更改获取资源的路径,而location是对一类路径做控制访问和反向代理,可以proxy_pass到其他服务器。
Nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。 rewrite只能放在server{},location{},if{}中, 并且只能对域名后边的除去传递的参数外的字符串起作用。
URL 重写语法:
rewrite <regex> <replacement> [flag];
关键字 正则 替代内容 flag标记
2
regex :可以使用正则或者字符串来表示相匹配的地址。
replacement:可以表示重定向的地址。
flag :flag标志的作用是用于控制当匹配到对应的rewrite规则后是否继续检查后续的rewrite规则。
flag值为如下四种,分别是:
- last:停止处理当前的rewrite指令集,而后通过重写后的规则重新发起请求,浏览器地址栏URL地址不变。
- break:和break指令一样,都是停止处理当前上下文中的其他重写模块指令。
- redirect:如果替换字符串不以“ http://”,“ https://”或“ $scheme” 开头,返回带有302代码的临时重定向,浏览器地址会显示跳转后的URL地址。
- permanent:返回301代码的永久重定向,浏览器地址栏会显示跳转后的URL地址。
# 防盗链
要实现防盗链,需要了解HTTP协议中的请求头的Referer头域。
通过该头域的值,可以检测到访问目标资源的源地址。
这样,如果我们检测到Referer头域中的值并不是自己站点内的URL时就采取阻止措施,实现防盗链。
但是要提醒大家的是, 任何HTTP协议头都会有可能被篡改,因此这种方法并不能完全的阻止盗链行为。
如何配置防盗链:
Nginx的ngx_http_referer_module
模块中提供了一个指令valid_referers
,用来获取Referer头域中的值
并根据该值的情况给Nginx全局变量$invalid_referer
赋值。 如果Referer头域中没有符合valid_referers
指令配置的值,$invalid_referer
变量将会被赋值为1。
语法如下:
valid_referers none | blocked | server_names | string ...;
- none:HTTP 头中不存在 Referer 头域
- blocked:存在Referer头域,但里面的值有可能由于防火墙或者代理服务器的原因被删除或者伪装。
- server_names: 设置一个或者多个域名, 检测Referer 头域的值是不是这些域名中的某个。支持使用通配符
*
示例:
server {
listen 80;
server_name yourdomain.com;
root /var/www/yourdomain.com;
# 允许的 referer
valid_referers none blocked yourdomain.com *.yourdomain.com;
location / {
# 检查是否是有效的 referer
if ($invalid_referer) {
return 403; # 返回403 Forbidden状态码
}
# 正常处理请求
try_files $uri $uri/ =404;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
配置说明:
- valid_referers:该指令指定允许的
Referer
来源。参数none
表示允许无Referer
的请求,blocked
表示允许无法读取Referer
的请求,yourdomain.com
和*.yourdomain.com
允许来自你自己域名的请求。 - **if (
invalid_referer` 变量为真),则返回 403 Forbidden 状态码,拒绝访问。 - try_files:用于处理合法的请求,检查文件是否存在,如果不存在则返回 404 错误。
通过这种配置,Nginx 可以有效防止其他网站直接引用你的网站资源(如图片、视频等)。
# 平滑升级
可以在不中断服务的情况下,新的请求也不会丢失,使用新的 nginx 可执行程序替换旧的版本
步骤:
编译新版本 Nginx
首先,需要下载并编译新版本的 Nginx,确保其二进制文件可以替换现有的版本。
./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --with... (其他参数) make
1
2注意:不要执行
make install
,否则会直接替换现有的 Nginx 二进制文件备份现有的二进制文件
以防出现问题需要会馆
cp /usr/sbin/nginx /usr/sbin/nginx.backup
1替换 Nginx 二进制文件
将新编译的 Nginx 二进制文件复制到现有 Nginx 二进制文件的位置。
发送 USER2 信号
发送
USR2
信号给 Nginx 主进程,触发 Nginx 创建新的工作进程(workers),以使用新二进制文件。kill -USR2 <master_process_id>
1执行后,Nginx 会将当前主进程的 PID 文件重命名,并生成一个新的 PID 文件(通常在
/usr/local/nginx/logs/nginx.pid
)。验证新进程
在新进程启动后,可以验证其状态,确保新版本工作正常。如果一切正常,可以停止旧进程。
kill -WINCH <old_master_process_id>
1上述命令会停止旧的工作进程,新的进程将完全接管。
(可选) 回滚到旧版本:
如果新版本出现问题,可以发送
HUP
信号回滚到旧版本。mv /usr/sbin/nginx.backup /usr/sbin/nginx kill -HUP <new_master_process_id>
1
2
📢 注意事项:
确保在进行升级前备份好现有的 Nginx 二进制文件和配置文件。
平滑升级操作通常不会中断现有连接,但确保在流量较少的时间段进行操作以减少风险。
在升级前,可以通过
nginx -t
检查配置文件语法,避免因配置错误导致升级失败。
# ⭐️反向代理
代理(Proxy)也称网络代理。它是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这 个服务与另一个网络终端(一般为服务器)进行非直接的连接。
一些网关、路由器等网络设备具备网络代理功能。
一般认为代理服务有助于保障网络终端的隐私或安全,防止攻击。代理通常分为正向代理、反向代理及透明代理。
正向代理:
目的:内网的服务器通过代理服务器,然后能够访问外网的服务器
原理:内网用户将请求发给代理服务器,代理服务器根据用户需求,向真正的web服务器发出请求,然后获取到网页内容之后,在本地缓存然后发给用户。
透明代理服务器
目的和原理与正向代理服务器一致,但一般布署在网关上,用户不需要再对浏览器进行设置
反向代理
目的:外网客户端通过代理服务器,能够访问内网服务器的资源
原理:外网客户端访问正常的域名或者IP,其实访问的是代理服务器,代理服务器帮助客户端请求页面,在代理服务器上缓存,然后再发送给客户端。
# 七层代理
nginx反向代理功能由 ngx_http_proxy_module 模块提供
配置反向代理
location / { proxy_pass http://192.168.10.12; }
1
2
3以上配置将所有的访问请求代理到后端服务器 http://192.168.10.12 上
当我们配置好反向代理后,使用客户端访问代理服务器,将能获取后端真实服务器的内容
真实IP
配置代理服务器将客户端 IP 传递给后端服务器
查看后端服务器的访问日志,我们发现日志中记录的客户端 IP 并非真实客户端的 IP,而是代理服务器的 IP 。显然,记录这个 IP 是没有意义的。
由于后端服务器和客户端并没有直接的通信,所以客户端的真实 IP 只能通过代理服务器传递给后端服务器。我们可以在代理服务器上将客户端的 IP 封装到请求报文中发送给后端服务器。
location / { proxy_pass http://webs; proxy_set_header X-Real-IP $remote_addr; # 将 $remote_addr 的值封装到请求报文头部的 X-Real-IP 字段中 }
1
2
3
4配置后端 web 服务器记录下客户端真实 IP
虽然经过以上第1步配置,代理能够将客户端真实 IP 传递过来了,但默认情况下后端服务器并不会在日志中记录请求头部 X-Real-IP 字段的值。
简单配置
如果后端为nginx,有两种方法可实现最简单的方法就是直接修改 log_format ,将
$remote_addr
改为 $http_x_real_ip 即可上述配置方法最为简单,但有一个问题,客户端直接访问(不通过代理服务器)时没有IP
使用模块处理
nginx服务器可通过 ngx_http_realip_module 模块获取真实 IP
server { ... set_real_ip_from 代理服务器IP; real_ip_header X-Real-IP; ... }
1
2
3
4
5
6这样配置后,$remote_addr 的值将会从请求报文中的 X-Real-IP 字段中获取。
# proxy_pass 详解
proxy_pass是反向代理最核心的指令。代理时,后面如果有路径,将替换location部分;否则,将所有路径拼接到后面。
location /static/ {
proxy_pass http://192.168.10.12; # 后面不接任何路径,所有URL路径将被拼接到后面
}
# 将被代理到:http://192.168.10.12/static/index.html
location /static/ {
proxy_pass http://192.168.10.12/;
}
# http://192.168.10.12/index.html
location /static/ {
proxy_pass http://192.168.10.12/abc;
}
# 将被代理到:http://192.168.10.12/abcindex.html
location /static/ {
proxy_pass http://192.168.10.12/abc/;
}
# 将被代理到:http://192.168.10.12/abc/index.html
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 四层代理
nginx 也支持做四层代理,不过实际应用较少,因为四层调度通常使用 LVS 来做
nginx 四层代理由 ngx_stream_proxy_module 模块提供
# 🌟负载均衡
该功能由 ngx_http_upstream_module + ngx_http_proxy_module 模块提供
当我们把后端服务器换成后端服务器组后,此代理服务器便成了负载均衡调度器
服务器组的配置
使用 upstream 指令配置服务器组,一个 http 块中可配置多个服务器组。
upstream backend {
server 192.168.10.11 weight=5;
server 127.0.0.1:8080 max_fails=3 fail_timeout=3s;
server 192.168.10.12 down;
server 192.168.10.13 backup;
}
# weight=N 定义权值,默认为1
# max_conns=N 限制最大连接数
# backup 备用服务器,当所有服务器都宕机后才会起用
# down 不起用的服务器
# max_fails=N 认为后端服务器失效的连接失败次数
# fail_timeout=Ns 心跳检测响应超时秒数,超过这个时间未响应则认为失败
2
3
4
5
6
7
8
9
10
11
12
13
配置负载均衡
server {
location / {
proxy_pass http://backend; # 将请求代理到服务器组中,即负载均衡
}
}
2
3
4
5
# 调度算法
轮询
加权轮询
upstream backend { server backend1.example.com weight=3; server backend2.example.com weight=1; server backend3.example.com weight=2; }
1
2
3
4
5ip_hash
upstream backend { ip_hash; server backend1.example.com; server backend2.example.com; server backend3.example.com; }
1
2
3
4
5
6least_conn 最少连接数
将请求传递到活动连接数最少的服务器,同时考虑服务器的权重。
upstream backend { least_conn; server backend1.example.com; server backend2.example.com; server backend3.example.com; }
1
2
3
4
5
6公平(Fair)
将请求分配给响应时间最短的服务器,适合对响应时间要求较高的场景。
需要第三方模块
ngx_http_upstream_fair_module
。URL 哈希(URL Hash)
通过计算请求 URL 的哈希值,将相同 URL 的请求分配给同一台后端服务器。
适用于缓存服务器,确保相同 URL 的请求命中相同的缓存。
需要第三方模块
ngx_http_upstream_hash_module
。
# 缓存
缓存一般存的是静态资源
缓存可以提高客户端的访问速度,并能减轻服务器的压力
我们分别从客户端缓存及服务器端缓存两个场景去分析
客户端缓存
通过设置 expires 指令,响应头中将会返回Expires 和Cache-Control字段。
当浏览器发现响应头存在这样的缓存字段,当再次请求相同资源时,就会确认在客户端的资源是否过期。浏览器在不强制刷新的情况下可使用有效期内的缓存。
location /images/ { root /data/www; expires 30d; }
1
2
3
4服务器缓存(主要设置反向代理服务器)
如果我们能将静态资源的缓存设置在服务器端,当多个用户访问同一个资源时,缓存命中率及系统的性能将以指数的形式提升。
用于缓存从后端服务器(如应用服务器或其他 HTTP 服务器)获取的响应数据。适合缓存动态内容和外部服务的响应。
http { proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g; server { location / { proxy_cache my_cache; proxy_pass http://backend; proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m; } } }
1
2
3
4
5
6
7
8
9
10
11proxy_cache_path
:指定缓存的路径和一些其他参数,缓存的数据存储在文件中,并且使用代理url的哈希值作为关键字与文件名。levels
- 目录结构,可以使用任意的1位或2位数字作为目录结构,如 X, X:X或X:X:X ,最多三级。keys_zone
- 指定所有活动的key和元数据存储的共享内存池区域。 10分钟过期max_size
- 定义最大缓存大小,超出后则删除最少使用的数据
proxy_cache
:设置一个缓存区域的名称,一个相同的区域可以在不同的地方使用。proxy_cache_valid
:为应答代码为200和302的设置缓存时间为10分钟,404代码缓存1分钟。
# 状态码问题
401 授权的问题 404 页面找不到 403 访问被拒绝
nginx的500,502,504错误
500错误:
500错误指的是服务器内部错误,也就是服务器遇到意外情况,而无法履行请求.
500错误一般有几种情况:
- 程序语法错误
- 访问量过大
- 服务器配置问题
- 数据库、缓存不可用
500错误分析思路:
查看 nginx 错误日志
connect() failed (111: Connection refused)
:表示 Nginx 无法连接到后端服务器,可能是后端服务未启动或端口错误。upstream timed out (110: Connection timed out)
:表示请求到达后端服务器时超时,可能是后端服务响应缓慢或负载过高。检查后端服务器
后端服务状态:确认后端服务是否正常运行,可以通过访问后端服务的健康检查 URL 或直接访问后端服务来测试其可用性。
网络连接:检查 Nginx 和后端服务器之间的网络连接,确保它们之间的通信正常。可以使用
telnet
或curl
命令来测试连接。负载均衡配置
权重配置:检查负载均衡器中各个服务器的权重是否配置正确,确保负载均衡算法没有问题。
健康检查:确保 Nginx 配置了后端服务器的健康检查,避免将请求分发到不可用的服务器上。
资源限制
Nginx 系统资源限制:检查系统的资源限制(如内存、CPU、文件句柄数),看看是否达到了瓶颈。Nginx 有时可能因为系统资源不足而无法正常处理请求。
连接数限制:检查 Nginx 的连接数限制和后端服务的连接数限制,确保没有因为连接数过多导致请求失败。
其他的潜在问题
缓存问题:如果使用了缓存(如
proxy_cache
),可能是缓存配置导致了问题。可以通过禁用缓存来测试。SSL/TLS问题:如果使用了 SSL/TLS 终止,检查证书和 SSL 配置是否正确。
502 错误(Bad Gateway)
通常表示 Nginx 无法与后端服务器建立正确的连接或接收到有效的响应。以下是一些常见的排查思路和步骤:
502错误发生在nginx为代理服务器的情况下,如上游应用服务器不可用,调度无法完成就会报502错误。 502错误从上游服务器找原因
解决503错误 限流
503 Service Temporarily Unavailable错误
表示服务器暂时无法处理请求。这通常是因为服务器过载、维护或暂时不可用。以下是 503 错误的常见原因和对应的排查思路:
单个ip并发设置过小会导致503报错
- 服务器过载
- 服务器维护和升级,暂时下线
- 后端服务不可用
- 资源耗尽
- 限流策略
# 动静分离
前端使用 VUE,后端使用 Spring Boot。
通常情况下,Vue 应用是一个单页应用程序(SPA),经过构建后生成静态文件(如 HTML、CSS、JavaScript),这些静态文件可以直接由 Nginx 提供,而动态请求则通过 Nginx 代理到 Spring Boot 处理。
假设 Vue 项目构建后的静态文件位于 /var/www/vue-app
,Spring Boot 应用运行在 http://localhost:8080
。
server {
listen 80;
server_name example.com;
# 静态文件(Vue 应用)
location / {
root /var/www/vue-app;
try_files $uri $uri/ /index.html;
}
# 动态请求(Spring Boot API)
location /api/ {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
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 $scheme;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CORS 配置:如果前端和后端不在同一个域下,需要配置跨域资源共享(CORS)。
# 日志轮转
日志轮转(Log Rotation)是管理日志文件的过程,旨在防止日志文件无限增长,占用过多磁盘空间。它涉及定期将当前的日志文件归档并创建新的日志文件。这通常是通过以下步骤完成的:
- 归档:
- 当前的日志文件被重命名或移动到一个存档位置,通常会加上时间戳或序列号。例如,
app.log
可能被重命名为app.log.2024-08-17
。
- 当前的日志文件被重命名或移动到一个存档位置,通常会加上时间戳或序列号。例如,
- 创建新日志文件:
- 在归档后,系统会创建一个新的日志文件,继续记录新的日志数据。这可以是新的
app.log
文件。
- 在归档后,系统会创建一个新的日志文件,继续记录新的日志数据。这可以是新的
- 管理和删除旧日志:
- 旧的日志文件可能会被压缩以节省空间,或者根据设定的策略(如保留一定天数或文件数量)被删除。
日志轮转的主要好处:
- 防止磁盘耗尽:定期轮转日志文件可以防止日志文件占用过多磁盘空间。
- 提高性能:较小的日志文件可以提高系统的写入和读取性能。
- 简化日志管理:将日志文件归档到单独的文件或目录中,便于日志分析和管理。
# Logrotate
创建一个配置文件,例如 /etc/logrotate.d/nginx
,并添加如下内容:
/var/log/nginx/*.log {
daily # 每天轮转日志。
missingok
rotate 14 # 保留最近 14 个归档日志
compress # 压缩
delaycompress # 延迟压缩上一个轮转周期的日志文件
notifempty # 如果日志文件为空,则不轮转
create 0640 www-data adm # 创建新的日志文件时设置权限和所有者
sharedscripts
postrotate # 在日志轮转后,发送信号给 Nginx 重新打开日志文件
if [ -f /run/nginx.pid ]; then
/bin/kill -USR1 `cat /run/nginx.pid`
fi
endscript
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 自定义脚本
可以编写自定义脚本来处理 Nginx 日志轮转。这些脚本可以定期检查日志文件的大小,并在需要时轮转日志文件。
使用 cron 定期执行
下面这个脚本会检查日志文件的大小(一个中等流量网站每天流量几个 G),如果超过指定阈值,它将重命名当前的日志文件,创建一个新的日志文件,并重新加载 Nginx 以使其开始写入新的日志文件。
#!/bin/bash
# 配置
LOG_DIR="/var/log/nginx"
MAX_SIZE=10485760 # 最大日志文件大小,单位字节(这里是10MB)
DATE=$(date +'%Y-%m-%d')
PID_FILE="/run/nginx.pid"
# 循环处理每个日志文件
for LOG_FILE in $LOG_DIR/*.log; do
if [ -f "$LOG_FILE" ]; then
FILE_SIZE=$(stat -c%s "$LOG_FILE")
if [ "$FILE_SIZE" -gt "$MAX_SIZE" ]; then
# 归档当前日志文件
mv "$LOG_FILE" "$LOG_FILE.$DATE"
# 创建新的空日志文件
touch "$LOG_FILE"
# 重新加载 Nginx 配置
if [ -f "$PID_FILE" ]; then
kill -USR1 $(cat "$PID_FILE")
fi
fi
fi
done
sudo crontab -e
0 1 * * * /usr/local/bin/nginx-logrotate.sh
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
⭐️使用 logrotate
是最常见和推荐的方案,因为它功能强大且易于配置。日志收集使用 ELK,同时使用logrotate
或者自定义脚本定时清理 Nginx 的原始日志。
日志切割和日志轮转:日志切割指的是将一个大的日志文件分成多个较小的文件。日志轮转是一种更为全面的日志管理策略,涉及日志文件的归档、压缩和删除,以防止日志文件无限增长。
日志保存策略:
- 短期保存:通常保存最近的几天或几周的数据,适用于实时监控和故障排查。
- 长期存档:将日志数据归档到低成本存储中,保存较长时间(如一年或更长),以满足合规性或长期分析需求。
日志清理:
- 定期清理旧的日志数据,以保持系统的性能和可用空间。可以通过配置 Logstash、Elasticsearch 或使用外部脚本来实现。
# 配置https
安装ssl 模块
./nginx -V ./configure ... --with-http_ssl_module make
1
2
3放置ssl 证书
解压缩下载好的证书(证书一般是pem文件和key文件)
进行nginx.conf配置
http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { #监听443端口 listen 443; #你的域名 server_name huiblog.top; ssl on; #ssl证书的pem文件路径 ssl_certificate /root/card/huiblog.top.pem; #ssl证书的key文件路径 ssl_certificate_key /root/card/huiblog.top.key; location / { proxy_pass http://公网地址:项目端口号; } } server { listen 80; server_name huiblog.top; #将请求转成https rewrite ^(.*)$ https://$host$1 permanent; }
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重启nginx
./nginx -s reload