如果您想要在應用程式開發中使用叢集業務的成本或資源資料,除了使用成本資料API,您也可以直接通過Kubernetes External Metrics API進行自訂查詢。本文介紹成本套件註冊的外部指標(External Metrics)的使用方式和調用樣本。
前提條件
已啟用成本洞察功能。
已開啟阿里雲Prometheus監控。
已安裝alibaba-cloud-metrics-adapter組件,且在安裝組件時設定
AlibabaCloudMetricsAdapter.prometheus.url參數為阿里雲Prometheus監控的地址。具體操作,請參見基於阿里雲Prometheus指標的容器水平伸縮。
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。
確定請求參數
名稱 | 類型 | 是否必選 | 描述 |
| time.Time | 是 | 查詢的開始時間。 |
| time.Time | 是 | 查詢的結束時間。 |
| []string | 否 | 過濾命名空間。 |
| []string | 否 | 過濾控制器類型。 |
| []string | 否 | 過濾控制器名稱。 |
| []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擷取成本資料概述。