现在还剩下最后一东西没有解决了,就是一个查询的接口,提供一个prometheus转接的地方。第一篇blog已经介绍了通过自定义指标采集器调用custom-metrics接口。现在看看这个接口的实现,首先得有这样一个接口
这个是基于k8s的一个孵化项目:
github.com/kubernetes-incubator/custom-metrics-apiserver
他就是提供一个自定义服务接口,我们只要自己去实现就可以了,当然如果其他的监控也可以自己去实现他的接口。先看看这个项目如果启动服务的注册接口的github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver/apiserver.go,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 1func (c completedConfig) New(cmProvider provider.CustomMetricsProvider) (*CustomMetricsAdapterServer, error) {
2 genericServer, err := c.Config.GenericConfig.SkipComplete().New(genericapiserver.EmptyDelegate) // completion is done in Complete, no need for a second time
3 if err != nil {
4 return nil, err
5 }
6
7 s := &CustomMetricsAdapterServer{
8 GenericAPIServer: genericServer,
9 Provider: cmProvider,
10 }
11
12 if err := s.InstallCustomMetricsAPI(); err != nil {
13 return nil, err
14 }
15
16 return s, nil
17}
18
通过InstallCustomMetricsAPI去注册github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver/cmapis.go,
1
2
3
4
5
6
7
8
9
10 1 groupMeta := registry.GroupOrDie(custom_metrics.GroupName)
2
3 ...
4
5 cmAPI := s.cmAPI(groupMeta, &groupMeta.GroupVersion)
6
7 if err := cmAPI.InstallREST(s.GenericAPIServer.Handler.GoRestfulContainer); err != nil {
8 return err
9 }
10
如果熟悉k8s api注册的人,对此肯定很熟悉,这个和k8s api注册一样。上面的custom_metrics.GroupName就是custom-metrics.metrics.k8s.io,ok关于api如果去注册,不在我的重点,在此先略过。
重点是我们去实现的数据转化的接口
github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider/interfaces.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 1type CustomMetricsProvider interface {
2 // root级别指定的一个指标
3 GetRootScopedMetricByName(groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValue, error)
4
5 // root级别通过label筛选的指标
6 GetRootScopedMetricBySelector(groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error)
7
8 // 特定namespace下的某个指标
9 GetNamespacedMetricByName(groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error)
10
11 // 特定namespace下通过label筛选的指标
12 GetNamespacedMetricBySelector(groupResource schema.GroupResource, namespace string, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error)
13
14 // 查询所有的指标
15 ListAllMetrics() []MetricInfo
16}
17
我们要写的prometheus的provider当然也要实现这些指标查询的接口。
下面看具体代码,拿一个namespace下通过指标名称和label的接口举例,
1
2
3
4
5
6
7
8
9 1func (p *prometheusProvider) GetNamespacedMetricBySelector(groupResource schema.GroupResource, namespace string, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) {
2 info := provider.MetricInfo{
3 GroupResource: groupResource,
4 Metric: metricName,
5 Namespaced: true,
6 }
7 return p.getMultiple(info, namespace, selector)
8}
9
这个就是具体接口的实现,getMultiple具体实现如下pkg/custom-provider/provider.go
1
2
3
4
5 1//查询数据
2queryResults, err := p.buildQuery(info, namespace, resourceNames...)
3//封装数据
4p.metricsFor(queryResults, info, matchingObjectsRaw)
5
具体的查询无非是调用prometheus的查询api
1
2 1queryResults, err := p.promClient.Query(context.Background(), pmodel.Now(), fullQuery)
2
封装数据代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 1err := apimeta.EachListItem(list, func(item runtime.Object) error {
2 objUnstructured := item.(*unstructured.Unstructured)
3 objName := objUnstructured.GetName()
4 if _, found := values[objName]; !found {
5 return nil
6 }
7 value, err := p.metricFor(values[objName], info.GroupResource, objUnstructured.GetNamespace(), objName, info.Metric)
8 if err != nil {
9 return err
10 }
11 res = append(res, *value)
12
13 return nil
14 })
15 if err != nil {
16 return nil, err
17 }
18
19 return &custom_metrics.MetricValueList{
20 Items: res,
21 }, nil
22
就是拼装成k8s.io/metrics/pkg/apis/custom_metrics/types.go下面定义的MetricValueList
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 1type MetricValueList struct {
2 metav1.TypeMeta `json:",inline"`
3 metav1.ListMeta `json:"metadata,omitempty"`
4 Items []MetricValue `json:"items"`
5}
6
7type MetricValue struct {
8 metav1.TypeMeta `json:",inline"`
9 DescribedObject api.ObjectReference `json:"describedObject"`
10 // 指标名称
11 MetricName string `json:"metricName"`
12 // 采集时间
13 Timestamp metav1.Time `json:"timestamp"`
14 // 采集时间窗口
15 WindowSeconds *int64 `json:"window,omitempty"`
16 // 指标值
17 Value resource.Quantity `json:"value"`
18}
19