触类旁通,从400错误看Nginx常见故障与修复

故障简述

小明某天中午在线优化一个敏感服务的Nginx配置时,发现5分钟内Nginx errorlog里出现了大量400错误,于是迅速回滚了Nginx配置。

故障详情

原来的Nginx配置存在重复或者需废弃的内容,于是在多次diff了新旧两份配置内容后,小明认为最新配置是不影响业务的,因此在线推送更新配置后,直接reload了Nginx,出于double check原则,在线观察了5分钟Nginx日志:

/etc/init.d/Nginx reload

tail -f /var/log/access.log

tail -f /var/log/error.log

发现出现大量类似下面的400错误:

1.1.1.1 - - [21/Feb/2017:13:53:00 +0800] "GET /x/get?id=hh2&aid=11642618&currenttime=1487656379&num=21461799&flag=1&host=23000&user=60%2E220%2E132%2E66&logintime=1487654812&username=howru%2D10 HTTP/1.1" 400 166 "-" "-" "223.252.221.10" "0.000" "-" "-"

400错误的产生,很可能影响服务端或客户端的后续业务逻辑判断,因此需要引起重视。

处理过程

节点1

当时回滚配置后,小明先在搜索引擎查找了Nginx 400错误的可能原因和解决办法,初步确定有下面两种可能:1是空主机头,2是请求包头过大。

小明跟客户端同学确认了客户端请求方式,发现他们使用的是类似telnet的方式发起的http请求,类似下面的:

telnet 111.11.1.1 7600

Trying 111.11.1.1...

Connected to kk-none-none-ip11.x.com

Escape character is '^]'.

为了方便后续排查,小明参考线上环境临时搭建了一套Nginx测试环境,重现了故障:

GET /x/get?id=hh2&aid=11642618&currenttime=1487656379&num=21461799&flag=1&host=23000&user=60%2E220%2E132%2E66&logintime=1487654812&username=howru

<html>

<head><title>400 Bad Request</title></head>

<body bgcolor="white">

<center><h1>400 Bad Request</h1></center>

<hr><center>Nginx</center>

</body>

</html>

Connection closed by foreign host.

后来小明了解到原来客户端不是从代码的http库调用, 而是按照上面的方式走TCP/telnet传递http参数来调用服务端http接口。但是为什么一样的客户端请求方式,旧配置完全ok,新配置则会出现大量400错误?

节点2

至此,小明怀疑自己没有完全diff出新旧两份配置的差别,于是他使用vimdiff再次对比新旧两份配置。下面仅贴出关键配置:

旧配置:

modern_browser msie 0;

modern_browser gecko 0;

modern_browser opera 0;

modern_browser safari 0;

modern_browser konqueror 0;

location / {

root /home/web/Nginx/html;

index index.html index.htm;

}

location /favicon.ico {

log_not_found off;

}

error_page 500 502 503 504 /50x.html;

location = /50x.html {

root /home/web/Nginx/html;

}

location = /60x.html {

root /home/web/Nginx/html;

access_log logs/browserblock.log;

}

location ~ ^/(api|newapi)/get {

#access_log off;

proxy_pass http://myfcgi-get-proxy;

proxy_set_header X-REAL-IP $remote_addr;

keepalive_timeout 900;

proxy_read_timeout 900;

}

location ~ ^/(api|newapi|admin)/ {

#access_log off;

proxy_pass http://myfcgi-take-proxy;

proxy_set_header X-REAL-IP $remote_addr;

keepalive_timeout 900;

proxy_read_timeout 900;

}

location ~ (.*) {

rewrite ^ /60x.html;

}

}

新配置:

location ~ ^/(api|newapi|admin)/ {

set $xheader $remote_addr;

if ( $http_x_forwarded_for != '' ){

set $xheader $http_x_forwarded_for;

}

proxy_set_header X-Real-IP $xheader;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header Host $http_host;

proxy_redirect off;

proxy_http_version 1.1;

proxy_set_header Connection "";

proxy_pass http://myfcgi-take-proxy;

}

error_page 500 502 503 504 /50x.html;

location = /50x.html {

root /home/web/Nginx/html;

}

本次排查中,小明考虑的重点是新配置里遗漏了某些配置,于是他把location ~ (.*)的相关逻辑加上,发现问题依旧:

{

location = /60x.html {

root /home/web/Nginx/html;

access_log logs/browserblock.log;

}

location ~ (.*) {

rewrite ^ /60x.html;

}

节点3

既然前面往缺失配置的思路走不通,下面就按照新增配置的思路排查,结果发现新配置增加了一些包头信息,小明怀疑是请求包过大,于是优先排查了Nginx针对包头大小的设置,其中有这么几个配置:

  • client_header_buffer_size:默认是1k,所以header小于1k的话是不会出现问题的。

  • large_client_header_buffers:该命令用于设置客户端请求的Header头缓冲区的大小,默认值为4KB。

  • 客户端请求行不能超过large_client_header_buffers指令设置的值,客户端请求的Header头信息不能大于large_client_header_buffers指令设置的缓冲区大小,否则会报“Request URL too large”(414)或者“Bad-request”(400)错误,如果客户端Cookie信息较大,则须增加缓冲区大小。于是小明将client_header_buffer_size和large_client_header_buffers都设置为128k。结果问题也重现了。

接下来,小明发现新配置中多了“proxy_set_header Host $http_host;”查找了Nginx官方文档发现跟$http_host类似功能的还有$server_name和$host等变量,在他将$http_host更换成$host后,问题修复了。

原因分析

根据Nginx官方文档介绍,400状态码含义如下:

A client MUST include a Host header field in all HTTP/1.1 request messages . If the requested URI does not include an Internet host name for the service being requested, then the Host header field MUST be given with an empty value. An HTTP/1.1 proxy MUST ensure that any request message it forwards does contain an appropriate Host header field that identifies the service being requested by the proxy. All Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad Request) status code to any HTTP/1.1 request message which lacks a Host header field.

上面是http1.1的rfc关于host部分的解释,从上面我们了解到如果一个http1.1的请求没有host域,那么server应该给client段发送400的状态码,表明这个请求server不能处理。而对于Nginx server来说,也遵循这样的方式,说明client发送了一个无效的请求,Nginx server无法处理,于是返回了400的状态码。

另外,关于$host和$http_host这两个变量的区别如下:

$host

This variable is equal to line Host in the header of request or name of the server processing the request if the Host header is not available.

This variable may have a different value from $http_host in such cases: 1) when the Host input header is absent or has an empty value, $host equals to the value of server_name directive; 2)when the value of Host contains port number, $host doesn't include that port number. $host's value is always lowercase since 0.8.17.

本次故障中,客户端的调用方式没有使用host 参数,传递了空的Host头给服务端,一旦Nginx设置了proxy_set_header Host $http_host,空Host头就传给了后端。然而,在http 1.1的规范中,Host只要出现空,就会返回400,所以出现了这个故障。而对于需要在Host字段里带上端口信息的,则仍需要配置proxy_set_header Host $http_host。

最后,需要注意的是,400错误不一样会影响业务,需要看具体的业务处理逻辑,比如使用nagios的check_tcp插件对Nginx server端口做检测或者使用keepalived的tcp_check功能对后端Nginx端口的存活做检测,这两种情况都会在Nginx errorlog中产生400的请求。

原因也很简单,就是一般tcp check的方式,就是建立tcp连接,但是没有发送任何数据,当然也没有Host头,然后再reset或者四次挥手断开连接。

经验教训

运维规范

细心的同学会发现本次配置更新是在大中午操作,而且也没有在测试环境测试通过,这在流程上是不严谨的。虽然Nginx等web服务的配置更新基本上通过热更就可以了,但没有灰度测试或者在测试环境测试,一是无法提前发现问题,二是无法控制业务影响。所以,在运维规范上看,即使是热更也应当在测试环境测试正常后再同步到线上,其他的更新则应在业务低谷时操作。

技术学习方法

本次故障的产生,很大程度上就是运维同学不理解Nginx变量的定义和区别,直接从搜索引擎上找了些配置,检查觉得正确就推到了线上。这里仍需要重申的是,以官方文档为准!互联网上很多知识或者配置有各种各样的问题,随时都有暗坑在里边,只有啃过官方文档才能避免误读。

Web日志分析

针对这里的Nginx错误日志查看,我们看到小明是用在线命令查看的,其实现在有很多web日志分析工具或系统,比如ELK(ElacticSearch+LogStash+Kibana),只需要配置好grok正则,是可以通过可视化界面实时监控web服务质量的。

引申

上面介绍了Nginx 400错误的可能原因和解决办法,但实际工作中,我们遇到的可不止这么一点。于是,由此引申出去的是,针对那些Nginx常见错误如何去排查和解决。

  • 403错误

403是很常见的错误代码,一般就是未授权被禁止访问的意思。

可能的原因有两种:

  1. Nginx程序用户无权限访问web目录文件

  2. Nginx需要访问目录,但是autoindex选项被关闭

修复方法:

  1. 授予Nginx程序用户权限读取web目录文件

  2. 设置autoindex目录为on

location /path/to/website/folder {

...

autoindex on;

... }

  • 413错误

在上传时Nginx返回了413错误:“413 Request Entity Too Large”,这一般就是上传文件大小超过Nginx配置引起。

修复方法:

  1. 在Nginx.conf增加client_max_body_size的设置,这个值默认是1M,可以增加到8M以提高文件大小限制;

  2. 如果运行的是php,那么还要检查php.ini,这个大小client_max_body_size要和php.ini中的如下值的最大值一致或者稍大,这样就不会因为提交数据大小不一致出现的错误。

post_max_size = 8M

upload_max_filesize = 2M

  • 502错误

Nginx 502 Bad Gateway的含义是请求的PHP-CGI已经执行,但是由于某种原因(一般是读取资源的问题)没有执行完毕而导致PHP-CGI进程终止。一般来说Nginx 502 Bad Gateway和php-fpm.conf的设置有关。

修复方法:

1、查看FastCGI进程是否已经启动

ps -aux | grep php-cgi

2、检查系统Fastcgi进程运行情况

除了第一种情况,fastcgi进程数不够用、php执行时间长、或者是php-cgi进程死掉也可能造成Nginx的502错误。

运行以下命令判断是否接近FastCGI进程,如果fastcgi进程数接近配置文件中设置的数值,表明worker进程数设置太少。

netstat -anpo | grep "php-cgi" | wc -l

3、FastCGI执行时间过长

根据实际情况调高以下参数值

fastcgi_connect_timeout 300;

fastcgi_send_timeout 300;

fastcgi_read_timeout 300;

  • 504错误

Nginx 504 Gateway Time-out的含义是所请求的网关没有请求到,简单来说就是没有请求到可以执行的PHP-CGI。

Nginx 504 Gateway Time-out一般与Nginx.conf的设置有关。

头部太大这种情况可能是由于Nginx默认的fastcgi进程响应的缓冲区太小造成的, 这将导致fastcgi进程被挂起,如果你的fastcgi服务对这个挂起处理的不好,那么最后就极有可能导致504 Gateway Time-out。

默认的fastcgi进程响应的缓冲区是8K,可以调大以下参数:

  • fastcgi_buffer_size 128k;

    fastcgi_buffers 8 128k;

  • fastcgi_busy_buffers_size 由 128K 改为 256K;

    fastcgi_temp_file_write_size 由 128K 改为 256K。

此外,也可能是php-cgi的问题,需要修改php.ini的配置:

  1. 将max_children由之前的10改为30,这样操作是为了保证有充足的php-cgi进程可以被使用。

  2. 将request_terminate_timeout由之前的0秒改成60秒,这样使php-cgi进程处理脚本的超时时间提高到60秒,可以防止进程被挂起以提高利用效率。

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,关于nginx配置与常见错误解决方法,我可以给您一些简单的介绍和建议。 首先,nginx作为一个开源的高性能Web服务器和反向代理服务器,其配置文件的语法非常简单,易于学习和使用。一般来说,nginx的配置文件主要包含以下几个部分: 1. 全局块:主要是一些全局参数的设置,比如worker_processes、pid、error_log等。 2. events块:主要是一些与事件相关的参数设置,比如worker_connections等。 3. http块:主要是配置http协议相关的参数,比如http服务器、虚拟主机、upstream负载均衡等。 4. server块:主要是配置http服务器相关的参数,比如监听端口、域名、SSL证书等。 当然,nginx的配置还可以包括一些额外的模块配置,比如gzip、fastcgi等等。 关于常见错误解决方法,以下是一些可能会遇到的问题和解决方法: 1. nginx启动失败:这个问题可能是由于配置文件语法错误、端口占用等原因导致的。解决方法可以通过查看日志、检查配置文件、查看端口占用情况等。 2. 403 Forbidden错误:这个错误通常是由于权限不足导致的。解决方法可以通过修改文件或目录的权限、检查SELinux等。 3. 404 Not Found错误:这个错误通常是由于资源不存在导致的。解决方法可以通过检查文件路径、检查文件权限、检查文件是否存在等。 4. 502 Bad Gateway错误:这个错误通常是由于后端服务器连接失败导致的。解决方法可以通过检查后端服务器是否正常运行、检查网络连接等。 总之,nginx作为一个高性能的Web服务器和反向代理服务器,其配置和错误处理都需要一定的经验和技巧,建议您多练习和学习,掌握一些常用的技巧和方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值