全部產品
Search
文件中心

Container Service for Kubernetes:使用External Metrics API擷取成本資料

更新時間:Jun 26, 2024

如果您想要在應用程式開發中使用叢集業務的成本或資源資料,除了使用成本資料API,您也可以直接通過Kubernetes External Metrics API進行自訂查詢。本文介紹成本套件註冊的外部指標(External Metrics)的使用方式和調用樣本。

前提條件

External Metrics說明

指標名稱

指標說明

cpu_core_request_average

請求的平均CPU核心數。

cpu_core_usage_average

使用的平均CPU核心數。

memory_request_average

請求的平均記憶體量。

memory_usage_average

使用的平均記憶體量。

cost_pod_cpu_request

Pod基於CPU請求量的估算成本。

cost_pod_memory_request

Pod基於記憶體請求量的估算成本。

cost_total

叢集所有節點的總成本。

billing_pretax_amount_total

應付的叢集總賬單費用。

billing_pretax_gross_amount_total

原始的叢集總賬單費用。

以上的cost_pod_cpu_request和cost_pod_memory_request對應成本估算策略中使用單資源估算策略獲得的成本。如需使用權重混合估算,您可以分別擷取各資源的成本資料後,再按照如下公式自訂加權彙總。

Pod成本 = cost_pod_cpu_request指標資料 * CPU權重 + cost_pod_memory_request指標資料 * 記憶體權重

調用樣本

Kubernetes用戶端目前支援大部分程式設計語言,例如Java、Go、Python、Ruby等。更多資訊,請參見Client Libraries

確定請求參數

名稱

類型

是否必選

描述

start

time.Time

查詢的開始時間。

end

time.Time

查詢的結束時間。

namespace

[]string

過濾命名空間。

controllerKind

[]string

過濾控制器類型。

controllerName

[]string

過濾控制器名稱。

pod

[]string

過濾Pod名稱。

程式碼範例

以下樣本通過Go語言的用戶端庫查詢Cost External Metrics指標。

package main

import (
	"flag"
	"fmt"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	"k8s.io/klog/v2"
	"path/filepath"
	"strings"
	"time"

	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
	externalclient "k8s.io/metrics/pkg/client/external_metrics"
)

const (
	WindowLayout = "20060102150405"
)

func getWindowLabelSelectorStr(start, end time.Time) string {
	startStr := start.Format(WindowLayout)
	endStr := end.Format(WindowLayout)

	selector := fmt.Sprintf("window_start=%s,window_end=%s,window_layout=%s", startStr, endStr, WindowLayout)
	klog.Infof("get window label selector str: %s", selector)
	return selector
}

func getFilterLabelSelectorStr(namespace, controllerName, controllerKind, pod []string) string {
	var requirements []labels.Requirement

	addInRequirement := func(key string, values []string) error {
		if len(values) == 0 {
			klog.Infof("filter value is empty: %s", key)
			return nil
		}
		r, err := labels.NewRequirement(key, selection.In, values)
		if err != nil {
			klog.Errorf("failed to parse filter %s to requirement, error: %v", key, err)
			return err
		}
		requirements = append(requirements, *r)
		return nil
	}

	if err := addInRequirement("namespace", namespace); err != nil {
		return ""
	}

	if err := addInRequirement("created_by_name", controllerName); err != nil {
		return ""
	}

	if err := addInRequirement("created_by_kind", controllerKind); err != nil {
		return ""
	}

	if err := addInRequirement("pod", pod); err != nil {
		return ""
	}

	if requirements == nil {
		klog.Infof("filter is empty, do not need to parse.")
		return ""
	}

	selector := labels.NewSelector().Add(requirements...).String()
	klog.Infof("get filter label selector str: %s", selector)
	return selector
}

func main() {
	// 預設從 $HOME/.kube/config 擷取叢集憑證
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()

	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		panic(err.Error())
	}

	externalClient, err := externalclient.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	// external metric名稱
	metricName := "cost_pod_cpu_request"

	// 請求開始和結束時間
	start := time.Now().Add(-time.Hour * 2)
	end := time.Now()

	// 請求過濾條件,可選
	var namespace, controllerKind, controllerName, pod []string
	namespace = []string{"default", "kube-system"}
	controllerKind = []string{"DaemonSet"}
	controllerName = []string{"csi-plugin"}

	// 基於請求起止時間和過濾條件構造請求labelSelector
	selectorStr := make([]string, 0)
	if str := getWindowLabelSelectorStr(start, end); str != "" {
		selectorStr = append(selectorStr, str)
	}
	if str := getFilterLabelSelectorStr(namespace, controllerName, controllerKind, pod); str != "" {
		selectorStr = append(selectorStr, str)
	}
	metricSelector, err := labels.Parse(strings.Join(selectorStr, ","))
	if err != nil {
		klog.Errorf("failed to parse metricSelector, error: %v", err)
	}
	
	metrics, err := externalClient.NamespacedMetrics("*").List(metricName, metricSelector)
	if err != nil {
		klog.Errorf("unable to fetch metrics %s from apiServer: %v", metricName, err)
	}
	
	// Pod基於CPU request的估算成本
	for _, item := range metrics.Items {
		klog.Infof("pod: %s, metric: %s, value: %v", item.MetricLabels["pod"], metricName, float64(item.Value.MilliValue())/1000)
	}
}

預期輸出:

I0415 17:09:11.327321    9646 main.go:127] pod: csi-plugin-t55b2, metric: cost_pod_cpu_request, value: 0.048
I0415 17:09:11.327371    9646 main.go:127] pod: csi-plugin-8z5ls, metric: cost_pod_cpu_request, value: 0.048
I0415 17:09:11.327381    9646 main.go:127] pod: csi-plugin-97w8x, metric: cost_pod_cpu_request, value: 0.048
I0415 17:09:11.327390    9646 main.go:127] pod: csi-plugin-49crh, metric: cost_pod_cpu_request, value: 0.048
I0415 17:09:11.327397    9646 main.go:127] pod: csi-plugin-22bm5, metric: cost_pod_cpu_request, value: 0.048
I0415 17:09:11.327405    9646 main.go:127] pod: csi-plugin-zmhrk, metric: cost_pod_cpu_request, value: 0.048

相關文檔

除了本文的方式外,您也可以通過HTTP API命令查看上報資料,以擷取叢集成本最佳化的建議,便於您擷取成本資料進行二次開發。具體操作,請參見通過API擷取成本資料概述