一文玩转NGINX(对于NGINX,你真的了解吗?)

前言

nginx [engine x] 是一个 HTTP 和反向代理服务器, 邮件代理服务器, 和一个通用的 TCP/UDP 代理服务器, 最初由 Igor Sysoev 。 运行了很久 在许多负载重的俄罗斯网站上,包括 雅得士 邮件.Ru , _ 漫步者 。 根据 Netcraft 的说法,nginx 服务或代理 21.62% 2022 年 8 月最繁忙的站点

nginx常用于反向代理,负载均衡。也可作WAF(应用防火墙)等。

Nginx优势

对于NGINX而言,IO多路复用epoll为其优点之一。而NGINX的轻量级以及CPU亲和,sendfile(零拷贝),动静分离等,都是NGINX优势所在。

IO多路复用epoll\

IO多路复用:多个描述符的I/O操作都能在一个线程内并发交替地顺序完成,这里的"复用" 指的是复用同一个线程
epoll。IO多路复用的实现方式 select,poll,epoll。

关于Unix与IO,可以参考笔者的《关于网络I/O与并发》文章。

什么是IO复用?下方图示对此技术有较为直观的体现。IO复用通常指多个描述的I/O操作在一个线程内并发交替顺序完成。而复用,指的是同一个线程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZBo20tje-1663225422001)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c6368a0251bf4f898884cb24addadfb1~tplv-k3u1fbpfcp-watermark.image?)]

轻量级

NGINX轻量级体现于:

  • 源代码只有核心代码,其它可以以插件的形式安装。
  • 代码模块化,适合二次开发。

sendfile零拷贝

关于sendfile技术,建议参考笔者的《关于网络I/O与并发》文章,了解前置知识。

sendfile系统调用在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝,操作效率很高,被称之为零拷贝。

sendfile() 利用 DMA 引擎,将文件数据拷贝到操作系统内核缓冲区,然后数据被拷贝到与 socket 相关的内核缓冲区。完成以上步骤后,DMA 引擎将数据从内核的 socket 缓冲区拷贝到协议引擎中去。
相比于传统I/O,无需再将数据从页缓存写道磁盘上,可以直接再以后公用程序地址空间和磁盘之间进行数据传输。无需占用大量的CPU与内存,即可完成数据传输。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-43Fu0BVS-1663225422002)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/53972c28f98341b1aa0cd3c2e299255c~tplv-k3u1fbpfcp-watermark.image?)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V7MzaGWO-1663225422003)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eef55b38390c442e9dd4a8d77355c12c~tplv-k3u1fbpfcp-watermark.image?)]

CPU亲和

cpu的亲和能够使nginx对于不同的work工作进程绑定到不同的cpu上面去。就能够减少在work间不断切换cpu,把进程通常不会在处理器之间频繁迁移,进程迁移的频率小,来减少性能损耗。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngGDzotr-1663225422004)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bd52db11abf74fc6a6a4c558b1411138~tplv-k3u1fbpfcp-watermark.image?)]

动静分离

对于NGINX动静分离,简单描述就是将动态请求与静态请求分开。
目前对于动静分离通常为两种,一种是将静态文件使用单独的域名,部署在独立的服务器上,也是目前最主流推崇的方案。而另一种方案则是将动静态文件混合发布,使用NGINX将其分开。\

对于动静分离,我们可以使用NGINX的expire指令,通过location规定不同文件类型,实现不同的请求转发。expire指令可以设置浏览器缓存过期时间,减少与服务器的请求和流量。当然,在使用CDN内容分发网络的情况下,无需使用此配置。对于动静分离,可参考下文动静分离与缓存部分。

简单使用

nginx简单使用一般为两种情况。流量转发与本地解析。流量转发通常用于将请求流量转发到源站(提供对应服务的服务器)。当然,流量转发也可配置负载均衡。而本地解析用于将访问路径解析到主机指定路径。

注意:所有配置文件在更改后请使用nginx -t命令以校验配置正确性,并使用systemctl restart nginx命令重启nginx服务。

配置文件结构

http{
    
    server{
    
        location / {
        
        }
    }

}

配置server

server通常按照以下格式进行配置。domian.com为所请求的域名。可以为多级域名。listen为所监听的端口,可以为任意端口。https模式下,证书建议使用lets encrypto。国内厂商可使用亚信,一般腾讯云域名会有免费证书额度,有效期一年。但亚信证书通常都是付费的,价格也不便宜。对于SSL证书管理,可以参考笔者后面发布的文章。

#http模式
server{
    listen 80;
    server_name domain.com;
}
#https模式
server{
	listen 443 ssl http2;
  	listen [::]:443 ssl http2;
        server_name domain.com;
  	ssl_certificate         /etc/nginx/cert/domain.crt;
  	ssl_certificate_key     /etc/nginx/cert/domain.key;
}

1.流量转发

以下为简单流量转发配置文件。我们可以使用该配置将流量转发到192.168.0.1服务器的8000都端口。

#该部分放置于server{}内部。
	location / {
    		proxy_pass http://192.168.0.1:8000;
    		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 x-forwarded-host $server_name;
  	}

2.本地解析

本地解析指将请求解析到本地文件,通常有root,alias两种解析方式。下面我们简述两种解析方式的区别。

root

访问 http://domain.com/url1/file

    location ^~ /url1/{
        root /home/ubuntu/;
    }

此处location解析到服务器的文件路径为: /home/ubuntu/url1/file

alias

访问 http://domain.com/url1/file

    location ^~ /url1/{
        alias /home/ubuntu/;
    }

此处location解析到服务器的文件路径为:/home/ubuntu/file(即alias不会解析location后面配置的路径)

高级使用

nginx鉴权

安装Nginx鉴权工具.

# ubuntu系统
apt install apache2-utils
# centos系统
yum install httpd-tools

生成密码。

此处username为登录用户名,运行此命令后按照步骤输入需要设置的密码即可。
/etc/nginx/.htpasswd为所生成文件保存地址。

htpasswd -c /etc/nginx/.htpasswd username

会话保持

通常,nginx默认会话非持久。未使用upstream的情况下,连接可能会终止。

upstream可以使用于会话保持,也可以使用作负载均衡。

仅会话保持

在nginx.conf内添加以下配置文件。此处为保持与127.0.0.1:8000的会话。此处ip地址可以为公网地址,端口自定义,连接名自定义,此处示例为example。

        upstream example {
                server 127.0.0.1:8000;
        }

在server{}配置内修改location{}内容

	location / {
    		proxy_pass http://example/;
    		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 x-forwarded-host $server_name;
  	}

负载均衡

负载均衡可以使用以下方案进行线路分配。

  1. 默认情况下,nginx按照轮询分配请求。
    轮询适合服务器配置相当,无状态且短平快的服务使用。另外在轮询中,如果服务器挂掉,会自动剔除该服务器。
        upstream example {
                server 192.168.0.1:8080;
                server 192.168.0.2:8001;
                server example.com;
                server example.com:8000;
        }
  1. 权重轮询
    如果在 upstream 中配置的server参数后追加 weight 配置,则会根据配置的权重进行请求分发。此策略可以与least_conn和ip_hash结合使用,适合服务器的硬件配置差别比较大的情况。
        upstream example {
                server 192.168.0.1:8080;
                server 192.168.0.2:8001;
                server example.com;
                server example.com:8000;
        }
  1. url_hash
    按照url的哈希值分配请求
        upstream example {
                server 192.168.0.1:8080;
                server 192.168.0.2:8001;
                server example.com;
                server example.com:8000;
                url_hash;
        }
  1. fair
    fair可以根据页面大小、加载时长智能的进行负载均衡。
        upstream example {
                server 192.168.0.1:8080;
                server 192.168.0.2:8001;
                server example.com;
                server example.com:8000;
                fair;
        }
  1. 最少连接
    轮询算法把请求平均转发到各服务器后端,使它们的负载大致相同。但有些请求占用的时长,导致其所在后端负载较高。那么该情况下,least_conn可以达到更好的负载均衡效果,适合请求处理时间长短不一造成服务器过载的情况。least_conn将请求分配给连接数最少的服务器。
        upstream example {
                server 192.168.0.1:8080;
                server 192.168.0.2:8001;
                server example.com;
                server example.com:8000;
                least_conn;
        }
  1. 源IP
    该方案nginx将根据源IP的哈希值分配服务器,即仅每个IP拥有对应的服务器。
        upstream example {
                server 192.168.0.1:8080;
                server 192.168.0.2:8001;
                server example.com;
                server example.com:8000;
                ip_hash;
        }

备注:

轮询:以循环方式分发对应用服务器的请求,将请求平均分发到每台服务器上。

server后面可使用IP/IP+端口/域名/域名+端口。分号前可标记backup,fail_timeout,max_fails,max_conns,down。

例如 server127.0.0.1 down;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xXN1qeks-1663225422005)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55d0e53260844b8898690a84e790629d~tplv-k3u1fbpfcp-watermark.image?)]

启用websocket

于nginx.conf配置文件的http{}内添加以下内容。

	map $http_upgrade $connection_upgrade {  
    	default upgrade;
    	''      close;
	}

于需要启用websocker的location内添加以下内容。

	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection $connection_upgrade;

访问控制

nginx可自定义访问控制,如限制UA头、限制请求体、限制请求头大小、限制请求源,允许请求源,限制访问文件类型等。下面对于部分限制手段进行举例。

限制ip访问

禁止所有ip访问

deny all;

允许指定ip访问(示例)

allow 192.168.0.1/24;
allow 192.168.1.2;
allow 192.168.2.3;
deny all;

注意,dney all;必须写在最后,否则优先级错误,allow配置不生效。

ip限制可在以下位置写入:

  • http: Nginx中所有服务起效
  • server: 指定的服务起效
  • location: 满足的location下起效
  • limit_except: 指定的http方法谓词起效

控制访问路径

云上环境有条件的可以使用云服务厂商的waf,企事业单位可购买安全厂商的waf。

预算不足的可以使用nginx防护

于server{}下禁止访问指定路径,如:

    location /upload.php {
        return 403;
    }

限制请求头

以下示例为限制请求头中包含python字段的请求。^$为不区分大小写。

if ($http_user_agent ~* "python|^$") {
        return 403;
        break; 
}

限制请求体、限制请求头大小。

限制允许客户端请求的最大单文件字节数。示例最大为4G。

client_max_body_size 4G;

限制请求行+请求头的标准大小为1k

    client_header_buffer_size 1k;

缓存

缓存就是数据交换的缓冲区(Cache),当用户要获取数据的时候,会先从缓存中去查询获取数据,如果缓存中有就会直接返回给用户,如果缓存中没有,则会发请求从服务器重新查询数据,将数据返回给用户的同时将数据放入缓存,下次用户就会直接从缓存中获取数据。

缓存分为服务端侧和客户端侧。
服务端缓存又分为:代理服务器缓存,反向代理服务器缓存。(也叫网关缓存,比如 Nginx反向代理就可以设置缓存)。

客户端侧缓存一般指的是浏览器缓存、app缓存等等,目的为加速各种静态资源的访问,降低服务器压力。

expires(浏览器侧缓存)

对于expires的定义:对于资源设定过期时间,无需通过服务端验证,仅通过浏览器浏览器自身确认是否过期。当然,与浏览器处清除Cookie和网站数据后重新发起请求,则会重新向服务器请求文件。

expires 指令可以控制 HTTP 应答中的“ Expires ”和“ Cache-Control ”的头标(起到控制页面缓存的作用)

示例(缓存三十天)

        location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$
        {   
        root /var/www/html/examplt/;
        expires 30d;

如上设置后,对于html/htm/gif/jpg/jpeg/bmp/png/ico/txt/js/css文件,客户端请求并缓存后,于三十天后发起请求才会更新缓存数据。而重复发起请求则服务器应当返回304状态码。

304状态码
如果客户端发送了一个带条件的GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个304状态码。

缓存的优点

  • 减少数据传输,节省网络流量,加快响应速度,提升用户体验;
  • 减轻服务器压力;
  • 提供服务端的高可用性;

缓存的缺点

  • 数据的不一致;
  • 增加成本;
  • 降低搜索引擎信任度;
  • 关键词排名稳步下降;
  • 搜索引擎蜘蛛抓取频率逐渐下降;

因此,该方案会影响SEO(搜索引擎优化),慎用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ecEuVS8Y-1663225422006)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3d7f0872bbac472eaaac8f7ab74318c7~tplv-k3u1fbpfcp-watermark.image?)]

服务器侧缓存

NGINX的服务端侧缓存由ngx_http_proxy_module模块相关指令集完成。

  • proxy_cache_path 缓存文件存放路径
  • proxy_cache 代理缓存设置
  • proxy_cache_key 缓存key值
  • proxy_cache_valid 缓存时间
  • proxy_cache_min_uses 访问量后缓存
  • proxy_cache_methods 指定缓存方法
  • upstream_cache_status 缓存命中

PHP内容渲染

默认情况下,直接使用alias或root解析,访问时php文件会因未渲染而无法使用浏览器查看,该情况下php文件会被自动下载。以下为nginx作反向代理的情况下渲染php的方案。

前置步骤

安装php-fpm

apt install php-fpm -y

编辑配置文件并添加设置监听端口。此处监听9002端口。

vim /etc/php/7.4/fpm/pool.d/www.conf
listen 127.0.0.1:9002;

配置完成后,运行以下命令检查配置文件正确性并启动fpm

/usr/sbin/php-fpm7.4 -t && /usr/sbin/php-fpm7.4

使用ps aux | grep php-fpm命令即可查看php-fpm是否已运行。

以下为nginx配置文件。该配置文件将url请求的路径解析到/home/ubuntu/文件夹下,用户可根据项目位置自行修改。此处fastcgi转发端口对应上文php-fpm所监听的9002端口。

location ~ \.php(.*)$ {
root /home/ubuntu/;
autoindex on;
            fastcgi_pass   127.0.0.1:9002;
            fastcgi_index  index.php;
            fastcgi_split_path_info  ^((?U).+\.php)(![]().+)$;
           fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            fastcgi_param  PATH_INFO  $fastcgi_path_info;
            fastcgi_param  PATH_TRANSLATED  $document_root$fastcgi_path_info;
            include        fastcgi_params;
    }

php-fpm + nginx 部署方案不建议用于多页面解析部署,fpm在解析时会产生进程并增加使用资源。通常情况下,对于多页面多服务,我们建议使用Docker,使用nginx将流量转发到docker,并于docker内安装php环境。

简单监控

我们可以在server{}内添加以下内容

         location /status{
             stub_status on;
         }

配置以上内容并应用后,可访问server指定的域名/IP/端口加上路径/status查看活跃连接、读、写、等待等信息。

关于status的解释

active connections – 活跃的连接数量
server accepts handled requests — 总共处理了3个连接 , 成功创建3次握手, 总共处理了19个请求
reading — 读取客户端的连接数.
writing — 响应数据到客户端的数量
waiting — 开启 keep-alive 的情况下,这个值等于 active – (reading+writing), 即 Nginx 已经处理完正在等候下一次请求指令的驻留连接。

统一管理与封禁

我们可以通过使用include语句,统一在每个server{}配置文件下新增以下语句。

include conf.d/agent/agent.conf;

在我们管理封禁IP、自定义报错页面、访问UA头等内容,我们只需在、nginx目录下的conf.d/agent/agent.conf文件内更改封禁策略即可实现统一管理。该方案无需运维在每个server{}下管理封禁策略。该方案同样适用于站群。多服务器场景下,我们只需更新agent.conf即可完成所有反向代理的访问控制等变更。同时,该方案不影响针对单个服务的限制,使用该方案的同时可以同时在server{}内写入针对性限制策略。

以下,给出封禁示例配置。

location = /favicon.ico {
	alias /var/www/html/favicon.ico;
}
error_page 500 502 503 504 400 403 404 /errors.html;
location = /errors.html {
        alias  /var/www/html/errors.html;
}
if ($http_user_agent ~* "MJ12bot|Scrapy|python|AhrefsBot|BLEXBot|DotBot|^$") {
        return 403;
        break; 
}

解释:

  1. location = /favicon.ico
    统一管理页面icon,将所有由url请求的icon解析到本地的/var/www/html/favicon.ico文件。
  2. error_page 500 502 503 504 400 403 404 /errors.html;
    统一将所有报错也解析为errors.html
  3. location = /errors.html
    指定所有报错页解析到本地的/var/www/html/errors.html文件
  4. if ($http_user_agent ~* "MJ12bot|Scrapy|python|AhrefsBot|BLEXBot|DotBot|$")\ 封禁请求头中含有MJ12bot、Scrapy、python、AhrefsBot、BLEXBot、DotBot的请求。$为不区分大小写。其中,MJ12bot、Scrapy、AhrefsBot、BLEXBot、DotBot为国外垃圾蜘蛛,seo蜘蛛,营销蜘蛛等,会占用站点大大量资源。经分析,以上蜘蛛每日请求量为百万级。对于python,通常过滤非法爬虫请求。

nginx使用优化

句柄优化

linux/Unix上,一切皆文件,每一次用户发起请求就会生成一个文件句柄,文件句柄可以理解为就是一个索引,所以文件句柄就会随着请求量的增多,而进程调用的频率增加,文件句柄的产生就越多,系统对文件句柄默认的限制是1024个,对Nginx来说非常小了,需要改大一点

根据句柄设置,又可以分为以下三种:

  • 系统全局性修改
  • 用户局部性修改
  • 进程局部性修改

系统全局以及用户局部修改可以通过更下以下文件实现:

vim /etc/security/limits.conf

在End of file前面添加个参数

  • soft:软控制,到达设定值后,操作系统不会采取措施,只发提醒
  • hard:硬控制,到达设定值后,操作系统会采取机制对当前进程进行限制,这个时候请求就会受到影响
  • root:代表root用户(系统全局性修改)
  • *:代表全局,即所有用户都受此限制(用户局部性修改)
  • nofile:指限制的是文件数的配置项。后面的数字即设定的值,一般设置10000左右

以下为示例配置:

root soft nofile 65535
root hard nofile 65535
*    soft nofile 65535
*    hasd nofile 65535

局部修改可以通过更改nginx配置文件实现:

vim /etc/nginx/nginx.conf

此处限制每个进程的最大文件打开数,建议与ulimit -n的值保持一致。

worker_rlimit_nofile 35535; #进程限制

CPU关联性设置

该设置视具体情况选择设置,正常使用下不必考虑。

一般设置CPU的核心或者核心数x2为NGINX运行工作进程数。我们可以使用以下命令查看CPU核心数。
/proc/cpuinfo文件 grep ^processor /proc/cpuinfo | wc -l。

4核配置:

worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;

8核配置:

worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

worker_processes最多为8个,8个以上性能将不会提升,而稳定性也会下降。

NGINX版本1.9后,以下指令可自动绑定CPU。

worker_cpu_affinity auto;

优化

NGINX的连接处理机制在于不同的操作系统会采用不同的I/O模型,Linux下,nginx使用epoll的I/O多路复用模型。

  • Linux,使用epoll
  • freebsd,使用kqueue
  • solaris使用/dev/pool
  • windows使用icop
设置work_connections 连接数
 worker_connections  10240;
keepalive timeout会话保持时间
keepalive_timeout  60;
GZIP压缩性能优化
gzip on;       #表示开启压缩功能
gzip_min_length  1k; #表示允许压缩的页面最小字节数,页面字节数从header头的Content-Length中获取。默认值是0,表示不管页面多大都进行压缩,建议设置成大于1K。如果小于1K可能会越压越大
gzip_buffers     4 32k; #压缩缓存区大小
gzip_http_version 1.1; #压缩版本
gzip_comp_level 6; #压缩比率, 一般选择4-6,为了性能gzip_types text/css text/xml application/javascript;  #指定压缩的类型 gzip_vary on; #vary header支持
proxy超时设置
proxy_connect_timeout 90;
proxy_send_timeout  90;
proxy_read_timeout  4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k
高效传输模式
sendfile on; # 开启高效文件传输模式。
tcp_nopush on; #需要在sendfile开启模式才有效,防止网路阻塞,积极的减少网络报文段的数量。将响应头和正文的开始部分一起发送,而不一个接一个的发送。

Nginx尽可能达到更好的性能,我们还需要调整在服务器上的内核参数。

vim /etc/sysctl.conf

更高后执行sysctl -p载入配置文件,使配置生效

1)调节系统同时发起的tcp连接数

net.core.somaxconn = 262144

2)允许等待中的监听

net.core.somaxconn = 4096 

3) tcp连接重用

net.ipv4.tcp_tw_recycle = 1 
net.ipv4.tcp_tw_reuse = 1   

4)不抵御洪水攻击

net.ipv4.tcp_syncookies = 0  
net.ipv4.tcp_max_orphans = 262144  #该参数用于设定系统中最多允许存在多少TCP套接字不被关联到任何一个用户文件句柄上,主要目的为防止Ddos攻击

5)最大文件打开数

在命令行中输入如下命令,即可设置Linux最大文件打开数。

ulimit -n 30000
相关命令:

查看cpu核心数

cat /proc/cpuinfo|grep "cpu cores"|uniq

显示物理cpu数量:

cat /proc/cpuinfo | grep "physical id"|sort|uniq|wc -l

查看cpu使用率

top  回车后按 1

查看nginx使用cpu核心和对应的nginx进程号

ps -eo pid,args,psr | grep [n]ginx
posted @ 2022-09-22 00:45  K龙  阅读(394)  评论(0编辑  收藏  举报


Site | Public account | Range | Instagram | Mail | Group

© Honker Security Commando