又是一个月的忙忙碌碌,但是没啥很大的收获。但是在忙碌过程中,还是出现了一些小问题,特此记录一下。由于我们是做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 took
, vendor/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)