问题描述

近期在我的Miku Cluster集群上出现了很多网络问题,包括但不限于:

  • 部署在k8s集群内的网站响应异常缓慢(Kuboard点一下得响应5秒多,有的时候直接404)
  • 部署在k8s集群内的网站行为异常(Alist无法加载js,存储驱动异常等)
  • 部署在k8s集群内的网站失联(404, 502, etc.)
  • 部署在k8s集群内的本地镜像库上传大的Layer有很大概率会超时
  • 使用scp命令在内网传输文件时,网速忽高忽低,经常stall

部分问题截图展示:

image.png

image.png

其实只是个小问题,然而却磕磕绊绊耗费了我两周的时间。下面记录我排查这一问题的过程。

环境概述

为了帮助读者理解,简要介绍一下目前的网络环境。

我有5台设备(节点),地理位置和带宽情况如下:

  • A: 湖北,上下带宽10/40Mbps
  • B: 香港,上下带宽5/5Mbps
  • C: 重庆,家用电信大带宽
  • D: 宁夏,家用广电小水管,大概有个30Mbps左右
  • E: 宁夏,家用广电小水管,大概有个30Mbps左右

为了使这些异地设备加入同一内网,使用了Softether VPN进行了组网(CIDR为10.10.10.0/24)。Softether Server使用二层网桥,没有使用Secure NAT,其他配置基本都是默认配置。

内网组建完毕后,在这5台设备上使用Sealos部署了Kubernetes集群,版本是1.27.7,即Miku Cluster(老二次元了)。网络插件使用Cilium。Ingress Controller使用Traefik,其Service NodePort监听0.0.0.0的某个http端口。

A节点作为国内域名解析的目标节点,即整个集群的Entrypoint。在A节点的操作系统上(k8s外)安装nginx,监听80和443端口,卸载tls后将对应流量转发到集群Traefik监听的http端口。

排查步骤

镜像push异常,排查Nginx

某天我发现GitLab CI/CD推送镜像失败了,原因是推送超时。

image.png

推送目标是Harbor搭建的本地镜像仓库,部署于k8s上,后端存储位于阿里云OSS。

后来经过反复测试,发现在手动push镜像时,较小的Layer大概率成功,较大的Layer小概率成功,比较尴尬。而且Layer push完毕后进度条会卡在100%很久才会变成Pushed,对于大的Layer,基本等不到Pushed状态就会超时重试。

对于这个情况,第一反应是Nginx可能限制了proxy_body_max_size(说实话已经4202年了这个默认值是时候改大一点了)。然而尝试了很多值,甚至改成了无限制,push依然失败。

后来后知后觉去看了下nginx的错误日志,发现是上游超时了。

image.png

看来是冤枉nginx了。

Nginx上游超时,检查Traefik

既然nginx报错上游超时,那么就来检查下上游的Traefik。

Traefik是Miku Cluster中的默认Ingress Class的Controller,可以理解为k8s的入口反代。首先看了下Traefik的日志,没有什么相关的报错,只有一些不规范配置的警告。

image.png

经过查询,我发现可以把Traefik的日志的级别从默认的INFO调整为DEBUG。也许有什么错误被隐藏在DEBUG日志中了?感到胜利就在前方,我修改了Traefik的Helm Chart部署配置:

logs:
  general:
    level: "DEBUG"  # Default: INFO

然后又进行一次失败的push后,来到日志,我看到了满页的调试信息,而且每秒都会刷新很多新的信息。在粗略阅读一遍后,我认为这些调试日志与这个问题并没有很大的关系,或者说要从这些海量的调试日志中找出一丝与超时相关的信息,难度还是太大了点。或许我可以先尝试其他方法,实在不行再来阅读这堆调试日志。

由于日志太多且很难发现其中的关键信息,这里就不贴出日志了。

我开始排查Traefik是否也有类似nginx的对请求体的限制。经过搜索,我看到了几个相关的GitHub Traefik项目内的Issue,其中一个是Can't upload docker images larger than 400MB or 2GB via traefik 3.0 3.0.1 3.0.2 proxy #10805。了解到Traefik部署于k8s中会限制连接的时间,默认为60秒,超过后就断开,这主要是为了防止恶意请求建立过多空连接而不释放,以此来消耗服务器资源。为了测试去除这一限制能否使镜像推送正常,我在chart values中设置了超时为600秒:

ports:
  web:
    transport:
      respondingTimeouts:
        readTimeout: 600   # @schema type:[string, integer, null]
        writeTimeout: 600  # @schema type:[string, integer, null]
        idleTimeout: 600   # @schema type:[string, integer, null]
      lifeCycle:
        requestAcceptGraceTimeout:  # @schema type:[string, integer, null]
        graceTimeOut:               # @schema type:[string, integer, null]
      keepAliveMaxRequests:         # @schema type:[integer, null]; minimum:0
      keepAliveMaxTime:             # @schema type:[string, integer, null]
  websecure:
    transport:
      respondingTimeouts:
        readTimeout: 600   # @schema type:[string, integer, null]
        writeTimeout: 600  # @schema type:[string, integer, null]
        idleTimeout: 600   # @schema type:[string, integer, null]
      lifeCycle:
        requestAcceptGraceTimeout:  # @schema type:[string, integer, null]
        graceTimeOut:               # @schema type:[string, integer, null]
      keepAliveMaxRequests:         # @schema type:[integer, null]; minimum:0
      keepAliveMaxTime:             # @schema type:[string, integer, null]

测试发现依然无法推送。

我试图寻找k8s中的Traefik请求体的大小限制选项,但并没有找到相关配置项。

Traefik未发现问题,测试Ingress

Kubernetes的官方Ingress Controller是ingress-nginx,它可以根据Ingress资源中的nginx.ingress.kubernetes.io/proxy-body-size注解来实现限制请求体的大小。考虑到很多第三方Ingress Controller都支持检测ingress-nginx的配置,Traefik可能也支持了这一限制,于是我添加了这一注解并进行了测试。

image.png

测试结果是,即使放宽了限制,推送依然失败。

修改Ingress无果,查看仓库自身的参数

于是接下来便想到了本地镜像库Harbor本身是否存在限制。

翻看了一遍Harbor官方Helm ChartValues,试图找到有可能存在的大小限制选项,但只在S3存储和OSS存储中发现了分片大小的配置,还有上述ingress-nginx的注解配置。

Harbor的后端存储位于阿里云OSS,但是配置方式并非Harbor Chart提供的S3兼容存储或OSS存储方式(主要是因为多次尝试后发现push Layer会卡住,原因不明),而是使用了另一个开源的k8s-csi-s3项目作为集群存储类的CSI,在Harbor处仅仅简单配置了存储类(之前经过测试,push没问题)。

尝试搜索阿里云OSS是否存在请求体大小限制,发现限制为5GB,显然我推送的Layer不会有这么大。

这下可真是一头雾水了。

内网不稳定,怀疑网络层和传输层问题

经过上述的排查过程,在应用层的各种HTTP代理中没找到线索。

接着我某次偶然发现在内网的节点之间使用scp传输文件速度非常不稳定,一会儿跑满VPN带宽上线,一会儿又降到个位数KB/s,随后stall,于是我将目光转向网络层和传输层。

一直以来使用Softether VPN让我非常信任它,但是其他排查方向都没有眉目,不得不怀疑一下Softether VPN了。

要研究网络层和传输层问题,我决定先不测试k8s内的cilium网络组件,而是测试Softether VPN。一是因为cilium组件使用了eBPF技术,非常复杂,以我目前的知识难以入手;二是因为k8s的cilium网络组件乃至整个k8s网络是建立在集群Softether VPN内网之上的,如果问题出在底层的网络上,排查完毕后上层的问题也许就能迎刃而解。

测试网络层,使用ping命令对内网的节点进行长时间互相测试,并未发现异常,延时都在正常范围内。

image.png

image.png

测试传输层,使用iperf命令进行数据传输测试。

以下是对部分节点的iperf测试,A节点作为iperf服务端,B和E节点作为iperf客户端。

客户端测试命令:

iperf3 -c <server_addr> -n 100M -P 8  # 使用默认TCP协议以8线程传输100M数据到服务端

内网测试

B节点测试的服务端显示过程:
image.png

B节点测试的客户端结果:
image.png

可以看到B节点的iperf测试比较不稳定,断断续续,有上百次的重传。

E节点测试的服务端显示过程:
image.png

E节点测试的客户端结果:
image.png

可以看到E节点的iperf测试非常不稳定,数据断断续续,有上万次重传,非常难绷。

公网测试

作为对比,测试下公网上(不经过Softether VPN)iperf的效果。

B节点测试的服务端显示过程:
image.png

B节点测试的客户端结果:
image.png

E节点测试的服务端显示过程:
image.png

E节点测试的客户端结果:
image.png

待更新