k8s clientset 出现 Throttling request took 限流的解决方法

前言

又是一个月的忙忙碌碌,但是没啥很大的收获。但是在忙碌过程中,还是出现了一些小问题,特此记录一下。由于我们是做nocalhost开发的,也就是开发一个k8s开发工具, 所以不断的和k8s打交道,然后有个需求是获取资源树结构,也就是

- cluster
  - namespace
    - application
      - workloads
        - pod
        - deployment
        - statefulset
        - replicaset
        ...
      - networks
        - service
        - ingress
        ...

做这个需求是需要快速返回结果,插件端会不断的刷新这个树,所以很自然的用到的informer去做,也就是本地开informer,缓存不同类型的资源和数据。其实这一套informer已经写好了,并且速度也还可以
,但是突然有一天,大佬说是树展不开了,也就是获取资源卡住了,然后要来了kubeconfig,就开始了排错之旅。

分析

由于这块儿逻辑都是我写的,所以比较熟悉。单步调试后,到了 ServerPreferredResources这里,卡住了。

func TestGetResource(t *testing.T) {
kubeconfigBytes, _ := ioutil.ReadFile(clientcmd.RecommendedHomeFile)
config, _ := clientcmd.RESTConfigFromKubeConfig(kubeconfigBytes)
clientset, _ := kubernetes.NewForConfig(config)
restMappingList, _ := clientset.ServerPreferredResources()
fmt.Println(len(restMappingList))
}

这里跑了近30s,经过一顿操作,最终定位到是由于,k8s中有个apiserivce不可用导致的。

kubectl get apiservice --kubeconfig=aa
NAME                                   SERVICE                           AVAILABLE                 AGE
...
v1beta1.discovery.k8s.io               Local                             True                      234d
v1beta1.events.k8s.io                  Local                             True                      234d
v1beta1.extensions                     Local                             True                      234d
v1beta1.external.metrics.k8s.io        keda/keda-operator                False (ServiceNotFound)   2d14h
v1beta1.getambassador.io               Local                             True                      35d
v1beta1.metrics.k8s.io                 kube-system/hpa-metrics-service   True                      38d
v1beta1.networking.istio.io            Local                             True                      3d16h
v1beta1.networking.k8s.io              Local                             True                      234d
...

然后我手动删除了这个apiservice,就恢复,方法 ServerPreferredResources就很快返回了,也没有报错。

问题就这样解决了?

但是用户的集群资源,是不可以删除的。因此还得自己想办法。分析一下,apiservice是有aggregate
server创建的资源,目的是标识注册的服务,通过将对应类型的服务,转发到后端的service,进行查询和汇聚(不知道理解的对不对,欢迎大佬指正)
,而如果某个apiservice不可用了,那么其后台的服务应该也就不可以用了,那么请求这个apiservice可能就会超时。并且看了一眼方法 ServerPreferredResources的输出

...
I0828 10:02:54.754702   91039 request.go:655] Throttling request took 1.152816427s, request: GET:https://cls-23a4ww3k.ccs.tencent-cloud.com/apis/custom.metrics.k8s.io/v1beta1?timeout=32s
I0828 10:02:58.754702   91039 request.go:655] Throttling request took 6.245363456s, request: GET:https://cls-23a4ww3k.ccs.tencent-cloud.com/apis/custom.metrics.k8s.io/v1beta1?timeout=32s
...

也就是被限速了,神奇了,然后全局搜索了一下 关键字 Throttling request tookvendor/k8s.io/client-go/rest/request.go:580, 发现

func (r *Request) tryThrottle(ctx context.Context) error {
if r.rateLimiter == nil {
return nil
}

now := time.Now()

err := r.rateLimiter.Wait(ctx)

latency := time.Since(now)
if latency > longThrottleLatency {
klog.V(3).Infof("Throttling request took %v, request: %s:%s", latency, r.verb, r.URL().String())
}
if latency > extraLongThrottleLatency {
// If the rate limiter latency is very high, the log message should be printed at a higher log level,
// but we use a throttled logger to prevent spamming.
globalThrottledLogger.Infof("Throttling request took %v, request: %s:%s", latency, r.verb, r.URL().String())
}
metrics.RateLimiterLatency.Observe(r.verb, r.finalURLTemplate(), latency)

return err
}

原来这里有个 RateLimiter, 也就是限流器。然后一顿引用查看,最后找到,原来restclient.Config是可以设置 RateLimiter

type Config struct {
...
// QPS indicates the maximum QPS to the master from this client.
// If it's zero, the created RESTClient will use DefaultQPS: 5
QPS float32

// Maximum burst for throttle.
// If it's zero, the created RESTClient will use DefaultBurst: 10.
Burst int

// Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
RateLimiter flowcontrol.RateLimiter
...
}

可以从注释中看到,默认 QPS 是5,最大 QPS 是10,如果设置了 RateLimiter,将会覆盖 QPS 和 Burst 参数。所以果断覆盖

func TestGetResource(t *testing.T) {
kubeconfigBytes, _ := ioutil.ReadFile(filepath.Join(homedir.HomeDir(), ".kube", "aa"))
config, _ := clientcmd.RESTConfigFromKubeConfig(kubeconfigBytes)
// 可以设置 RateLimiter
config.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(1000, 1000)
clientset, _ := kubernetes.NewForConfig(config)
}

而不过不设置这个参数,那么默认就是 然后就设置了一个1000 QPS的限流器,发现就没有限流提示,并且速度也很快了。终于是找到了一个两全奇美的办法啦,赞👍

解决方法

restclient.Config设置一个较大的 RateLimiter,例如 mermaid flowchatcontrol.NewTokenBucketRateLimiter(1000, 1000)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值