Kubernetes 使用 Zuul 网关时上游 Tomcat 400 报错问题排查
在 Kubernetes 里,使用 Zuul 转发请求到上游的 Tomcat 服务时,Tomcat 报了一个很诡异的错误:1
The host [zuul.host,zuul.host] is not valid
其中 zuul.host 是 Zuul 服务的域名。
看到这个报错时,有两个点很奇怪。一是 [zuul.host, zuul.host]
是怎么来的,为什么它重复了一遍。二是为什么 host 的值会是这个,应该是 Tomcat 服务的域名。
网上搜了一圈,没找到相关资料,只能一步步来挖了。
由于是 Tomcat 直接报错,未进入 Spring Boot 内部,配置的 Sentry 也没捕捉到该错误,只能通过添加 Tomcat 日志配置打印相应的 Header。
1 | # application.properties |
日志打印出了两个很有意思的值,Host 和 X-Forwarded-Host 的值是同样的,都为 [zuul.host, zuul.host]
。1
2Host: [zuul.host, zuul.host]
X-Forwarded-Host: [zuul.host, zuul.host]
这时可以确定,Tomcat 确实收到了一个诡异的请求,由于 Host Header 值无效导致 Tomcat 400 报错。但问题是,这请求是怎么来的呢?只能一步步往前追查,在 Zuul 服务中添加相应的日志配置。
1 | // ZuulFilter.java |
1 | LOG_HEADER: /api/v1/login/app Host-(zuul.host) X-Forwarded-Host-(zuul.host) |
从日志中可以看出,Zuul 服务接收到的请求 Host 和 X-Forwarded-Host 都为 zuul.host
,再加上 Zuul 默认情况下也会为转发的请求添加 X-Forwarded-Host,所以 Tomcat 收到的时候 X-Forwarded-Host 为两次的 zuul.host
。现在已经知道 X-Forwarded-Host 的值是如何来的,但 Host 值的原因还没找到。
没其他线索的情况下,我本地往 Tomcat 服务发送了几次请求,X-Forwarded-Host 的值从零个到多个,发现了些许端倪。在 Tomcat 打印的日志中,Host 和 X-Forwarded-Host 的值都是一样的。而当请求的 X-Forwarded-Host 值零个或一个时,请求返回 200。X-Forwarded-Host 值大于一个时,请求返回 400。这时我有了一个猜想,难道 X-Forwarded-Host 的值会被复制到 Host 吗?
按照这个想法,找到了相关的 ISSUE,原来 Ingress 在开启 use-forwarded-headers
后,会将 X-Forwarded-Host 的值复制到 Host,但由于没有考虑 X-Forwarded-Host 为多个值的情况,导致 Host 不合法。这个问题已经在 ingress-nginx
0.24 版本被修复。
请求的整个流程,如下图所示。
找到了问题根源,那解决起来就简单了。有以下三种方法:
- 升级
ingress-nginx
至 0.24 及以上版本,从根本上解决这个问题。 - 配置
zuul.addProxyHeaders = false
,使得 Zuul 不添加额外的X-Forwarded-Host
。 - Zuul 直接使用 Tomcat 服务的 Service Name 进行访问,不经过 Ingress。