Kubernetes Ingress 400 Bad Request 问题处理过程复盘
文章目录
复盘
突然接到同事反馈说是线上某些接口偶尔会报400 Bad Request
错误,去现场复现了下问题,习惯性的打开开发者工具查找错误细节,发现Query String Parameters
和Cookie
都异常的长,直觉告诉我跟这个应该有关系。
查了下资料Nginx 400 Bad Request通常都是Request Header过大所引起的,结合我这里Query String Parameters
和Cookie
都特别长的情况,那应该就是这个问题没跑了。
于是顺着这个方向去寻找解决方法,网上的资料都提到了要调整下边两个参数,
client_header_buffer_size
large_client_header_buffers
于是看了下我最外层的Nginx
(我这里的架构:LVS(公网)->Nginx->LVS(内网)->Kubernetes Ingress->Pod)的配置:
|
|
发现已经是加大过的了所以并没有立即无脑加大,而是想能不能从日志中分析一下具体报错的原因呢,于是打开error_log
一顿狂grep
,然而并没有什么发现。
后来看到了这个问题,原来是要调整error_log
的级别,果断debug
大法,本以为这下就找到错误原因呢,结果又一次扑空。
重新理了一下思路,如果真是Request Header
过大导致了这层Nginx
返回的400 Bad Request
的话 error_log
中一定是能扑捉到具体错误原因,然后这里并没扑捉到说明400
不是这层Nginx
返回的,既然不是它那就要怀疑下一层了,这下焦点就到了Kuberneres ingress-nginx
控制器这里了,根据ingress-nginx文档查看了下client_header_buffer_size
和large_client_header_buffers
参数对应的选项和默认值:
name | type | default |
---|---|---|
client-header-buffer-size | string | “1k” |
large-client-header-buffers | string | “4 8k” |
默认值都不大,确实有超过限制的可能,于是适当的调大了相应值。
|
|
结果又一次扑空了,问题依旧,于是再次调整error_log
级别
|
|
日志中还是一样没扑捉到具体错误原因,到这里一切迹象都把矛头指向了后端服务。
果断进到Pod中,本地访问了下接口,
wow~200
终于回来了,但是这和之前的推断矛盾了,难道400
不是后端服务返回的?
既然本地访问是没问题的,那通过ingress-nginx
的svc
ip访问呢?
结果毫无意外是400 Bad Request
,不然就诡异了。
到这里问题似乎越来越近了,重新整理下思路,如果真是ingress-nginx
的问题,那么错误日志中为什么扑捉不到相关信息呢,可见不是。
焦点回到后端服务上,这次我打算伪造一个假的头增大Request Header
,看看会发生什么?
wow~,400 Bad Request
了,证明了之前的推断确实是Request Header
过大导致了后导服务返回了400
了错误。
这时又有新的问题了:
为什么本机访问没问题,走
ingress-nginx
反向代理后就有问题了?
推断ingress-nginx
反代时在请求后端服务时追加了Header
内容导致长度达到了后台服务的限制从而报了400 Bad Request
,果然谷歌找到了这个issue,原来从0.14.0
开始nginx-ingress-controller
增加了一个proxy-add-original-uri-header
参数且默认为true
,翻译成对应Nginx
参数就是:
|
|
它的作用是在反向代理请求后端服务时会将原始请求加到Header
里然后带给后端服务,这就解释了为什么本机访问没问题走ingress-nginx
后就有问题。原因找到了,解决也很简单了,修改
|
|
然后应该生效就可以了,完整nginx-configuration ConfigMap
:
|
|
总结
-
问题虽然不复杂但是排查着实费了些功夫,如果将问题排查的经过倒过来先定位错误是否是服务返回的可能会少走很多弯路。
-
官方也意识到
proxy-add-original-uri-header
参数默认为true
可能是个问题,This makes it configurable if a location adds an X-Original-Uri header to the backend request. Default is “true”, the current behaviour.
Our use case, we have some internal services where the URL can be quite long, though still quite below nginx’s large_client_header_buffers default limit. But by automatically adding the request URI as an additional header on the backend proxy request, a similar request header size limit from the backend web server, e.g. Jetty or another nginx, is exceeded.
所以从
0.26.0
开始参数的默认值改为了false
Change the default value for
proxy-add-original-uri-header
from true to false, introduced in #2353. This default adds a header that can trigger an errorHTTP/1.1 431 Request Header Fields Too Large
.
参考
https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md
文章作者 XniLe
上次更新 2024-10-14