freeleaps-ops/3rd/load-watcher/pkg/watcher/internal/metricsprovider/k8s.go
2025-03-19 15:59:37 +08:00

167 lines
5.1 KiB
Go

/*
Copyright 2020 PayPal
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metricsprovider
import (
"context"
"fmt"
"net/http"
"os"
"github.com/paypal/load-watcher/pkg/watcher"
log "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
metricsv "k8s.io/metrics/pkg/client/clientset/versioned"
)
var (
kubeConfigPresent = false
kubeConfigPath string
)
const (
// env variable that provides path to kube config file, if deploying from outside K8s cluster
kubeConfig = "KUBE_CONFIG"
)
func init() {
var ok bool
kubeConfigPath, ok = os.LookupEnv(kubeConfig)
if ok {
kubeConfigPresent = true
}
}
// This is a client for K8s provided Metric Server
type metricsServerClient struct {
// This client fetches node metrics from metric server
metricsClientSet *metricsv.Clientset
// This client fetches node capacity
coreClientSet *kubernetes.Clientset
}
func NewMetricsServerClient() (watcher.MetricsProviderClient, error) {
var config *rest.Config
var err error
kubeConfig := ""
if kubeConfigPresent {
kubeConfig = kubeConfigPath
}
config, err = clientcmd.BuildConfigFromFlags("", kubeConfig)
if err != nil {
return nil, err
}
metricsClientSet, err := metricsv.NewForConfig(config)
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
return metricsServerClient{
metricsClientSet: metricsClientSet,
coreClientSet: clientSet}, nil
}
func (m metricsServerClient) Name() string {
return watcher.K8sClientName
}
func (m metricsServerClient) FetchHostMetrics(host string, window *watcher.Window) ([]watcher.Metric, error) {
var metrics = []watcher.Metric{}
nodeMetrics, err := m.metricsClientSet.MetricsV1beta1().NodeMetricses().Get(context.TODO(), host, metav1.GetOptions{})
if err != nil {
return metrics, err
}
var cpuFetchedMetric watcher.Metric
var memFetchedMetric watcher.Metric
node, err := m.coreClientSet.CoreV1().Nodes().Get(context.Background(), host, metav1.GetOptions{})
if err != nil {
return metrics, err
}
// Added CPU latest metric
cpuFetchedMetric.Value = float64(100*nodeMetrics.Usage.Cpu().MilliValue()) / float64(node.Status.Capacity.Cpu().MilliValue())
cpuFetchedMetric.Type = watcher.CPU
cpuFetchedMetric.Operator = watcher.Latest
metrics = append(metrics, cpuFetchedMetric)
// Added Memory latest metric
memFetchedMetric.Value = float64(100*nodeMetrics.Usage.Memory().Value()) / float64(node.Status.Capacity.Memory().Value())
memFetchedMetric.Type = watcher.Memory
memFetchedMetric.Operator = watcher.Latest
metrics = append(metrics, memFetchedMetric)
return metrics, nil
}
func (m metricsServerClient) FetchAllHostsMetrics(window *watcher.Window) (map[string][]watcher.Metric, error) {
metrics := make(map[string][]watcher.Metric)
nodeMetricsList, err := m.metricsClientSet.MetricsV1beta1().NodeMetricses().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return metrics, err
}
nodeList, err := m.coreClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
if err != nil {
return metrics, err
}
cpuNodeCapacityMap := make(map[string]int64)
memNodeCPUCapacityMap := make(map[string]int64)
for _, host := range nodeList.Items {
cpuNodeCapacityMap[host.Name] = host.Status.Capacity.Cpu().MilliValue()
memNodeCPUCapacityMap[host.Name] = host.Status.Capacity.Memory().Value()
}
for _, host := range nodeMetricsList.Items {
var cpuFetchedMetric watcher.Metric
cpuFetchedMetric.Type = watcher.CPU
cpuFetchedMetric.Operator = watcher.Latest
if _, ok := cpuNodeCapacityMap[host.Name]; !ok {
log.Errorf("unable to find host %v in node list caching cpu capacity", host.Name)
continue
}
cpuFetchedMetric.Value = float64(100*host.Usage.Cpu().MilliValue()) / float64(cpuNodeCapacityMap[host.Name])
metrics[host.Name] = append(metrics[host.Name], cpuFetchedMetric)
var memFetchedMetric watcher.Metric
memFetchedMetric.Type = watcher.Memory
memFetchedMetric.Operator = watcher.Latest
if _, ok := memNodeCPUCapacityMap[host.Name]; !ok {
log.Errorf("unable to find host %v in node list caching memory capacity", host.Name)
continue
}
memFetchedMetric.Value = float64(100*host.Usage.Memory().Value()) / float64(memNodeCPUCapacityMap[host.Name])
metrics[host.Name] = append(metrics[host.Name], memFetchedMetric)
}
return metrics, nil
}
func (m metricsServerClient) Health() (int, error) {
var status int
m.metricsClientSet.RESTClient().Verb("HEAD").Do(context.Background()).StatusCode(&status)
if status != http.StatusOK {
return -1, fmt.Errorf("received response status code: %v", status)
}
return 0, nil
}