什么是evaluator
大家都知道,Kubernetes中使用resourcequota对配额进行管理。配额的管理涉及两个步骤:1、计算请求所需要的资源;2、比较并更新配额。所以解读resourcequota将分为两次进行。
evaluator就是用来计算请求所需要的资源的。
GenericEvaluator
GenericEvaluator实现了evaluator,是一个基础的evaluator。
我们先来看下GenericEvaluator的定义,在/pkg/quota/generic/evaluator.go中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| type GenericEvaluator struct { Name string InternalGroupKind unversioned.GroupKind InternalOperationResources map[admission.Operation][]api.ResourceName MatchedResourceNames []api.ResourceName MatchesScopeFunc MatchesScopeFunc UsageFunc UsageFunc ListFuncByNamespace ListFuncByNamespace GetFuncByNamespace GetFuncByNamespace ConstraintsFunc ConstraintsFunc }
|
其中:
- Name: 表示该Evaluator的名称;
- InternalGroupKind: 表明该Evaluator所处理资源的内部的类型;
- InternalOperationResources: 表明该Evaluator所支持的请求的类型,如Create, Update等及这些操作所支持的资源;
- MatchedResourceNames: 表明该Evaluator所对应的资源名称,如ResourceCPU, ResourcePods等;
- MatchesScopeFunc: resourcequota的scope判断函数。resourcequota只处理满足scope判断函数的请求(即只统计部分对象的配额),目前有Terminating, NotTerminating, BestEffort, NotBestEffort这些Scope;
- UsageFunc: 用来计算对象所占资源;
- ListFuncByNamespace: 对象List函数;
- GetFuncByNamespace: 对象获取函数;
- ConstraintsFunc: 对对象申请的资源进行合理性检查,如requests<limits。
Matches()
Matches()方法判断该Evaluator及resourceQuota是否需要处理该请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| func (g *GenericEvaluator) Matches(resourceQuota *api.ResourceQuota, item runtime.Object) bool { if resourceQuota == nil { return false } matchResource := false for resourceName := range resourceQuota.Status.Hard { if g.MatchesResource(resourceName) { matchResource = true break } } matchScope := true for _, scope := range resourceQuota.Spec.Scopes { matchScope = matchScope && g.MatchesScope(scope, item) } return matchResource && matchScope } func (g *GenericEvaluator) MatchesResource(resourceName api.ResourceName) bool { for _, matchedResourceName := range g.MatchedResourceNames { if resourceName == matchedResourceName { return true } } return false } func (g *GenericEvaluator) MatchesScope(scope api.ResourceQuotaScope, object runtime.Object) bool { return g.MatchesScopeFunc(scope, object) }
|
Usage()
Usage()方法可以计算object所需要的资源。
1 2 3 4
| func (g *GenericEvaluator) Usage(object runtime.Object) api.ResourceList { return g.UsageFunc(object) }
|
UsageStats()
UsageStats()可以计算出某命名空间下某类对象的资源使用情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| func (g *GenericEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.UsageStats, error) { result := quota.UsageStats{Used: api.ResourceList{}} for _, resourceName := range g.MatchedResourceNames { result.Used[resourceName] = resource.MustParse("0") } items, err := g.ListFuncByNamespace(options.Namespace, api.ListOptions{ LabelSelector: labels.Everything(), }) if err != nil { return result, fmt.Errorf("%s: Failed to list %v: %v", g.Name, g.GroupKind(), err) } for _, item := range items { matchesScopes := true for _, scope := range options.Scopes { if !g.MatchesScope(scope, item) { matchesScopes = false } } if matchesScopes { result.Used = quota.Add(result.Used, g.Usage(item)) } } return result, nil }
|
PodEvaluator
上小节介绍了Evaluator,在这小节将以PodEvaluator。PodEvaluator可以计算Pod的所需资源量。
PodEvaluator定义在/pkg/quota/evaluator/core/pods.go中,其本身就是一个Evaluator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| func NewPodEvaluator(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Evaluator { computeResources := []api.ResourceName{ api.ResourceCPU, api.ResourceMemory, api.ResourceRequestsCPU, api.ResourceRequestsMemory, api.ResourceLimitsCPU, api.ResourceLimitsMemory, } allResources := append(computeResources, api.ResourcePods) listFuncByNamespace := listPodsByNamespaceFuncUsingClient(kubeClient) if f != nil { listFuncByNamespace = generic.ListResourceUsingInformerFunc(f, unversioned.GroupResource{Resource: "pods"}) } return &generic.GenericEvaluator{ Name: "Evaluator.Pod", InternalGroupKind: api.Kind("Pod"), InternalOperationResources: map[admission.Operation][]api.ResourceName{ admission.Create: allResources, }, GetFuncByNamespace: func(namespace, name string) (runtime.Object, error) { return kubeClient.Core().Pods(namespace).Get(name) }, ConstraintsFunc: PodConstraintsFunc, MatchedResourceNames: allResources, MatchesScopeFunc: PodMatchesScopeFunc, UsageFunc: PodUsageFunc, ListFuncByNamespace: listFuncByNamespace, } }
|
这里要着重说下listFuncByNamespace,有listPodsByNamespaceFuncUsingClient和ListResourceUsingInformerFunc两种,而且ListResourceUsingInformerFunc优先级更高,具体由NewPodEvaluator()的参数来控制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| func listPodsByNamespaceFuncUsingClient(kubeClient clientset.Interface) generic.ListFuncByNamespace { return func(namespace string, options api.ListOptions) ([]runtime.Object, error) { itemList, err := kubeClient.Core().Pods(namespace).List(options) if err != nil { return nil, err } results := make([]runtime.Object, 0, len(itemList.Items)) for i := range itemList.Items { results = append(results, &itemList.Items[i]) } return results, nil } }
|
1 2 3 4 5 6 7 8 9 10
| func ListResourceUsingInformerFunc(f informers.SharedInformerFactory, groupResource unversioned.GroupResource) ListFuncByNamespace { return func(namespace string, options api.ListOptions) ([]runtime.Object, error) { informer, err := f.ForResource(groupResource) if err != nil { return nil, err } return informer.Lister().ByNamespace(namespace).List(options.LabelSelector) } }
|
PodUsageFunc()
PodUsageFunc()函数用来计算Pod的所需资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| func PodUsageFunc(object runtime.Object) api.ResourceList { pod, ok := object.(*api.Pod) if !ok { return api.ResourceList{} } if !QuotaPod(pod) { return api.ResourceList{} } requests := api.ResourceList{} limits := api.ResourceList{} for i := range pod.Spec.Containers { requests = quota.Add(requests, pod.Spec.Containers[i].Resources.Requests) limits = quota.Add(limits, pod.Spec.Containers[i].Resources.Limits) } for i := range pod.Spec.InitContainers { requests = quota.Max(requests, pod.Spec.InitContainers[i].Resources.Requests) limits = quota.Max(limits, pod.Spec.InitContainers[i].Resources.Limits) } return podUsageHelper(requests, limits) } func podUsageHelper(requests api.ResourceList, limits api.ResourceList) api.ResourceList { result := api.ResourceList{} result[api.ResourcePods] = resource.MustParse("1") if request, found := requests[api.ResourceCPU]; found { result[api.ResourceCPU] = request result[api.ResourceRequestsCPU] = request } if limit, found := limits[api.ResourceCPU]; found { result[api.ResourceLimitsCPU] = limit } if request, found := requests[api.ResourceMemory]; found { result[api.ResourceMemory] = request result[api.ResourceRequestsMemory] = request } if limit, found := limits[api.ResourceMemory]; found { result[api.ResourceLimitsMemory] = limit } return result }
|
GenericRegistry
如PodEvaluator这样的Evaluator有非常多个,所以需要有一个地方来管理这些Evaluator,这个管理Evaluator的就是GenericRegistry。
GenericRegistry定义在/pkg/quota/generic/registry.go中:
1 2 3 4 5 6 7 8 9 10 11 12 13
| type GenericRegistry struct { InternalEvaluators map[unversioned.GroupKind]quota.Evaluator } ``` 可以看出,GenericRegistry中有字段InternalEvaluators,里面记录了GK和对应Evaluator的映射关系。可以通过Evaluators()方法获取InternalEvaluators。 ``` Go func (r *GenericRegistry) Evaluators() map[unversioned.GroupKind]quota.Evaluator { return r.InternalEvaluators }
|
GenericRegistry的生成函数定义在/pkg/quota/evaluator/core/registry.go中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| func NewRegistry(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Registry { pod := NewPodEvaluator(kubeClient, f) service := NewServiceEvaluator(kubeClient) replicationController := NewReplicationControllerEvaluator(kubeClient) resourceQuota := NewResourceQuotaEvaluator(kubeClient) secret := NewSecretEvaluator(kubeClient) configMap := NewConfigMapEvaluator(kubeClient) persistentVolumeClaim := NewPersistentVolumeClaimEvaluator(kubeClient, f) return &generic.GenericRegistry{ InternalEvaluators: map[unversioned.GroupKind]quota.Evaluator{ pod.GroupKind(): pod, service.GroupKind(): service, replicationController.GroupKind(): replicationController, secret.GroupKind(): secret, configMap.GroupKind(): configMap, resourceQuota.GroupKind(): resourceQuota, persistentVolumeClaim.GroupKind(): persistentVolumeClaim, }, } }
|
可以看出,NewRegistry()会把PodEvaluator, ReplicationControllerEvaluator, ResourceQuotaEvaluator, SecretEvaluator, ConfigMapEvaluator, PersistentVolumeClaimEvaluator注册到GenericRegistry。
入口
整个Evaluator的入口定义在/pkg/quota/install/registry.go中:
1 2 3 4 5 6
| func NewRegistry(kubeClient clientset.Interface, f informers.SharedInformerFactory) quota.Registry { return core.NewRegistry(kubeClient, f) }
|
这里的core.NewRegistry()就是上面的NewRegistry(),返回GenericRegistry。得到GenericRegistry后,通过调用Evaluators()方法,即可获取全部的Evaluator。