1. 概述
??nginx是一款高性能的开源Web服务器和反向代理服务器。它由俄罗斯的工程师Igor Sysoev开发,并于2004年首次公开发布。Nginx的设计目标是提供高性能、稳定性和低资源消耗的解决方案,以应对大流量的网站和应用程序。
??ginx的主要特点之一是其事件驱动的架构,它采用异步、非阻塞的方式处理并发连接。这使得Nginx能够高效地处理大量的并发请求,而不会占用过多的系统资源。此外,Nginx还具有灵活的配置选项和模块化的架构,使其能够适应各种不同的应用场景。
??然而人要衣装马靠鞍,虽然nginx本身的设计架构上可以有能力提供大并发高性能服务,但是在实际部署的时候,也需要根据业务需要对nginx以及其依赖的操作系统进行性能参数调整,以期让nginx能够在实际应用环境中达到最佳性能和安全性。本文参考nginx的官方文档和网络上的成功经验,结合自己的学习和实践经验,全方位地对nginx服务器的配置优化进行了描述。
2. 性能调优
2.1 CPU和进程数调整
-
worker_processes:
??nginx配置文件中的worker_processe参数可以用来给nginx配置worker进程数量,一般的建议是,如果服务器有多少CPU核就最大配置多少worker进程,可以直接配置成:
??让nginx启动的时候自己检测有多少CPU核并启动对应数量的worker进程。?当然也不是一概而论的,譬如对于本身nginx只是做代理转发功能的服务器,往往CPU不是瓶颈,按需配置进程数即可,而对于高磁盘I/O的业务环境,适当超过实际的CPU核心数反而可以提高系统的响应能力,不至于因为部分请求的磁盘读写卡顿导致其他请求的响应受到影响。
-
worker_cpu_affinity:
??nginx配置文件中的worker_cpu_affinity参数用来设置worker进程和cpu核之间的绑定关系,指定让某个worker进程在某个或某几个CPU核上运行,避免因为进程在不同的核之间进行切换,导致性能的下降。从1.9.10版本开始,worker_cpu_affinity参数也可以配置成auto,让nginx自行执行CPU核心亲和性绑定操作。
2.2 并发连接数调整
?? linux操作系统单进程的默认最大可以打开的文件句柄数是1024个,这对于一个提供高并发服务的nginx是远远不够的,因此首先需要打开linux操作系统的限制,然后在nginx配置里面同步增加可以接受的并发连接的数量。
2.2.1 修改操作系统单进程最大句柄数限制
?? 通过 ulimit -n 命令查看当前linux的限制值,如果只是临时修改限制,则直接可以用以下命令来设置允许但进程最多可以打开65536个文件句柄:
ulimit -n 65536
那么单个进程打开65536个句柄是上限吗?远远不是的,只是大家习惯用65536来举例而以,一般来说也是足够用了。
?? 以上设置方法当linux重启后就实效了,那么如果希望无论是不是操作系统重启设置都永久生效,那么就需要修改linux的核心配置,首先需要确认linux内核允许的最大文件句柄数:
cat /proc/sys/fs/file-max
1000000
?? 这表明这台Linux系统最多允许一个单独的Linux用户登录会话可以同时打开(即包含所有用户打开文件数总和)1000000个文件,是Linux系统级硬限制。也可以通过以下命令查看:
sysctl fs.file-max
并通过以下命令进行修改:
sysctl -w fs.file-max=1000000
解开了内核级别的限制后,再修改用户级别的限制, 配置文件 /etc/security/limits.conf了,添加以下两行:
* soft nofile 65536
* hard nofile 65536
2.2.2 修改nginx的最大并发连接限制
??nginx配置文件中的event块中的worker_connections参数可以用来配置单个worker进程允许建立的最大tcp并发连接数,那么如果配置了n个worker进程,理论上nginx可以支持n*worker_connections个并发连接。配置如下:
??以linux为例,配置如下:
events {
......
use epoll;
}
??对于windows, iocp模型在老的nginx版本中中是不支持的,如果要在windows中使用要么就升级到最新版本要么就是自己从新版本中将iocp事件模块移植过来。
2.4 启用keepalived功能
?? http keepalive连接保持特性能够避免每次进行http请求都需要重新建立tcp连接带来的额外消耗,从而提升http访问的响应效率。
2.4.1 和客户端保持keepalive
?? nginx控制和客户端连接的keepalive是通过以下四个参数来控制的:
- keepalive_disable:可以指定对于那些keepalived处理功能缺陷的浏览器关闭keepalive功能,譬如msie6。
- keepalive_time:表示一个连接最大存活的可复用的时间。
- keepalive_timeout:表示如果超过指定的时间没有新的请求该连接就会被关闭。
- keepalive_requests:表示一个被复用的连接最大支持多少次请求,超过次数后连接就会被关闭了。
-
listen 参数优化:?在一个繁忙的nginx服务器上,随时有大量的并发连接请求过来,这个时候需要适当增大backlog的值,如果这值太小,socket连接请求建立队列太小会导致部分连接被拒绝。配置如下:
listen *:80 backlog=2048;
??在内核接收到一个连接请求后,正常的情况下是立即回调应用层(这里是nginx)进行处理,但是现在内核允许设置延后接收请求,等到客户端有数据法上来的时候才回调应用层,这样的好处是减少无效连接、提高处理效率、节约资源。配置如下:
listen *:80 deferred;
??调整接收和发送缓冲区大小,根据TCP滑动窗口大小的占比1-1/(2^tcp_adv_win_scale),计算出缓冲区大小上限;
??举例:例如若我们的带宽为2Gbps,时延为10ms,那么带宽时延积BDP则为2G/8*0.01=2.5MB,所以这样的网络中可以设最大接收窗口为2.5MB,当tcp_adv_win_scale=2时最大读缓存可以设为4/3*2.5MB=3.3MB。当然还要综合服务器的实际内存和并发量来考虑最终的配置值,配置举例如下:
listen *:80 recvbuf=1048576 sndbuf=1048576;
-
sendfile :?用于启用或禁用Nginx在发送文件时使用零拷贝技术,在传统的文件传输过程中,数据从磁盘读取到内核缓冲区,然后再从内核缓冲区复制到用户空间缓冲区,最后才能发送给客户端。这种复制过程涉及多次数据拷贝,会增加CPU和内存的负担,降低性能。而使用sendfile指令后,Nginx可以直接从磁盘将数据发送到网络套接字,绕过了用户空间缓冲区,实现了零拷贝。这样可以显著提高文件传输的效率,减少了CPU和内存的开销。?需要注意的是,
sendfile
指令在某些情况下可能会导致一些问题,例如在某些操作系统或文件系统中可能存在兼容性问题。因此,在特定的应用场景中,可能需要根据具体情况来决定是否启用或禁用sendfile
指令。 -
tcp_nopush :?它用于控制是否启用TCP的TCP_NODELAY选项,以及在发送HTTP响应时如何处理数据的推送。当tcp_nopush被设置为on时,Nginx会禁用TCP_NODELAY选项,这意味着Nginx将等待发送缓冲区填满后才发送数据给客户端。这样可以减少发送小数据包的频率,提高网络传输的效率。同时,当tcp_nopush被设置为on时,Nginx还会尝试将HTTP响应中的所有数据一次性发送给客户端,而不是分多次发送。这种方式称为数据推送(data push),它可以减少数据包的数量,提高传输效率,尤其适用于较大的响应,但是可能会影响客户端的首字节接收时间的指标。
?? 例如:
http {
sendfile on;
tcp_nopush on;
}
2.8 异步文件io优化
??异步文件IO是一种文件读写操作的方式,它允许Nginx在执行文件IO操作时不会阻塞其他的请求处理,从而提高服务器的并发性能。当aio被设置为on时,Nginx将使用异步文件IO进行文件的读取和写入操作。这意味着Nginx在进行文件IO操作时,不会阻塞其他的请求处理,而是继续处理其他请求。这对于高并发的场景非常有用,可以提高服务器的响应性能和吞吐量。
??需要注意的是,默认情况下,aio是禁用的(即设置为off),这意味着Nginx将使用同步的文件IO操作。在同步模式下,Nginx在进行文件IO操作时,会阻塞其他的请求处理,直到文件IO操作完成。
??在选择是否启用aio时,需要根据具体的应用场景和服务器硬件环境进行评估。异步文件IO在某些情况下可以显著提高性能,特别是在处理大量并发请求或对文件IO操作较频繁的情况下。但在某些特定的环境中,异步文件IO可能会导致性能下降或不稳定。因此,建议在启用aio之前进行性能测试和评估,以确定最佳的配置方式。
2.9 内容压缩
?? http协议规范支持对响应的内容进行压缩,nginx默认内置了gzip压缩能力。通过压缩方式响应,可以大大降低网络I/O,提升客户端的访问体验。但是压缩也不是免费的午餐,配置不当也会导致nginx因为需要进行压缩消耗大量的CPU资源引起系统故障。
# 开启gzip压缩功能
gzip on;
# 设置允许压缩的页面最小字节数,页面字节数从header头的Content-Length中获取。
# 默认值是0,表示不管页面多大都进行压缩。
# 如果本身内容很小进行压缩,那么压缩效率就很差,可能越压越大,得不偿失
gzip_min_length 1k;
# 压缩缓存区大小。表示申请4个单位的位16K的内存作为压缩结果流缓存
gzip_buffers 4 16k;
# 设置http请求只要要等于这个指定的HTTP协议版本才启用压缩功能,默认是1.1,
gzip_http_version 1.1;
# 压缩比率。用来指定gzip压缩比,1压缩比最小,处理速度最快;
# 9压缩比最大,传输速度快,但处理最慢,也比较消耗CPU资源
gzip_comp_level 2;
# 用来指定压缩的类型,一般对文本类型的内容进行压缩
# 图片、压缩包、视频不要开启压缩
gzip_types text/plain text/css text/xml application/javascript;
# 开启或者关闭\\\"Vary: Accept-Encoding\\\" 响应头
# nginx响应Vary: Accept-Encoding用来告诉缓存,Accept-Encoding头需要作为
# 缓存key的一部分
gzip_vary on;
??另外补充一下,brotli压缩是2015年9月谷歌开源的一个压缩算法,其压缩率比gzip更高,如果浏览器支持的情况下,也可以考虑给nginx加载brotli压缩模块,提供更高效率的压缩能力。
2.10 多线程
?? 从1.7.12版本开始,nginx提供了线程池能力。能够在读写文件的时候用多线程来避免阻塞worker进程的问题,特别是对I/O比较密集的情况下效果会比较好。多线程默认是不开启的,需要在编译的时候添加–with-threads选项来开启。配置举例如下:
location /video/ {
sendfile on;
aio threads;
}
??以上配置nginx在进行sendfile时会将文件读取和发送的工作卸载到多线程中来避免阻塞worker进程,从而提升nginx的并发能力。
2.11 开启http2
??相对于http/1.1协议,http2协议可以提供更好的并发和传输性能,通过开启http2协议,能够让有能力支持http2的浏览器通过http2来访问nginx,从而提升用户的体验。但是http2是依赖于ssl的,因此,需要提前准备好https证书和对应的私钥。开启http2的配置举例如下:
server {
listen 443 ssl http2;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
....
}
2.12 安全
2.12.1 SSL优化
2.12.1.1 禁用老旧的ssl协议
?? 现在最新的ssl协议已经更新到TLS v1.3了,建议不要再使用TLS v1.1及之前的版本了,配置指令举例如下:
ssl_protocols TLSv1.2 TLSv1.3;
2.12.1.2 禁止弱加密套件
??密码套件是提供加密、认证和完整性的算法组合。为了保护数据传输安全,TLS/SSL使用一个或多个密码套件。这些密码套件在SSL/TLS协商过程中以及数据传输过程中使用。弱密码套件和过时的密码套件配置可能会给你的网站带来安全隐患,并使其容易受到攻击。如果你使用这些密码套件,攻击者可能会拦截或修改传输中的数据。?由于加密套件的应用是依赖于nginx和openssl的版本的,Mozilla SSL Configuration Generator 可以帮助我们来生成nginx的配置参数,通过这个工具生成的nginx ssl配置举例如下:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /path/to/signed_cert_plus_intermediates;
ssl_certificate_key /path/to/private_key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
ssl_dhparam /path/to/dhparam;
# intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security \\\"max-age=63072000\\\" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
# verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
# replace with the IP address of your resolver
resolver 127.0.0.1;
}
2.12.1.3 使用安全的密钥交换机制??
DH参数的目的是允许交换一个密钥,用于在会话中加密消息的传输记录。临时DH提供前向安全性,意味着会话密钥在会话结束后被删除。因此,攻击者无法检索两方之间交换的消息,除非是最后一个会话。? 用openssl命令生成一个至少2048bits的唯一DH组,例如:
openssl dhparam -out dhparams.pem 2048
??然后在nginx中加载这个dhparams.pem文件:
ssl_dhparam ./ssl/dhparam.pem;
2.12.1.4 ocsp stapling验证优化
?? 在客户端请求https网站的时候,需要对CA中间商的证书的有效性进行验证。如果客户端访问CA的域名被污染或者访问CA的服务器网络比较卡,会导致验证的时间会比较长,导致用户体验下降。通过nginx的ocsp stapling机制,nginx会去CA服务器进行ocsp查询并缓存结果,客户端在进行TLS连接握手的时候nginx就会把缓存的结果响应给客户端,避免客户端自己去请求验证。下面是nginx开启ocsp stapling功能的配置,举例如下:
# 开启 OCSP Stapling
ssl_stapling on;
# 启用nginx对OCSP响应的验证
ssl_stapling_verify on;
# 默认信任的ca根证书
ssl_trusted_certificate lestencrypt_root.cer;
# 添加resolver解析OSCP响应服务器的主机名,valid表示缓存。
resolver 114.114.114.114 223.5.5.5 valid=60s;
# DNS解析的超时时间
resolver_timeout 3s;
2.12.2 关闭服务器响应中的版本信息的显示
默认情况下,当客户端发送请求并获得Nginx服务器的响应时,响应头中会包含服务器的版本信息。这个版本信息可能包含敏感的细节,如Nginx的版本号和其他相关信息,这可能会增加服务器面临的潜在安全风险。配置如下;
server_token off;
??通过使用\\”server_tokens off\\”指令,可以禁用服务器版本信息的显示。当该指令在Nginx配置文件中进行配置后,服务器响应的响应头中将不再包含版本信息。这样可以提高服务器的安全性,因为潜在的攻击者将无法直接获得关于服务器使用的软件版本的详细信息。
2.12.3 防ddos攻击
??通过nginx自带的限流模块能够在一定程度上防止小规模ddos攻击流量。??包括,限制每个IP的最大并发连接数和每个IP的每秒最大请求数,同时配合限制客户端请求最大body大小超时时间等参数来防止ddos攻击。配置举例如下:
# 定义一个zone用于限制单个IP的并发连接数量
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
# 定义一个zone用于限制单个IP的每秒请求数,限制平均速率为5rps
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
# 在server级别上限制但IP最多10个并发连接
# 在server级别上限制单个个IP的每秒最大请求数为10
server {
limit_conn conn_limit_per_ip 10;
limit_req zone=req_limit_per_ip burst=10 nodelay;
}
# 限制客户端请求body部分的内存缓冲区大小,超过则写入临时文件
# request body is written into a temporary file
client_body_buffer_size 128k;
# 限制最大可以上传的body大小
client_max_body_size 10m;
# 限制客户端请求header部分的缓冲区大小
client_header_buffer_size 3m;
# 限制客户端请求头最大允许的缓冲区个数和每个缓冲区的大小
large_client_header_buffers 4 256k;
# 客户端请求body部分读超时时间
client_body_timeout 3m;
# 客户端请求header部分读超时时间
client_header_timeout 3m;
??
2.11.4 防止range请求攻击
HTTP 1.1协议允许在一个请求中指定任意个数的子range的请求。无限制的多重范围请求容易受到拒绝服务攻击的影响,因为请求许多重叠的数据范围所需的工作量与尝试为请求的数据的多个部分提供服务所消耗的时间、内存和带宽相比微不足道。服务器应该忽略、合并或拒绝过分的范围请求,例如请求超过两个重叠范围或在单个集合中请求许多小范围,特别是当这些范围的请求没有明显原因的无序时。多部分范围请求不适用于支持随机访问。?nginx的核心模块提供了max_ranges参数用于限制一个请求最多可以有多少个子range。配置举例如下:
http{
server{
max_ranges 2;
......
}
}
2.11.5 禁止部分HTTP method
大部分的http请求只要GET、HEAD、POST就足够了,TRACE, DELETE, PUT and OPTIONS等这些method一般来说都是有一定危险性的,因此如果确认没有这些method的需求,应该明确禁止这些method的请求,配置举例如下:
location / {
limit_except GET HEAD POST { deny all; }
}
??以上配置禁止除了GET、HEAD、POST之外的method的请求。
2.11.6 referer限制
??为了避免网站的资源被盗用,可以在nginx上配置referer限制,限制只有白名单中的网站可以引用本网站的内容。配置举例如下:
location /path/to/resource {
valid_referers none blocked example.com;
if ($invalid_referer) {
return 403;
}
# 其他相关配置...
}
2.11.7 ua限制
为了保护服务器免受爬虫、脚本和其他自动化的网页获取方法的攻击,可以明确拒绝部分用户代理。例如,像wget这样的应用程序可以检索整个文档根目录结构,使其成为有用的拒绝服务(DoS)攻击者,或者仅仅是访问网站上的安全文件。譬如通过下述配置禁止curl和wget的http请求:
原创文章,作者:网络技术联盟站,如若转载,请注明出处:https://www.sudun.com/ask/49811.html