Knox无法处理302 response的问题


发布于 2024-07-30 / 45 阅读 / 0 评论 /
Knox代理Spark History WebUI的问题

1.问题描述

当前Knox和Spark History Server都是容器化部署,Knox转发Spark History WebUI的地址是pod的名称,当Spark History Server返回302 response时,response header中的Location是内网地址,也就是“pod名称+端口号+request path”,浏览器无法识别,导致无法访问此网站。

如下图所示:

如果Spark History Server返回非301或302 response时,则浏览器能正常得到200 response。

2.源码分析

经过多次测试,发现异常是偶现的,这里就需要通过源码来看看整个请求的过程。

(1)请求的入口在HistoryServer.loaderServlet。

(2)如果request path中不存在attemptId,则通过provider获取最后一个attemptId。

(3)然后会调用loadAppUi方法,把application ui页面信息加载到缓存中,缓存操作对象为ApplicationCache。缓存application的大小由参数spark.history.retainedApplications设置,默认值为50。ApplicationCache中会用到CacheLoader,这是个guava的类,当缓存key不存在或者缓存的内容过期时,调用get方法会触发一次重新的load。

(4)如果(2)中的attemptId,会进行一次重定向,返回302 response。

这里重定向触发的原因有:缓存中不存在此application;任务正在执行,本地缓存的内容与HDFS上event信息有差异,也就是缓存过期了。

3.解决方案

在重定向的地方,将redirect url进行改写,改成类似如下格式:

      var redirectUrl = httpResponse.encodeRedirectURL(requestURI + queryStr)
      val reqHost = httpRequest.getHeader("X-Forwarded-Host")
      val proxyPrefix = s"${redirectProtocol}://${reqHost}/${redirectPrefix}/"
      redirectUrl = s"${httpResponse.encodeURL(proxyPrefix)}${redirectUrl}"
      httpResponse.sendRedirect(redirectUrl)

这里reqHost需要根据需要进行修改,因为有些请求不会带有X-Forwarded-Host header。

有三处需要进行修改:

(1)core/src/main/scala/org/apache/spark/ui/JettyUtils.scala中的ResponseWrapper.sendRedirect方法

(2)core/src/main/scala/org/apache/spark/deploy/history/HistoryServer.scala中的loaderServlet属性定义中

(3)core/src/main/scala/org/apache/spark/deploy/history/ApplicationCache.scala有个内部类方法ApplicationCacheCheckFilter.doFilter

修改完成后,对于redirect的相应,请求地址会变成远程地址,而不是内网地址。

当然,这里是从spark侧对问题进行修复。可能也可以从knox对response header进行修改,这里需要进一步的探索。