一个计算机技术爱好者与学习者

0%

好好学Nginx:Nginx入门篇

1. Nginx是什么?

Nginx (engine x) 是一个高性能的HTTP静态页面服务器,更是一个常用的反向代理服务器,同时还可以作为IMAP/POP3/SMTP代理服务器。

经常使用Nginx服务器,进行一些简单配置,但只是从网上照抄,知其然不知其所以然。
本文,我们主要学习一下nginx的目录结构和基础规则,补一补nginx基础。

2. 安装Nginx

linux中安装nginx,参考文档《CentOS7设置Nginx开机自启动》

docker安装nginx,参考文档《使用Docker安装配置Nginx》

3. 目录结构

因为nginx目录是可以指定的,所以真实使用的目录结构请以命令查看。

1
ps aux | grep nginx

3.1. 程序目录

可能的程序目录:

1
/usr/sbin/nginx

3.2. 配置目录

可能的默认配置目录:

1
2
/etc/nginx/
/etc/nginx/conf

nginx.conf一般放在这两个目录中,nginx.conf中会写清楚子配置目录,比如:

1
2
3
http {
include /etc/nginx/conf.d/*.conf;
}

那么,子配置文件就放在 /etc/nginx/conf.d/ 目录中,并且子配置文件必须以 .conf 结尾。一般情况下,我们比较少修改 nginx.conf,大部分时候都是修改子配置文件。

3.3. 根目录

可能的默认根目录:

1
/usr/share/nginx/html

根目录下一般会有nginx自带的index.html和50x.html。

curl ${nginx_server}时看到内容,就是index.html文件中的内容。

我们自己的静态页面项目,最好不要放到默认根目录下,因为默认根目录可以通过ip和路径访问到,这往往是不符合预期的。
推荐的静态页面项目目录为:/usr/share/nginx/work

3.4. 日志目录

可能的默认日志目录:

1
/var/log/nginx

日志文件中一般有两个日志文件,error.log和access.log。

4. 常用命令

4.1. 测试配置

1
nginx -t

4.2. 重新加载配置

1
nginx -s reload

4.3. 重启nginx

1
2
3
4
5
systemctl restart nginx
# or
ps -ef | grep nginx
kill -9 xxx
/usr/sbin/nginx -c /etc/nginx/conf/nginx.conf

5. 常见配置

5.1. 静态页面服务器配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;
server_name www.voidking.com;
charset utf-8;

location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

root /usr/share/nginx/html/;
index index.html;
}
}

这个配置,是一个标准的静态页面服务器配置。
使用以上配置,如果已经配置好了域名解析,那么访问 www.voidking.com 时,就会看到nginx首页,也就是 index.html。

5.2. 反向代理到其他域名

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name www.voidking.com;
charset utf-8;

location / {
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_pass http://voidking.coding.me;
}
}

使用以上配置,访问 www.voidking.com 时,实际上看到的是 voidking.coding.me 这个域名返回的内容。

5.3. 反向代理到一个服务

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name www.voidking.com;
charset utf-8;

location / {
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_pass http://192.168.56.101:8080;
}
}

使用以上配置,访问 www.voidking.com 时,实际上看到的是 192.168.56.101:8080 这个服务返回的内容。

5.4. 反向代理到upstream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
upstream www_voidking_com{
server 192.168.56.101:8080 weight=5;
server 192.168.56.102:8081 weight=2;
server 192.168.56.103:8000;
}

server {
listen 80;
server_name www.voidking.com;
charset utf-8;

location / {
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_pass http://www_voidking_com;
}
}

使用以上配置,访问 www.voidking.com 时,实际上看到的是upstream里的服务返回的内容。
但是问题来了,upstream里有三个服务,到底是哪个服务返回的内容呢?答:不一定,这就要用到传说中的加权轮询算法了。
三个服务的权值分别为 5、2、1(默认),从权值来看,最大可能打到第一个服务。
有没有想到什么知识点?没错,负载均衡,使用了加权轮询算法的负载均衡。

5.5. 反向代理到websocket服务

Connection值硬编码:

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
http {
server {
listen 80;
server_name www.voidking.com;
charset utf-8;

location ^~ /ws {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

proxy_pass http://192.168.56.101:8080;
}

location / {
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_pass http://192.168.56.101:8080;
}
}
}

这种写法中,Connection的值直接被硬编码为upgrade。

动态设置Connection值:

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
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {
listen 80;
server_name www.voidking.com;
charset utf-8;

location ^~ /ws {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

proxy_pass http://192.168.56.101:8080;
}

location / {
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_pass http://192.168.56.101:8080;
}
}
}

这种写法中,Connection的值是变量$connection_upgrade。该变量的值是根据请求头中的$http_upgrade来决定的。如果$http_upgrade的值是默认值(即空),那么Connection的值会被设置为close,否则会被设置为upgrade。

更多内容参考一篇带给你Nginx代理WebSocket方法NGINX as a WebSocket Proxy

5.6. 配置域名证书

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
server {
listen 80;
listen 443 ssl;
server_name www.voidking.com;
charset utf-8;

ssl_certificate /etc/nginx/ssl/1_www.voidking.com_bundle.crt;
ssl_certificate_key /etc/nginx/ssl/2_www.voidking.com.key;
ssl_session_timeout 5m;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!ADH:!EXPORT56:RC4+RSA:+MEDIUM;
ssl_prefer_server_ciphers on;

if ($ssl_protocol = "") {
return 301 https://$host$request_uri;
}

location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

root /usr/share/nginx/work/voidking/;
index index.html;
}

error_page 404 /404.html;
location = /404.html {
root /usr/share/nginx/work/voidking/;
index 404.html;
}
location ~ /\.git {
return 404;
}
}

5.7. 四层代理

5.7.1. 加载stream模块

nginx四层代理依赖ngx_stream_core_module模块(下面简称stream模块)。

1
2
nginx -V &> nginx_version.txt
grep "stream" nginx_version.txt

如果看到--with-stream,表明已经安装并开启stream模块。

nginx1.22.0以上版本默认支持并开启stream模块,一般配置路径为:/usr/share/nginx/modules/ ,在 nginx.conf 中include该路径。

如果是低版本的nginx,需要自行编译安装stream模块,或者使用包管理器安装stream模块。

1
yum install -y nginx-mod-stream

安装完成后,nginx.conf中加载ngx_stream_module.so

1
2
#load_module /usr/lib/nginx/modules/ngx_stream_module.so;
load_module /usr/lib64/nginx/modules/ngx_stream_module.so;

5.7.2. 四层代理配置

一个四层代理的实例:

1
2
3
4
5
6
7
8
9
10
11
12
stream {
upstream remote_mysql {
server 192.168.1.1:3306;
}

server {
listen 3306 so_keepalive=on; # 开始TCP存活探测
proxy_connect_timeout 10s; # 连接超时时间
proxy_timeout 300s; # 端口保持时间
proxy_pass remote_mysql;
}
}

注意:stream是和http同一级的,不要配置到nginx/conf.d目录中。最好新建一个nginx/stream.d目录,专门放置四层代理配置。

5.8. 请求body大小

1
2
3
Syntax:     client_max_body_size size;
Default: client_max_body_size 1m;
Context: http, server, location

Sets the maximum allowed size of the client request body. If the size in a request exceeds the configured value, the 413 (Request Entity Too Large) error is returned to the client. Please be aware that browsers cannot correctly display this error. Setting size to 0 disables checking of client request body size.

参考文档ngx_http_core_module - client_max_body_size

5.9. 允许跨域

允许某个域名跨域:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name www.voidking.com;

add_header Access-Control-Allow-Origin 'test.voidking.com';
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods "GET, POST, PUT, OPTIONS";

...
}

允许所有域名跨域:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name www.voidking.com;

add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods "GET, POST, PUT, OPTIONS";

...
}

指定多个域名允许跨域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server {
listen 80;
server_name cdn.voidking.com;

location /search.xml {
if ($http_origin ~* (https?://(.+\.)?(voidking.com|www.voidking.com)$)) {
set $allowed_origin $http_origin;
}

add_header 'Access-Control-Allow-Origin' $allowed_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

root /usr/share/nginx/work/voidking/;
index search.xml;
}

...
}

5.10. 支持SNI转发

SNI(Server Name Indication)是一个TLS的扩展字段,对应nginx内置变量$ssl_preread_server_name,可用于访问不同域名跳转到不同的后端地址,实现根据域名四层代理。

此外,还有两个常用的TLS扩展字段:

  • Protocol Version:对应nginx内置变量是 $ssl_preread_protocol,可用于客户端不同TLS协议版本跳转到不同的后端地址,实现根据TLS协议版本四层代理。
  • ALPN(Application-Layer Protocol Negotiation),对应nginx内置变量是 $ssl_preread_alpn_protocols,可用于根据客户端不同应用层协议版本跳转到不同后端地址,实现根据应用层协议的四层代理。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
stream {
map $ssl_preread_server_name $name {
ssltest.voidking.com backend;
default backend2;
}

upstream backend {
server 127.0.0.1:8888;
}

upstream backend2 {
server 127.0.0.1:8889;
}

server {
ssl_preread on;
listen 443;
proxy_pass $name;
}
}

参考文档:

6. 配置文件优先级

conf.d中的配置文件,是按照名称顺序有优先级的,第一个匹配到的配置文件会作为default。
如果一个域名指向了nginx,但是nginx中没有这个域名的配置,那么就会走default配置。
详情参考《使用Docker安装配置Nginx》文中的【重定向问题排查】一节。

7. 基础规则

7.1. location规则

在常见配置中,我们看到了一个叫 location 的关键字。location规则为:

1
location [ = | ~ | ~* | ^~ ] /uri/ { ... }

紧跟在location后面的,是可选的修饰符,uri是要匹配的字符串。

常见匹配:

  • location = /uri 精确匹配
  • location ^~ /uri 前缀匹配,在正则匹配之前
  • location ~ pattern 正则匹配,区分大小写
  • location ~* pattern 正则匹配,不区分大小写
  • location /uri 前缀匹配,在正则匹配之后
  • location / 通用匹配,未匹配到其它location的请求会匹配到它

详情参考location 匹配规则

7.2. 匹配规则

匹配过程:
1、精确匹配
2、前缀匹配(^~
3、正则匹配(按配置顺序)
4、前缀匹配(/xxx
5、通用匹配

其中前缀匹配如果有包含关系时,遵循最大匹配原则。

7.3. proxy_pass规则

如果nginx作为反向代理使用,那么必须要搞明白的就是proxy_pass的规则,这关系到请求能否打到正确的接口。
proxy_pass规则:

  • 如果proxy_pass后面没有路径,那么转发时带上uri
  • 如果proxy_pass后面有路径,那么转发时使用该路径替换匹配到的uri

7.3.1. 保留uri

1
2
3
4
5
6
7
location  /test {
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_pass http://192.168.56.101:8080;
}

访问 www.voidking.com/test/index 会被代理到 http://192.168.56.101:8080/test/index 这个url,/test/index 被保留转发给了后端服务。

7.3.2. 去掉uri

1
2
3
4
5
6
7
location  /test {
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_pass http://192.168.56.101:8080/;
}

访问 www.voidking.com/test/index 会被代理到 http://192.168.56.101:8080/index 这个url,/test 被去掉了。

7.3.3. 替换uri

1
2
3
4
5
6
7
location  /test {
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_pass http://192.168.56.101:8080/qa/;
}

访问 www.voidking.com/test/index 会被代理到 http://192.168.56.101:8080/qa/index 这个url,/test/index 被替换成了 /qa/index,然后转发给了后端服务。

1
2
3
4
5
6
7
location  /test {
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_pass http://192.168.56.101:8080/qa;
}

访问 www.voidking.com/test/index 会被代理到 http://192.168.56.101:8080/qaindex 这个url,/test/index 被替换成了 /qaindex,然后转发给了后端服务。

8. 配置日志格式

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
http {
...
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';

#log_format main '$remote_addr - $remote_port [$time_local] '
# '"$http_user_agent" "$http_x_forwarded_for" '
# '$host $http_host $scheme '
# '"$http_referer" "$request" '
# '"$request_uri" "$request_method" '
# '$request_time $request_length '
# '$status $bytes_sent '
# '$body_bytes_sent '
# '$upstream_addr $upstream_response_time';

log_format main escape=json '{ "@timestamp": "$time_local", '
'"http_user_agent": "$http_user_agent",'
'"http_x_forwarded_for": "$http_x_forwarded_for",'
'"remote_addr": "$remote_addr",'
'"remote_port": "$remote_port",'
'"host": "$host",'
'"http_host": "$http_host",'
'"scheme": "$scheme",'
'"http_referer": "$http_referer",'
'"request": "$request",'
'"request_uri": "$request_uri",'
'"request_method": "$request_method",'
'"request_time": "$request_time",'
'"request_length": "$request_length",'
'"response_status": "$status",'
'"bytes_sent": "$bytes_sent",'
'"body_bytes_sent": "$body_bytes_sent",'
'"upstream_addr": "$upstream_addr",'
'"upstream_response_time": "$upstream_response_time"}';
}
字段含义示例
body_bytes_sent响应body字节数3650
bytes_sent响应总字节数175
hostIP或域名(不包括端口)10.10.10.14
http_hostIP或域名(包括端口)10.10.10.14:81
http_refererreferer信息http://10.10.10.14/
http_user_agentUA信息Mozilla/5.0
http_x_forwarded_forXFF信息192.168.1.1
remote_addr客户端地址10.10.10.1
remote_user客户端认证用户名admin
request请求URI和协议GET
request_body请求的body
request_length请求长度571
request_method请求方法GET
request_time请求处理时间0.000
response_body返回的body,依赖lua,需要编写lua脚本来采集
response_header_data响应头数据,依赖headers-more-nginx-module
schema协议http
server_name虚拟主机名称
server_port服务器端口
server_protocol服务器协议
ssl_cipher交换数据中的算法
ssl_protocolSSL协议版本
status返回状态码404
time_local时间戳16/Jun/2019:23:29:50
upstream_addr后端提供服务地址
upstream_connect_time与服务器连接所花费的时间
upstream_response_time后端处理时间
upstream_statusupstream状态200

参考文档:

修改日志格式后,需要重启nginx,然后查看日志确认格式

1
tail -f /opt/nginx/log/access.log

9. map指令

map 的作用是创建自定义变量。

语法:

1
map $var1 $var2 {...}

map 的var1为源变量,通常是nginx的内置变量,var2 是自定义变量。 var2 的值取决于 var1 在对应表达式的匹配情况。如果一个都匹配不到则 var2 就是 default 对应的值。

例子:

1
2
3
4
map $args $foo {
default 0;
debug 1;
}

args 是nginx内置变量,就是获取的请求 url 的参数。 如果 args 匹配到 debug 那么 foo 的值会被设为 1 ,如果 args 一个都匹配不到 foo 就是 default 定义的值,在这里就是 0

参考文档Nginx map 使用详解

10. rewrite指令

rewrite指令的作用是重定向。

语法:

1
rewrite regex replacement [flag];
  • rewrite:该指令是实现URL重写的指令。
  • regex:用于匹配URI的正则表达式。
  • replacement:将regex正则匹配到的内容替换成 replacement。
  • flag: flag标记。

flag有如下值:

  • last: 本条规则匹配完成后,继续向下匹配新的location URI 规则。(不常用)
  • break: 本条规则匹配完成即终止,不再匹配后面的任何规则(不常用)。
  • redirect: 返回302临时重定向,浏览器地址会显示跳转新的URL地址。
  • permanent: 返回301永久重定向。浏览器地址会显示跳转新的URL地址。

例子:

1
rewrite ^/(.*) http://www.baidu.com/$1 permanent;
  • rewrite 是固定关键字,表示开始进行rewrite匹配规则。
  • regex 是 ^/(.*),这是一个正则表达式,匹配完整的域名和后面的路径地址。
  • replacement 是 http://www.baidu.com/$1,其中$1是取regex部分()里面的内容,如果匹配成功后跳转到的URL
  • flag 是 permanent,代表永久重定向的含义,即跳转到 http://www.baidu.com/$1

参考文档Nginx中的Rewrite的重定向配置与实践

11. 状态码

  • 1xx:信息响应类,表示接收到请求并且继续处理
  • 2xx:处理成功响应类,表示动作被成功接收、理解和接受
  • 3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理
  • 4xx:客户端错误,客户请求包含语法错误或者是不能正确执行
  • 5xx:服务端错误,服务器不能正确执行一个正确的请求

详情参考HTTP 状态码详解与选用

  • 本文作者: 好好学习的郝
  • 原文链接: https://www.voidking.com/dev-nginx-start/
  • 版权声明: 本文采用 BY-NC-SA 许可协议,转载请注明出处!源站会即时更新知识点并修正错误,欢迎访问~
  • 微信公众号同步更新,欢迎关注~