1. 程式人生 > >kubernetes+ prometheus自動伸縮的設計與實現(三)

kubernetes+ prometheus自動伸縮的設計與實現(三)

現在還剩下最後一東西沒有解決了,就是一個查詢的介面,提供一個prometheus轉接的地方。第一篇blog已經介紹了通過自定義指標採集器呼叫custom-metrics介面。現在看看這個介面的實現,首先得有這樣一個介面
這個是基於k8s的一個孵化專案:
github.com/kubernetes-incubator/custom-metrics-apiserver
他就是提供一個自定義服務介面,我們只要自己去實現就可以了,當然如果其他的監控也可以自己去實現他的介面。先看看這個專案如果啟動服務的註冊介面的github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver/apiserver.go,

func (c completedConfig) New(cmProvider provider.CustomMetricsProvider) (*CustomMetricsAdapterServer, error) {
    genericServer, err := c.Config.GenericConfig.SkipComplete().New(genericapiserver.EmptyDelegate) // completion is done in Complete, no need for a second time
    if err != nil {
        return
nil, err } s := &CustomMetricsAdapterServer{ GenericAPIServer: genericServer, Provider: cmProvider, } if err := s.InstallCustomMetricsAPI(); err != nil { return nil, err } return s, nil }

通過InstallCustomMetricsAPI去註冊github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/apiserver/cmapis.go,

    groupMeta := registry.GroupOrDie(custom_metrics.GroupName)

    ...

    cmAPI := s.cmAPI(groupMeta, &groupMeta.GroupVersion)

    if err := cmAPI.InstallREST(s.GenericAPIServer.Handler.GoRestfulContainer); err != nil {
        return err
    }

如果熟悉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

type CustomMetricsProvider interface {
    // root級別指定的一個指標
    GetRootScopedMetricByName(groupResource schema.GroupResource, name string, metricName string) (*custom_metrics.MetricValue, error)

    // root級別通過label篩選的指標
    GetRootScopedMetricBySelector(groupResource schema.GroupResource, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error)

    // 特定namespace下的某個指標
    GetNamespacedMetricByName(groupResource schema.GroupResource, namespace string, name string, metricName string) (*custom_metrics.MetricValue, error)

    // 特定namespace下通過label篩選的指標
    GetNamespacedMetricBySelector(groupResource schema.GroupResource, namespace string, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error)

    // 查詢所有的指標
    ListAllMetrics() []MetricInfo
}

我們要寫的prometheus的provider當然也要實現這些指標查詢的介面。
下面看具體程式碼,拿一個namespace下通過指標名稱和label的介面舉例,

func (p *prometheusProvider) GetNamespacedMetricBySelector(groupResource schema.GroupResource, namespace string, selector labels.Selector, metricName string) (*custom_metrics.MetricValueList, error) {
    info := provider.MetricInfo{
        GroupResource: groupResource,
        Metric:        metricName,
        Namespaced:    true,
    }
    return p.getMultiple(info, namespace, selector)
}

這個就是具體介面的實現,getMultiple具體實現如下pkg/custom-provider/provider.go

//查詢資料
queryResults, err := p.buildQuery(info, namespace, resourceNames...)
//封裝資料
p.metricsFor(queryResults, info, matchingObjectsRaw)

具體的查詢無非是呼叫prometheus的查詢api

queryResults, err := p.promClient.Query(context.Background(), pmodel.Now(), fullQuery)

封裝資料程式碼如下

err := apimeta.EachListItem(list, func(item runtime.Object) error {
        objUnstructured := item.(*unstructured.Unstructured)
        objName := objUnstructured.GetName()
        if _, found := values[objName]; !found {
            return nil
        }
        value, err := p.metricFor(values[objName], info.GroupResource, objUnstructured.GetNamespace(), objName, info.Metric)
        if err != nil {
            return err
        }
        res = append(res, *value)

        return nil
    })
    if err != nil {
        return nil, err
    }

    return &custom_metrics.MetricValueList{
        Items: res,
    }, nil

就是拼裝成k8s.io/metrics/pkg/apis/custom_metrics/types.go下面定義的MetricValueList

type MetricValueList struct {
    metav1.TypeMeta `json:",inline"`
    metav1.ListMeta `json:"metadata,omitempty"`
    Items []MetricValue `json:"items"`
}

type MetricValue struct {
    metav1.TypeMeta `json:",inline"`
    DescribedObject api.ObjectReference `json:"describedObject"`
    // 指標名稱
    MetricName string `json:"metricName"`
    // 採集時間
    Timestamp metav1.Time `json:"timestamp"`
    // 採集時間視窗
    WindowSeconds *int64 `json:"window,omitempty"`
    // 指標值
    Value resource.Quantity `json:"value"`
}