1. 问题起源
在kubernetes里,使用kubectl get pods 时,返回
I0508 05:43:04.655602 100742 request.go:668] Waited for 1.178494016s due to client-side throttling, not priority and fairness, request: GET:https://10.103.0.1:443/apis/cert-manager.io/v1?timeout=32s
其中的URL每次会变。
2. 原因探究
(1) kubectl 是一个go语言实现的工具,其核心功能是发一下rest API请求到kubernetes,并显示返回的结果.
比如
hw2v2z3:# kubectl version
Client Version: v1.30.2+rke2r1
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.30.2+rke2r1
其中Client Verion就是kubectl这个命令本身的版本,而Server Version是kubernetes的版本,也就API server的版本.
(2) 什么是client-side throttling
回到问题本身,当执行kubect get 命令时,返回的是"client-side throttling",此处的client-side 即是kubectl 命令本身。也就是说,kubectl 发送了太多的rest API请求,结果kubectl 自身就发现命令太多而触发了流控,所以提示 "client-side throttling"
(3) 一条kubectl get 命令为啥会发多条REST API请求
The Kubernetes Discovery Cache: Blessing and Curse · Jonny Langefeld
kubernetes在设计时就提供了非常灵活的API接口。 一般来说我们会理解为kubectl get pod对应的api是GET https://127.0.0.1:6443/api/v1/namespaces/default/pods?limit=500,但kubernetes设计时认为这条API中的所有字段都是要查询过来的。比如v1, namespace, pods这些关键词都是通过一系列的API请求查询到有这些资源,这个过程称为Discovery,最终才会生成一条API请求获取你真正要查询 的POD。
还有一关键点,就是kubernetes中CRD会大大增加要查询的资源数量。我的理解是一个CRD会触发多条的查询资源命令,而且是不同的URL。这也可以解释为什么出现throttlin时,URL总是不同的原因。所以当出现这个错误时,首先需要查询系统中是不是有很多(>100)个CRD.
如果您觉得这个解释还是不清晰,可以看上面的贴子,看看文章中说的100s描述的API请求.
当然,这么多查询的结果会缓存下来,在~/.kube/cache/discovery/<host>/v1/serverresources.json
(4)为什么会一直有"client-side throttling"错误
按照第(3)节的描述,获取到的各种配置是有缓存的,也就是一次次get各种资源(如v1, namespace, pod),汇总起来总是可以收齐的。那为啥会老失败呢?
因为这个缓存是有有效期的,默认10分钟。也就是10分钟后,kubectl命令又会触发再重新取一遍所有的资源。那这样又会从头再来,一遍遍的失败。因为触发了流控,我们也不知道多少次获取后,才能获取到所有资源。
3. 源码佐证
https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/client-go/rest/request.go里的tryThrottleWithInfo:
if latency > longThrottleLatency {
if retryInfo == "" {
retryInfo = "client-side throttling, not priority and fairness"
}
另外,下面这个PR是把Discovery Burst请求从100改到300
https://github.com/kubernetes/kubernetes/pull/109141/files
本文主要记录碰到throttling的过程,不一定能解决问题,但是一定可以知道问题的原因. That's help.