- 发布于
nginx使用小结
- Authors
- Name
- 田中原
nginx使用小结
前言
今天想给大家分享一下日常工作中遇到的nginx相关问题的一些经验总结。
nginx作为web服务器几乎是我们公司项目生产环境的标配,nginx常见的使用场景有:‘静态资源代理服务’,‘反向代理服务’,‘邮件代理服务’,‘负载均衡’等,我们大部分项目都会使用nginx来代理html,图片和js等静态资源,以及反向代理后端的java服务。以下总结了一些在日常实际开发过程中遇到的一些nginx配置相关的问题,以及排查nginx配置问题时使用到的一些命令和方法。
如何快速找到nginx的配置文件
如果我们要查看或者调整nginx的配置,首先要找到nginx.conf文件,以及nginx的可执行文件或者说是nginx的sbin文件。
nginx的默认安装位置,在不同的操作系统下安装存在一定的差异
nginx在不同操作系统下安装时,默认的安装位置不太一样。使用安装方式的不同,也会导致安装位置的不同。同时nginx命令也不一定都配置到了全局的环境变量中,所以经常会遇到要查找配置文件位置的情况。
在mac系统下,nginx命令安装在/usr/local/bin/nginx
,mac下的很多全局命令的软连都在这个位置,实际可执行文件的位置为/usr/local/Cellar/nginx/1.21.3/bin/nginx
。配置文件的位置为/usr/local/etc/nginx
。
在Linux系统下,nginx的安装位置为/usr/local/nginx
或 /etc/nginx
或 /usr/local/etc/nginx
。
我们通常到usr/local
文件目录下去查找,一般是能找到的,也有找不到的时候。
快速查找文件安装位置命令
mdfind
在mac系统下可使用 mdfind 查找文件。
mdfind -name nginx.conf
which
which 命令会在环境变量$PATH设置的目录里查找符合条件的文件,一般是可执行文件。mac和linux都可用。
which nginx
whereis
whereis 可在指定目录中查找二进制文件,源代码文件和man手册页。在linux下可用
whereis nginx
nginx -V
使用nginx -V命令,可查看nginx的版本和编译版本以及配置项参数
nginx -v
nginx version: nginx/1.21.3 built by clang 11.0.0 (clang-1100.0.33.17) built with OpenSSL 1.1.1l 24 Aug 2021 TLS SNI support enabled configure arguments:
# nginx 安装路径,和root的默认地址
--prefix=/usr/local/Cellar/nginx/1.21.3
# 可执行文件路径
--sbin-path=/usr/local/Cellar/nginx/1.21.3/bin/nginx
--with-cc-opt='-I/usr/local/opt/pcre/include -I/usr/local/opt/[email protected]/include'
--with-ld-opt='-L/usr/local/opt/pcre/lib -L/usr/local/opt/[email protected]/lib'
# 配置文件路径
--conf-path=/usr/local/etc/nginx/nginx.conf
# nginx进程的主进程 id 存储路径
--pid-path=/usr/local/var/run/nginx.pid
--lock-path=/usr/local/var/run/nginx.lock
# 代理产生的临时文件路径
--http-client-body-temp-path=/usr/local/var/run/nginx/client_body_temp
--http-proxy-temp-path=/usr/local/var/run/nginx/proxy_temp
--http-fastcgi-temp-path=/usr/local/var/run/nginx/fastcgi_temp
--http-uwsgi-temp-path=/usr/local/var/run/nginx/uwsgi_temp
--http-scgi-temp-path=/usr/local/var/run/nginx/scgi_temp
# 日志存储的路径
--http-log-path=/usr/local/var/log/nginx/access.log --error-log-path=/usr/local/var/log/nginx/error.log --with-compat --with-debug --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-ipv6 --with-mail --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module
以上大部分的配置是可以修改的,除了可执行文件路径是在安装的时候指定,不可修改的。另外一些例如pid路径和日志路径在配置文件中也可配置。
启动、停止和重启nginx
直接执行nginx命令即可启动nginx。
nginx
# or
/usr/local/Cellar/nginx/1.21.3/bin/nginx
当nginx.conf文件被修改之后,必须要重启nginx,才能使配置文件生效。
nginx -s reload
reload会先检查配置文件的配置内容是否正确,如果正确的话,会重新加载配置文件。
nginx -s quit和nginx -s stop都可以停止nginx,前者更优雅,会在退出前完成所有已经连接的请求,然后再退出。
也可以使用kill命令来退出nginx,它的效果和nginx -s [signal]是一样的。
kill -s QUIT [pid]
这里的pid为nginx主进程的pid,可以通过查看主进程的存储文件或者使用ps命令就找到主进程id。
ps -ax | grep nginx
nginx进程管理
nginx使用多进程来处理用户请求,有一个主进程(master)和多个worker进程,进程之间通过信号来通信。
当退出主进程时,主进程会向worker进程发出退出的信号,通知所有子进程退出。
当worker进程意外退出时,同样会向主进程发射信号,主进程则会重新拉起worker进程。
nginx日志管理
当我们在排查页面问题的时候,要善于使用nginx日志。
通过访问日志,你可以得到用户跳转来源、使用终端信息、某个URL访问量等相关信息;
通过错误日志,你可以得到系统某个服务或server的性能瓶颈等。
利用好日志,可以帮助我们快速的排查到问题。
nginx日志有两种,一种是访问日志,一种错误日志。
默认情况下,访问日志和错误日志都是关闭的,这也是nginx性能优化的一个常用的处理方式。
#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;
log_format
定义日志格式的指令,用法 log_format name format [format...]
,在http和server中可用。
nginx内置了很多变量,在打印日志的时候可以直接使用。下面罗列了一些常用的变量。
$status # http状态码,记录请求返回的状态,例如200、404、301等
$request # 用户的http请求起始行信息
$time_local # 记录访问时间与时区
$http_referer # 记录此次请求是从哪个链接访问过来的,可以根据referer进行防盗链设置
$remote_addr # 记录访问网站的客户端地址
$remote_user # 远程客户端用户名称
$http_user_agent #记录客户端访问信息,例如:浏览器,手机客户端
$body_bytes_sent #服务器发送给客户端的响应body字节数
$http_x_forwarded_for #当前端有代理服务器时,记录客户端真正的地址,前提是代理服务器上也要进行相关的x_forwarded_for设置
**请求头变量:**请求头中的参数可通过 http_{参数}
来打印,例如http_user_agent
**响应头变量:**也有对应的变量前缀sent_http_{参数}
,例如sent_http_content_range
access_log
定义访问日志的路径和格式的指令
用法acess_log path [format [buffer=size | off]]
,在http、server和location中可用。
buffer用于指定缓存的日志的大小。
access_log logs/access.log main buffer=32k;
error_log
错误日志的路径和级别指令
用法error_log path level
,在全局上下文,http、server和location中可用。
错误日志的级别有 debug,info,notice,warn,error,crit一般不要定义太低的级别,对性能的消耗比较大。
查看日志文件
tail命令可用于查看文件的内容,参数-f
是循环读取的意思,并且不断的刷新。
tail -10f /usr/local/var/log/nginx/access.log
上述命令的意思是,循环从文件的尾部读取10条内容,可以实时查看访问日志。
生产中遇到的问题汇总
4A登录到系统的地址和方式自定义问题处理
背景说明
我们项目中通过一个固定的路由地址/redirect
来处理第三方请求,用户必须通过get请求到这个地址,并且将参数挂到query信息上。
数管各个省端对4A登录要求不同,很多省不希望通过get的方式,希望通过post请求来传递参数,而且要自定义路由地址。
这里有两个问题,第一,post传参的话在前端是拿不到的,必须要经过后端处理。第二,各个省端如果要使用不同的路由地址,前端要修改路由配置机制。
实现方法
我们最终的处理方法是在nginx上添加配置,将post请求直接转到后端处理。
保留之前的第三方登录方式,通过判断请求的方法来做不同的处理。
如果是post请求,则转发到后端服务处理,后端怎将参数和页面一并返回给前端,然后进入到系统的第三方登录流程中。
如果是get请求,则直接返回前端页面。
location /redirect {
# 转发后端地址
if (request_method !~ ^(POST)){
rewrite ^ /index.html;
}
proxy_pass http://{ServerHost}/api/platform-url;
}
文件下载失败问题处理
问题描述
用户在下载文件时,比较小的文件能够正常下载成功,当文件超过一定大小时则下载失败。
然后就查看了错误日志,如下,发现是权限问题,打开nginx/proxy_temp/xxxx
文件失败。
2021/09/07 18:35:56 [crit] 19075#0: *26697 open() "nginx/proxy_temp/xxxx" failed (13: Permission denied) while reading upstream, client: 133.96.97.1, server: 123.com, request: "GET /api/system/temp/filedownload?dir=&fileName=dis_DIS20210818170604519_2021090317335331.zip&reallyName=dis_DIS20210818170604519_2021090317335331.zip HTTP/1.1", upstream: "http://xx.xx.245.86:18080/api/system/temp/filedownload?dir=&fileName=dis_DIS20210818170604519_2021090317335331.zip&reallyName=dis_DIS20210818170604519_2021090317335331.zip", host: "xx.xx.245.82:8080", referrer: "http://xx.xx.245.82:8080/asset-event/check-list?_r=1631009283310"
nginx数据传输过程分析
nginx默认会开启proxy_buffering,所有的请求都会先缓存然后再返回给用户,当proxy_buffers写满之后,才开始传输。
控制传输过程的指令有,处理流程如下图
proxy_buffering on;
proxy_buffers 4 32k;
proxy_max_temp_file_size 64k;
proxy_temp_file_write_size 32k;
proxy_busy_buffer_size 32k;
解决方法
再回溯一下上面的问题,文件下载的过程超出了buffer的限制,但是在写入临时文件的时候,写入失败导致报错。最终通过修改nginx相关文件的权限来解决写入失败问题。
chmod 777 [nginx path]
$http_x_forwarded_for
和$remote_addr
如果客户端到nginx服务器中间还有别的代理服务器,我们可以通过在访问日志中打印$http_x_forwarded_for
和$remote_addr
变量来判断。
$http_x_forwarded_for
打印的真实的客户端地址,前提是我们在配置反向代理的是有对其进行设置。
location /api {
# 后台API地址 如:http://localhost:3000/api/
proxy_pass http://${ip}:${port}/api/;
# 客户端ip,携带到服务器端
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
set $Real $http_x_forwarded_for;
if ( $Real ~ (\d+)\.(\d+)\.(\d+)\.(\d+),(.*) ){
set $Real $1.$2.$3.$4;
}
proxy_set_header X-Real-Ip $Real;
}
$remote_addr
记录了客户端的地址,如果中间经过多层代理,这个地址则是代理服务器的地址。
我们可以通过对比这两个地址,来确定从客户端到服务端是否经过了其他的代理,比如vpn。
X-Frame-Options 防止站点被劫持
X-Frame-Options头主要是为了防止站点被别人劫持,iframe引入
nginx配置形式:
add_header X-Frame-Options ALLOWALL; #允许所有域名iframe
add_header X-Frame-Options DENY; #不允许任何域名iframe,包括相同的域名
add_header X-Frame-Options SANEORIGIN; #允许相同域名iframe,如a.test.com允许b.test.com
add_header X-Frame-Options ALLOW-FROM uri; #允许指定域名iframe,
配置可以放入到nginx的 http 或者 server 中