💠

💠 2024-09-20 17:30:23


Nginx

Official Site | Official Doc
Tengine

nginx-tutorial

Nginx的安装

命令安装

  • 安装 sudo apt install nginx
  • 启动服务 sudo nginx
    • 或者sudo /etc/init.d/nginx start
    • 或者 systemd 方式 systemctl start nginx
  • 关闭 sudo nginx -s quit
    • 或者 sudo /etc/init.d/nginx stop
    • 或者 systemd 方式systemctl stop nginx

编译安装

不建议使用这种方式进行安装,很容易出现兼容问题

  • 下载 nginx,pcre,zlib,openssl 的压缩包
  • 进入解压根目录(按实际情况配置) 配置各个包
1
2
3
4
5
6
7
8
    ./configure --sbin-path=/usr/local/nginx/nginx \
    --conf-path=/usr/local/nginx/nginx.conf \
    --pid-path=/usr/local/nginx/nginx.pid \
    --with-http_stub_status_module \
    --with-http_ssl_module \
    --with-pcre=/home/kuang/pcre-8.20 \
    --with-openssl=/home/kuang/openssl \
    --with-zlib=/home/kuang/zlib-1.2.11

Docker安装

nginx hub 官方镜像

命令参数

  • -s signal
    • stop 停止
    • quit 退出
    • reopen 重新打开
    • reload 重载配置(修改配置文件常使用)
  • -t 测试配置文件

可视化管理工具

nginxWebUI
nginx-proxy-manager
0xJacky/nginx-ui: Yet another WebUI for Nginxgo+js


内置变量

Official Doc | Nginx 内置变量

  • $remote_addr 客户端地址
  • $remote_port 客户端端口

配置使用

Official Doc

知乎专栏
nginx基本配置 | ngrok nginx docker本地搭建服务器

nginx 配置文件的语法是自己独有的语法, 比较像 shell, 里面有用到正则, 变量等概念

  • 读取自定义目录配置:
    • nginx.conf 中 http 块内添加 include /etc/nginx/conf.d/*.conf;
  • 错误页面重定向 error_page 404 /404.html; 也可以填完整URL

Nginx反向代理,当后端为Https时的一些细节和原理

nginx提供Http服务,但是反向代理了HTTPS地址时 需要注意证书的一致性问题


静态资源Web服务器

参考 nginx配置静态文件服务器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  server {
    client_max_body_size 4G;
    listen  80; 
    # server_name static.me; # 如果需要使用域名 则需要在hosts文件配置
    root /home/mini/Sync;
    location / {
        autoindex on; # 显示索引
        autoindex_exact_size on; # 显示大小
	      autoindex_localtime on;  # 显示时间
    }
  }
  1. 若出现403错误, 将 /etc/nginx/nginx.conf 中第一行的 user nginx; 改成可访问静态文件目录的用户即可
  2. 配置为文本文件类型 即 text/plain; 类型。例: 浏览器直接查看 code 目录下所有源代码
1
2
3
4
5
6
    location /code/ {
        # All files in it
        location ~* {
            add_header Content-Type text/plain;
        }
    }

如果有编码问题可配置成 add_header Content-Type 'text/plain;charset=UTF-8'; location ~* /.*\.(py|md|sql)${}

反向代理多个服务

配置反向代理

  1. nginx 的 80 端口下:/ 路径的请求转发到9991端口 /myth 转发到7898端口
 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
  upstream one {
    server 127.0.0.1:9991;
  }
  upstream two {
    server 127.0.0.1:7898;
  }

  server {
    listen 80;
    server_name 1.1.1.1;

    location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-Nginx-Proxt true;

      proxy_pass http://one;
      proxy_redirect off;
    }
    
    location /myth{
      proxy_pass http://two;
      proxy_redirect off;
    }
  }

静态资源+反代理后端

Nginx反向代理解决跨域问题 | nginx简易使用教程,使用nginx解决跨域问题

配置统一出口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    server {
        client_max_body_size 4G;
        listen  80;  # listen for ipv4; this line is default and implied
        server_name static.me;
        
        location / {
            root /data/static;
            # proxy_pass http://127.0.0.1:8889/; 如果静态资源在别的端口上,这样配置也可以
        }

        location /api/ {
            # add_header 'Access-Control-Allow-Origin' '*';
            proxy_pass http://127.0.0.1:8889/; # 去除请求的 api 路径,并访问后端
            # proxy_pass http://127.0.0.1:8889; 这种方式不会去除 /api/
        }

        location /api/a-service {
          proxy_pass http://127.0.0.1:8889/a-service; # 移除 /api/ 路径,保留a-service (api路径下多个服务时使用此类型配置)
        }
    }
  1. 将静态文件交由Nginx进行处理, 后台的服务统一用一个前缀和前台进行区分, 然后将服务端的真实host和ip或者域名配置进来
  2. 这样在于前端看来就是访问 static.me/api 而已, 实际上访问的是 127.0.0.1:8889/api

注意,原先使用nginx反向代理tomcat,尝试配置后端为一个本地dns解析的域名。然后发现这是无法生效的,所以应该使用真实IP或公网域名

前后端分离时避免跨域

在需要被跨域访问的服务端,添加如下配置

1
2
3
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS; 

配置https

nginx搭建https服务 | nginx http/2

自签发证书

配置HTTPS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    upstream one {
        server 127.0.0.1:8888;
    }
    server {
        listen 443;
        server_name web.me;

        # 主要就是添加了这一块
        ssl on;
        ssl_certificate  /data/https/server.crt;
        ssl_certificate_key  /data/https/server.key;
        
        # http 转向 https
        return 302  https://$host$request_uri;

        location / {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header X-Nginx-Proxt true;
            proxy_pass https://one;
            proxy_redirect off;
        }
    }

通过 certbot 配置 HTTPS

免费的网站, 并且现在支持泛域名了参考博客 | 参考博客
Nginx反向代理https

1
2
3
4
  wget https://dl.eff.org/certbot-auto
  chmod a+x certbot-auto
  ./certbot-auto #进行安装 但是过程中会有一些设置,
  ./certbot-auto certonly --email xxx@xxx --nginx -d xxx.domain # 生成 xxx.domain 证书

SSL 接收到一个超出最大准许长度的记录 要在端口后加上SSL nginx

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  upstream one {
    server 127.0.0.1:8080;
  }

  server{
    listen 443 ssl;
    server_name xxx.domain
    access_log /data/log/https.log;
    
    # ssl配置
    ssl on;
    ssl_certificate  /etc/letsencrypt/live/xxx.domain/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/xxx.domain/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/xxx.domain/chain.pem;
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;

    location / {
        proxy_pass https://one;
    }
  }

配置Websocket反向代理

 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
  # 配置连接的配置信息
  map $http_upgrade $connection_upgrade{
    default upgrade;
    ''  close;
  }
  upstream back_end {
    server 127.0.0.1:8888;
  }
  server {
    listen 80;
    server_name 127.0.0.1;
    location / {
      # 设置转发真实ip
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
      proxy_set_header X-Nginx-Proxt true;

      # 设置接收到的请求类型
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;

      proxy_pass http://back_end;
      # 默认是 1.0 不支持 keepAlive
      proxy_http_version 1.1; 
      proxy_redirect off;
      proxy_read_timeout 300s;
    }
  }

绕过Grafana,免密登录,需要预先生成key

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

  # http://grafana-user.test/d/spring_boot_21/shang-shu-tai-jian-kong-mian-ban?orgId=1
server {
    listen 80;
    server_name  grafana-user.test;

    location / {
        proxy_pass http://192.168.1.1:9091/;
        proxy_redirect off;
        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 Connection "";
        proxy_set_header Authorization "Bearer xxxxx";
        # add_header X-Frame-Options SAMEORIGIN;
        proxy_hide_header X-Frame-Options;
    }
    location /api/live/ws {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header X-Nginx-Proxt true;
        
        proxy_set_header Authorization "Bearer cccc";
        
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        
        proxy_http_version 1.1; 
        proxy_redirect off;
        proxy_read_timeout 300s;

        proxy_pass http://192.168.1.1:9091;
    }
}

转发代理

例如 aaa.com 需要VPN等方式才能访问,Nginx所在的主机能访问,就可以这么配置,然后配置DNS将 aaa.com 解析到Nginx的主机上,就可以实现其他客户机不安装VPN 直接访问 aaa.com

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  server {
    server_name aaa.com;
    listen 80;
    location / {
      proxy_pass http://aaa.com;
      proxy_set_header Host $host:$server_port;
      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;
    }
  }

防盗链

gzip

nginx 启用 gzip压缩

1
2
3
4
5
  gzip  on;
  gzip_comp_level 4; # 缺省值
  gzip_buffers 4 16k;
  gzip_http_version 1.1; # 缺省值
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

负载均衡

Nginx 反向代理 负载均衡 虚拟主机配置

分为四层和七层:

  • 在四层只依据ip的报文转发(修改进入时目的ip从nginx改成upstream的IP,修改返回时发送ip)
  • 在七层依据数据内容做转发,例如按http请求后缀做转发 *.jpg 到A服务器 *.jsp到B服务器

负载均衡策略

Doc: Http Load Balancer

  1. Round Robin:
    • 默认方式,对所有backend无差别按序轮询, 如果backend宕机会自动移除掉
  2. Weight:
    • 对所有backend按权重进行轮询,权重越高分发到的请求就越多,默认值为1。适合硬件差别比较大的多个节点
    1
    2
    3
    4
    5
    6
    7
    
        #动态服务器组
        upstream dynamic_server {
            server localhost:8080 weight=2;
            server localhost:8081;
            server localhost:8082 backup;
            server localhost:8083 max_fails=3 fail_timeout=20s;
        }
    
  3. Least Connections(least_conn):
    • 依据每个backend当前活跃连接数,将请求分发到连接数最少的backend上,此时会考虑backend的weight权重比例
    • upstream配置块中在首行添加 least_conn;即可
  4. IP Hash(ip_hash): 对请求来源IP地址计算hash值,通过某种映射(例如取余,详细可查看ip_hash相关源码)分发至对应backend
    • upstream配置块中在首行添加 ip_hash;即可
  5. Generic Hash(hash): 用户自定义资源(例如URL)计算hash完成分发,可选consistent关键字支持一致性hash特性

Q & A

  1. 如果负载均衡了A B两个节点,请求进入了A节点后,立马移除了对A节点的负载均衡,该请求是否能正常执行
    • 能正常执行,假如此时请求A失败,例如A节点宕机, 请求还会转移至B节点(failover)
  2. 如果负载均衡了A B两个节点, A节点宕机了,后续请求是否还会分发到A节点
    • 不会,原理: TODO

Nginx Plus

对标 F5 BIG-IP

5-reasons-switch-f5-big-ip-to-nginx-plus


Keepalived

Keepalived软件起初是专为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现高可用的VRRP功能。因此,Keepalived除了能够管理LVS软件外,还可以作为其他服务(例如:Nginx、Haproxy、MySQL等)的高可用解决方案软件。

参考: keepalived实现服务高可用


同类应用

Caddy

official website

具有丰富的插件支持, 配置简洁, 自动配置 HTTPS证书,相较于nginx资源消耗更多 吞吐量低一些

参考: 使用 Caddy 替代 Nginx,全站升级 https,配置更加简单

Squid

Official

Varnish

HAProxy

Official 企业级工具

nuster

Github基于 HAProxy


Tips

  • 文件上传报错 413
    • http{} 中添加 client_max_body_size 80M;
  • request_time 比实际时间长 Nginx的延迟关闭lingering_timeout

access 日志中 upstream 408状态码 本身返回的502 实际上upstream没收到请求

Random HTTP 408 on POST requests and no body is transferred