• 一次jenkins-kubernetes服务报错排查记录 (Request Header Fields Too Large)


    背景

    最近我们自研的云原生发布平台新支持了一种发布场景,简单来说client里面会把k8s里面的各资源文件比如:

    deploy.yaml

    service.yaml

    ingress.yaml

    pvc.yaml

    分别用字符串变量保存,然后通过jenkins开源库sdk传给一个带参数的jenkins pipeline job,来触发该job的运行,但是client在调用jenkins job的build方法(实际post的总数据是大于8KB)的时候程序了报了如下异常,

    Exception:Request Header Fields Too Large

    排查

    由于我们的jenkins和client发布平台程序都是运行在K8s里面,client里面的用的是jenkins开源库通过Ingress域名访问的jenkins master,所以开始怀疑是Kubernetes Ingress Nginx的参数设置的过小导致,跟Header有关的nginx参数如下:

    # body (和本次异常无关,顺带记录一下)
    client_body_buffer_size 10K; 
    client_max_body_size 8m;
    # header
    client_header_buffer_size 2m;
    large_client_header_buffers 4 8k;

    含义如下:

    client_body_buffer_size:

    Nginx分配给请求数据的Buffer大小,如果请求的数据小于client_body_buffer_size直接将数据先在内存中存储。如果请求的值大于client_body_buffer_size小于client_max_body_size,就会将数据先存储到临时文件中,使用client_body_temp 指定的路径中,默认该路径值是/tmp/.
    所以配置的client_body_temp地址,一定让执行的Nginx的用户组有读写权限。否则,当传输的数据大于client_body_buffer_size,写进临时文件失败会报错。

    client_max_body_size

    默认 1M,表示 客户端请求服务器最大允许大小,在“Content-Length”请求头中指定。如果请求的正文数据大于client_max_body_size,HTTP协议会报错 413 Request Entity Too Large。就是说如果请求的正文大于client_max_body_size,一定是失败的。如果需要上传大文件,一定要修改该值

    client_header_buffer_size:

    假设client_header_buffer_size的配置为1k,如果(请求行+请求头)的大小如果没超过1k,放行请求。如果(请求行+请求头)的大小如果超过1k,则以large_client_header_buffers配置为准

    large_client_header_buffers:

    请求行+请求头的总大小不能超过32k(4 * 8k)

    在我们调整k8s ingress nginx的相关配置后

    # header
    client_header_buffer_size 2m;
    large_client_header_buffers 4 2m;

    注意上述配置,可以不用修改k8s ingress controller 的全局配置,可以在应用级别配置,需要用k8s ingress controller的Snippets配置,具体参考:Advanced Configuration with Snippets | NGINX Ingress Controller

    使用上述配置后,报错依然没有消失,为了排除nginx的干扰,我们直接通过访问service来继续测试:

    curl --user user:pwd http://jenkins-svc.demo:8080/jenkins/job/my-test/config.xml

    发现依然报错,然后我们在去看jenkins k8s master的日志,发现了端倪:

    WARNING eclipse.jetty.http.HttpParser#parseFields: Header is too large 8193>8192

    到这里可以确认是jetty容器的设置,导致接受到的请求被解析失败了

    修复

    我们的jenkins是运行在k8s里面,而jetty又是jenkis内嵌的服务器,还没有办法直接调参数,传统的jetty用法,我们是把应用丢进jetty的webapps目录和tomcat类似,所以是可以直接去调ini文件参数的,当如果应用是引用jetty-core.jar,把web容器做成了内嵌服务,那么很多参数都是硬编码的,所以调起来很不方便。

    经过查询,发现jenkins有个JENKINS_OPTS参数,可以调整,在k8s里面这个参数的内容是挂在secret里面,所以,我们直接在secret里面新增下面的内容即可(单位Byte),具体大小可以根据业务调整:

    "--requestHeaderSize=258140" (252KB)

    问题本质

    上面解决方案确实有用,但却不是这个问题的本质原因,经过查看我们用的jenkis开源库的源码:

            
                com.offbytwo.jenkins
                jenkins-client
                0.3.8
            

    发现其代码里,将post请求转为url拼接:

        public QueueReference build(Map params, Map fileParams, boolean crumbFlag) throws IOException {
            String qs = join(Collections2.transform(params.entrySet(), new MapEntryToQueryStringPair()), "&");
            ExtractHeader location = client.post(url + "buildWithParameters?" + qs,null, ExtractHeader.class, fileParams, crumbFlag);
            return new QueueReference(location.getLocation());
        }

    如果是在body的data里,就不会有问题,因为body是没限制的,而url的长度也就是header长度是有限制的,这也是为什么GET请求和POST的请求的一个最大区别,这个确实是坑了,因为源码是这样,所以为了快速修复,我们采用加大了jetty容器的header检查限制,这样可快速修复问题,也不用改动源码再次发布,那样影响会比较大。

    最后推荐大家,如果使用开源库操作jenkins,可以使用官网推荐的jenkins-rest:

            
                com.cdancy
                jenkins-rest
                0.0.27
            

    总结

    云原生时代,很多应用跑在Kubernetes里面很方便,但相应的请求访问链路也变多了,这样就会导致排查问题起来相对比较困难,因为不仅仅涉及应用程序,还会和应用请求经过的中间层nginx,业务网关,以及k8s本身网络等依赖的组件都可能有关联,排查问题时,可以用排除法,逐步缩小问题范围,这样排查起来就高效多了。

  • 相关阅读:
    不清楚Word如何批量转PDF?快来跟我学这两个妙招
    StackExchange.Redis 高并发下timeout超时问题如何解决?
    MySQL 全局锁、表级锁、行级锁,你搞清楚了吗?
    [ICCV-23] Paper List - 3D Generation-related
    Ansible概述以及模块
    开发视频会议系统:使用GPU解码渲染视频
    java中的IO缓冲流(高效流)---原始流的升级版
    第六篇Android--ImageView、Bitmap
    python 爬取文章并保存为pdf
    Vue的devtools安装教程
  • 原文地址:https://blog.csdn.net/u010454030/article/details/127854294