|
|
@@ -67,11 +67,53 @@ type Vector struct {
|
|
|
}
|
|
|
|
|
|
func ComputeCostData(cli prometheusClient.Client, clientset kubernetes.Interface, cloud costAnalyzerCloud.Provider, window string) (map[string]*CostData, error) {
|
|
|
- queryRAMRequests := `avg(label_replace(label_replace(avg((count_over_time(kube_pod_container_resource_requests_memory_bytes{container!="",container!="POD"}[` + window + `]) * avg_over_time(kube_pod_container_resource_requests_memory_bytes{container!="",container!="POD"}[` + window + `]))) by (namespace,container,pod) , "container_name","$1","container","(.+)"), "pod_name","$1","pod","(.+)") ) by (namespace,container_name, pod_name)`
|
|
|
- queryRAMUsage := `sort_desc(avg(count_over_time(container_memory_usage_bytes{container_name!="",container_name!="POD"}[` + window + `]) * avg_over_time(container_memory_usage_bytes{container_name!="",container_name!="POD"}[` + window + `])) by (namespace,container_name,pod_name,instance))`
|
|
|
- queryCPURequests := `avg(label_replace(label_replace(avg((count_over_time(kube_pod_container_resource_requests_cpu_cores{container!="",container!="POD"}[` + window + `]) * avg_over_time(kube_pod_container_resource_requests_cpu_cores{container!="",container!="POD"}[` + window + `]))) by (namespace,container,pod) , "container_name","$1","container","(.+)"), "pod_name","$1","pod","(.+)") ) by (namespace,container_name, pod_name)`
|
|
|
- queryCPUUsage := `avg(rate(container_cpu_usage_seconds_total{container_name!="",container_name!="POD"}[` + window + `])) by (namespace,container_name,pod_name,instance)`
|
|
|
- queryGPURequests := `avg(label_replace(label_replace(avg((count_over_time(kube_pod_container_resource_requests{resource="nvidia_com_gpu", container!="",container!="POD"}[` + window + `]) * avg_over_time(kube_pod_container_resource_requests{resource="nvidia_com_gpu", container!="",container!="POD"}[` + window + `]))) by (namespace,container,pod) , "container_name","$1","container","(.+)"), "pod_name","$1","pod","(.+)") ) by (namespace,container_name, pod_name)`
|
|
|
+ queryRAMRequests := `avg(
|
|
|
+ label_replace(
|
|
|
+ label_replace(
|
|
|
+ avg(
|
|
|
+ count_over_time(kube_pod_container_resource_requests_memory_bytes{container!="",container!="POD", node!=""}[` + window + `])
|
|
|
+ *
|
|
|
+ avg_over_time(kube_pod_container_resource_requests_memory_bytes{container!="",container!="POD", node!=""}[` + window + `])
|
|
|
+ ) by (namespace,container,pod,node) , "container_name","$1","container","(.+)"
|
|
|
+ ), "pod_name","$1","pod","(.+)"
|
|
|
+ )
|
|
|
+ ) by (namespace,container_name,pod_name,node)`
|
|
|
+ queryRAMUsage := `sort_desc(
|
|
|
+ avg(
|
|
|
+ label_replace(count_over_time(container_memory_usage_bytes{container_name!="",container_name!="POD", instance!=""}[` + window + `]), "node", "$1", "instance","(.+)")
|
|
|
+ *
|
|
|
+ label_replace(avg_over_time(container_memory_usage_bytes{container_name!="",container_name!="POD", instance!=""}[` + window + `]), "node", "$1", "instance","(.+)")
|
|
|
+ ) by (namespace,container_name,pod_name,node)
|
|
|
+ )`
|
|
|
+ queryCPURequests := `avg(
|
|
|
+ label_replace(
|
|
|
+ label_replace(
|
|
|
+ avg(
|
|
|
+ count_over_time(kube_pod_container_resource_requests_cpu_cores{container!="",container!="POD", node!=""}[` + window + `])
|
|
|
+ *
|
|
|
+ avg_over_time(kube_pod_container_resource_requests_cpu_cores{container!="",container!="POD", node!=""}[` + window + `])
|
|
|
+ ) by (namespace,container,pod,node) , "container_name","$1","container","(.+)"
|
|
|
+ ), "pod_name","$1","pod","(.+)"
|
|
|
+ )
|
|
|
+ ) by (namespace,container_name,pod_name,node)`
|
|
|
+ queryCPUUsage := `avg(
|
|
|
+ label_replace(
|
|
|
+ rate(
|
|
|
+ container_cpu_usage_seconds_total{container_name!="",container_name!="POD",instance!=""}[` + window + `]
|
|
|
+ ) , "node", "$1", "instance", "(.+)"
|
|
|
+ )
|
|
|
+ ) by (namespace,container_name,pod_name,node)`
|
|
|
+ queryGPURequests := `avg(
|
|
|
+ label_replace(
|
|
|
+ label_replace(
|
|
|
+ avg(
|
|
|
+ count_over_time(kube_pod_container_resource_requests{resource="nvidia_com_gpu", container!="",container!="POD", node!=""}[` + window + `])
|
|
|
+ *
|
|
|
+ avg_over_time(kube_pod_container_resource_requests{resource="nvidia_com_gpu", container!="",container!="POD", node!=""}[` + window + `])
|
|
|
+ ) by (namespace,container,pod,node) , "container_name","$1","container","(.+)"
|
|
|
+ ), "pod_name","$1","pod","(.+)"
|
|
|
+ )
|
|
|
+ ) by (namespace,container_name,pod_name,node)`
|
|
|
queryPVRequests := `avg(kube_persistentvolumeclaim_info) by (persistentvolumeclaim, storageclass, namespace)
|
|
|
*
|
|
|
on (persistentvolumeclaim, namespace) group_right(storageclass)
|
|
|
@@ -240,31 +282,31 @@ func ComputeCostData(cli prometheusClient.Client, clientset kubernetes.Interface
|
|
|
containerName := container.Name
|
|
|
|
|
|
// recreate the key and look up data for this container
|
|
|
- newKey := ns + "," + podName + "," + containerName
|
|
|
+ newKey := newContainerMetricFromValues(ns, podName, containerName, pod.Spec.NodeName).Key()
|
|
|
|
|
|
RAMReqV, ok := RAMReqMap[newKey]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no RAM requests for " + newKey)
|
|
|
+ klog.V(4).Info("no RAM requests for " + newKey)
|
|
|
RAMReqV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
RAMUsedV, ok := RAMUsedMap[newKey]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no RAM usage for " + newKey)
|
|
|
+ klog.V(4).Info("no RAM usage for " + newKey)
|
|
|
RAMUsedV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
CPUReqV, ok := CPUReqMap[newKey]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no CPU requests for " + newKey)
|
|
|
+ klog.V(4).Info("no CPU requests for " + newKey)
|
|
|
CPUReqV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
GPUReqV, ok := GPUReqMap[newKey]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no GPU requests for " + newKey)
|
|
|
+ klog.V(4).Info("no GPU requests for " + newKey)
|
|
|
GPUReqV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
CPUUsedV, ok := CPUUsedMap[newKey]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no CPU usage for " + newKey)
|
|
|
+ klog.V(4).Info("no CPU usage for " + newKey)
|
|
|
CPUUsedV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
|
|
|
@@ -300,37 +342,49 @@ func ComputeCostData(cli prometheusClient.Client, clientset kubernetes.Interface
|
|
|
|
|
|
} else {
|
|
|
// The container has been deleted. Not all information is sent to prometheus via ksm, so fill out what we can without k8s api
|
|
|
- // TODO: The nodename should be available from the prometheus query. Check if that node still exists and use that price
|
|
|
- klog.V(3).Info("The container " + key + " has been deleted. Calculating allocation but resulting object will be missing data.")
|
|
|
- c, _ := newContainerMetricFromKey(key)
|
|
|
+ klog.V(4).Info("The container " + key + " has been deleted. Calculating allocation but resulting object will be missing data.")
|
|
|
+ c, err := newContainerMetricFromKey(key)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
RAMReqV, ok := RAMReqMap[key]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no RAM requests for " + key)
|
|
|
+ klog.V(4).Info("no RAM requests for " + key)
|
|
|
RAMReqV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
RAMUsedV, ok := RAMUsedMap[key]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no RAM usage for " + key)
|
|
|
+ klog.V(4).Info("no RAM usage for " + key)
|
|
|
RAMUsedV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
CPUReqV, ok := CPUReqMap[key]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no CPU requests for " + key)
|
|
|
+ klog.V(4).Info("no CPU requests for " + key)
|
|
|
CPUReqV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
GPUReqV, ok := GPUReqMap[key]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no GPU requests for " + key)
|
|
|
+ klog.V(4).Info("no GPU requests for " + key)
|
|
|
GPUReqV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
CPUUsedV, ok := CPUUsedMap[key]
|
|
|
if !ok {
|
|
|
- klog.V(2).Info("no CPU usage for " + key)
|
|
|
+ klog.V(4).Info("no CPU usage for " + key)
|
|
|
CPUUsedV = []*Vector{&Vector{}}
|
|
|
}
|
|
|
- costs := &CostData{ // TODO: Expand the prometheus query/use prometheus to query for more data here if it exists.
|
|
|
+
|
|
|
+ var node *costAnalyzerCloud.Node
|
|
|
+ if n, ok := nodes[c.NodeName]; !ok {
|
|
|
+ //TODO: The node has been deleted from kubernetes as well. You will need to query historical node data to get it.
|
|
|
+ klog.V(2).Infof("Node \"%s\" has been deleted from Kubernetes. Query historical data to get it.", c.NodeName)
|
|
|
+ } else {
|
|
|
+ node = n
|
|
|
+ }
|
|
|
+ costs := &CostData{
|
|
|
Name: c.ContainerName,
|
|
|
PodName: c.PodName,
|
|
|
+ NodeName: c.NodeName,
|
|
|
+ NodeData: node,
|
|
|
Namespace: c.Namespace,
|
|
|
RAMReq: RAMReqV,
|
|
|
RAMUsed: RAMUsedV,
|
|
|
@@ -425,28 +479,60 @@ func getNodeCost(clientset kubernetes.Interface, cloud costAnalyzerCloud.Provide
|
|
|
cnode.RAM = n.Status.Capacity.Memory().String()
|
|
|
}
|
|
|
ram = float64(n.Status.Capacity.Memory().Value())
|
|
|
- if cnode.RAMCost == "" { // We couldn't find a ramcost, so fix cpu and allocate ram accordingly
|
|
|
- basePrice, _ := strconv.ParseFloat(cnode.BaseCPUPrice, 64)
|
|
|
- totalCPUPrice := basePrice * cpu
|
|
|
- var nodePrice float64
|
|
|
- if cnode.Cost != "" {
|
|
|
- klog.V(3).Infof("Use given nodeprice as whole node price")
|
|
|
- nodePrice, _ = strconv.ParseFloat(cnode.Cost, 64)
|
|
|
- } else {
|
|
|
- klog.V(3).Infof("Use cpuprice as whole node price")
|
|
|
- nodePrice, _ = strconv.ParseFloat(cnode.VCPUCost, 64) // all the price was allocated the the CPU
|
|
|
+
|
|
|
+ if cnode.GPU != "" && cnode.GPUCost == "" { // We couldn't find a gpu cost, so fix cpu and ram, then accordingly
|
|
|
+ klog.V(3).Infof("GPU without cost found, calculating...")
|
|
|
+ basePrice, err := strconv.ParseFloat(cnode.BaseCPUPrice, 64)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- if totalCPUPrice >= nodePrice {
|
|
|
- totalCPUPrice = 0.9 * nodePrice // just allocate RAM costs to 10% of the node price here to avoid 0 or negative in the numerator
|
|
|
+ nodePrice, err := strconv.ParseFloat(cnode.Cost, 64)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- ramPrice := (nodePrice - totalCPUPrice) / (ram / 1024 / 1024 / 1024)
|
|
|
- cpuPrice := totalCPUPrice / cpu
|
|
|
-
|
|
|
- cnode.VCPUCost = fmt.Sprintf("%f", cpuPrice)
|
|
|
+ totalCPUPrice := basePrice * cpu
|
|
|
+ totalRAMPrice := 0.1 * totalCPUPrice
|
|
|
+ ramPrice := totalRAMPrice / (ram / 1024 / 1024 / 1024)
|
|
|
+ gpuPrice := nodePrice - totalCPUPrice - totalRAMPrice
|
|
|
+ cnode.VCPUCost = fmt.Sprintf("%f", basePrice)
|
|
|
cnode.RAMCost = fmt.Sprintf("%f", ramPrice)
|
|
|
cnode.RAMBytes = fmt.Sprintf("%f", ram)
|
|
|
- klog.V(2).Infof("Node \"%s\" RAM Cost := %v", name, cnode.RAMCost)
|
|
|
+ cnode.GPUCost = fmt.Sprintf("%f", gpuPrice)
|
|
|
+ klog.V(2).Infof("Computed \"%s\" GPU Cost := %v", name, cnode.GPUCost)
|
|
|
+ } else {
|
|
|
+ if cnode.RAMCost == "" { // We couldn't find a ramcost, so fix cpu and allocate ram accordingly
|
|
|
+ basePrice, err := strconv.ParseFloat(cnode.BaseCPUPrice, 64)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ totalCPUPrice := basePrice * cpu
|
|
|
+ var nodePrice float64
|
|
|
+ if cnode.Cost != "" {
|
|
|
+ klog.V(3).Infof("Use given nodeprice as whole node price")
|
|
|
+ nodePrice, err = strconv.ParseFloat(cnode.Cost, 64)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ klog.V(3).Infof("Use cpuprice as whole node price")
|
|
|
+ nodePrice, err = strconv.ParseFloat(cnode.VCPUCost, 64) // all the price was allocated the the CPU
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if totalCPUPrice >= nodePrice {
|
|
|
+ totalCPUPrice = 0.9 * nodePrice // just allocate RAM costs to 10% of the node price here to avoid 0 or negative in the numerator
|
|
|
+ }
|
|
|
+ ramPrice := (nodePrice - totalCPUPrice) / (ram / 1024 / 1024 / 1024)
|
|
|
+ cpuPrice := totalCPUPrice / cpu
|
|
|
+
|
|
|
+ cnode.VCPUCost = fmt.Sprintf("%f", cpuPrice)
|
|
|
+ cnode.RAMCost = fmt.Sprintf("%f", ramPrice)
|
|
|
+ cnode.RAMBytes = fmt.Sprintf("%f", ram)
|
|
|
+ klog.V(3).Infof("Computed \"%s\" RAM Cost := %v", name, cnode.RAMCost)
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
nodes[name] = cnode
|
|
|
}
|
|
|
return nodes, nil
|
|
|
@@ -514,15 +600,57 @@ func getPodDeployments(clientset kubernetes.Interface, podList *v1.PodList) (map
|
|
|
|
|
|
func ComputeCostDataRange(cli prometheusClient.Client, clientset kubernetes.Interface, cloud costAnalyzerCloud.Provider,
|
|
|
startString, endString, windowString string) (map[string]*CostData, error) {
|
|
|
- queryRAMRequests := `avg(label_replace(label_replace(avg((count_over_time(kube_pod_container_resource_requests_memory_bytes{container!="",container!="POD"}[` + windowString + `]) * avg_over_time(kube_pod_container_resource_requests_memory_bytes{container!="",container!="POD"}[` + windowString + `]))) by (namespace,container,pod) , "container_name","$1","container","(.+)"), "pod_name","$1","pod","(.+)") ) by (namespace,container_name, pod_name)`
|
|
|
- queryRAMUsage := `sort_desc(avg(count_over_time(container_memory_usage_bytes{container_name!="",container_name!="POD"}[` + windowString + `]) * avg_over_time(container_memory_usage_bytes{container_name!="",container_name!="POD"}[` + windowString + `])) by (namespace,container_name,pod_name,instance))`
|
|
|
- queryCPURequests := `avg(label_replace(label_replace(avg((count_over_time(kube_pod_container_resource_requests_cpu_cores{container!="",container!="POD"}[` + windowString + `]) * avg_over_time(kube_pod_container_resource_requests_cpu_cores{container!="",container!="POD"}[` + windowString + `]))) by (namespace,container,pod) , "container_name","$1","container","(.+)"), "pod_name","$1","pod","(.+)") ) by (namespace,container_name, pod_name)`
|
|
|
- queryCPUUsage := `avg(rate(container_cpu_usage_seconds_total{container_name!="",container_name!="POD"}[` + windowString + `])) by (namespace,container_name,pod_name,instance)`
|
|
|
- queryGPURequests := `avg(label_replace(label_replace(avg((count_over_time(kube_pod_container_resource_requests{resource="nvidia_com_gpu", container!="",container!="POD"}[` + windowString + `]) * avg_over_time(kube_pod_container_resource_requests{resource="nvidia_com_gpu", container!="",container!="POD"}[` + windowString + `]))) by (namespace,container,pod) , "container_name","$1","container","(.+)"), "pod_name","$1","pod","(.+)") ) by (namespace,container_name, pod_name)`
|
|
|
+ queryRAMRequests := `avg(
|
|
|
+ label_replace(
|
|
|
+ label_replace(
|
|
|
+ avg(
|
|
|
+ count_over_time(kube_pod_container_resource_requests_memory_bytes{container!="",container!="POD", node!=""}[` + windowString + `])
|
|
|
+ *
|
|
|
+ avg_over_time(kube_pod_container_resource_requests_memory_bytes{container!="",container!="POD", node!=""}[` + windowString + `])
|
|
|
+ ) by (namespace,container,pod,node) , "container_name","$1","container","(.+)"
|
|
|
+ ), "pod_name","$1","pod","(.+)"
|
|
|
+ )
|
|
|
+ ) by (namespace,container_name,pod_name,node)`
|
|
|
+ queryRAMUsage := `sort_desc(
|
|
|
+ avg(
|
|
|
+ label_replace(count_over_time(container_memory_usage_bytes{container_name!="",container_name!="POD", instance!=""}[` + windowString + `]), "node", "$1", "instance","(.+)")
|
|
|
+ *
|
|
|
+ label_replace(avg_over_time(container_memory_usage_bytes{container_name!="",container_name!="POD", instance!=""}[` + windowString + `]), "node", "$1", "instance","(.+)")
|
|
|
+ ) by (namespace,container_name,pod_name,node)
|
|
|
+ )`
|
|
|
+ queryCPURequests := `avg(
|
|
|
+ label_replace(
|
|
|
+ label_replace(
|
|
|
+ avg(
|
|
|
+ count_over_time(kube_pod_container_resource_requests_cpu_cores{container!="",container!="POD", node!=""}[` + windowString + `])
|
|
|
+ *
|
|
|
+ avg_over_time(kube_pod_container_resource_requests_cpu_cores{container!="",container!="POD", node!=""}[` + windowString + `])
|
|
|
+ ) by (namespace,container,pod,node) , "container_name","$1","container","(.+)"
|
|
|
+ ), "pod_name","$1","pod","(.+)"
|
|
|
+ )
|
|
|
+ ) by (namespace,container_name,pod_name,node)`
|
|
|
+ queryCPUUsage := `avg(
|
|
|
+ label_replace(
|
|
|
+ rate(
|
|
|
+ container_cpu_usage_seconds_total{container_name!="",container_name!="POD", instance!=""}[` + windowString + `]
|
|
|
+ ) , "node", "$1", "instance", "(.+)"
|
|
|
+ )
|
|
|
+ ) by (namespace,container_name,pod_name,node)`
|
|
|
+ queryGPURequests := `avg(
|
|
|
+ label_replace(
|
|
|
+ label_replace(
|
|
|
+ avg(
|
|
|
+ count_over_time(kube_pod_container_resource_requests{resource="nvidia_com_gpu", container!="",container!="POD", node!=""}[` + windowString + `])
|
|
|
+ *
|
|
|
+ avg_over_time(kube_pod_container_resource_requests{resource="nvidia_com_gpu", container!="",container!="POD", node!=""}[` + windowString + `])
|
|
|
+ ) by (namespace,container,pod,node) , "container_name","$1","container","(.+)"
|
|
|
+ ), "pod_name","$1","pod","(.+)"
|
|
|
+ )
|
|
|
+ ) by (namespace,container_name,pod_name,node)`
|
|
|
queryPVRequests := `avg(kube_persistentvolumeclaim_info) by (persistentvolumeclaim, storageclass, namespace)
|
|
|
- *
|
|
|
- on (persistentvolumeclaim, namespace) group_right(storageclass)
|
|
|
- sum(kube_persistentvolumeclaim_resource_requests_storage_bytes) by (persistentvolumeclaim, namespace)`
|
|
|
+ *
|
|
|
+ on (persistentvolumeclaim, namespace) group_right(storageclass)
|
|
|
+ sum(kube_persistentvolumeclaim_resource_requests_storage_bytes) by (persistentvolumeclaim, namespace)`
|
|
|
normalization := `max(count_over_time(kube_pod_container_resource_requests_memory_bytes{}[` + windowString + `]))`
|
|
|
|
|
|
layout := "2006-01-02T15:04:05.000Z"
|
|
|
@@ -701,7 +829,7 @@ func ComputeCostDataRange(cli prometheusClient.Client, clientset kubernetes.Inte
|
|
|
for i, container := range pod.Spec.Containers {
|
|
|
containerName := container.Name
|
|
|
|
|
|
- newKey := ns + "," + podName + "," + containerName
|
|
|
+ newKey := newContainerMetricFromValues(ns, podName, containerName, pod.Spec.NodeName).Key()
|
|
|
|
|
|
RAMReqV, ok := RAMReqMap[newKey]
|
|
|
if !ok {
|
|
|
@@ -761,8 +889,7 @@ func ComputeCostDataRange(cli prometheusClient.Client, clientset kubernetes.Inte
|
|
|
|
|
|
} else {
|
|
|
// The container has been deleted. Not all information is sent to prometheus via ksm, so fill out what we can without k8s api
|
|
|
- // TODO: The nodename should be available from the prometheus query. Check if that node still exists and use that price
|
|
|
- klog.V(3).Info("The container " + key + " has been deleted. Calculating allocation but resulting object will be missing data.")
|
|
|
+ klog.V(4).Info("The container " + key + " has been deleted. Calculating allocation but resulting object will be missing data.")
|
|
|
c, _ := newContainerMetricFromKey(key)
|
|
|
RAMReqV, ok := RAMReqMap[key]
|
|
|
if !ok {
|
|
|
@@ -789,9 +916,18 @@ func ComputeCostDataRange(cli prometheusClient.Client, clientset kubernetes.Inte
|
|
|
klog.V(2).Info("no CPU usage for " + key)
|
|
|
CPUUsedV = []*Vector{}
|
|
|
}
|
|
|
- costs := &CostData{ // TODO: Expand the prometheus query/use prometheus to query for more data here if it exists.
|
|
|
+ var node *costAnalyzerCloud.Node
|
|
|
+ if n, ok := nodes[c.NodeName]; !ok {
|
|
|
+ //TODO: The node has been deleted from kubernetes as well. You will need to query historical node data to get it.
|
|
|
+ klog.V(2).Infof("Node \"%s\" has been deleted from Kubernetes. Query historical data to get it.", c.NodeName)
|
|
|
+ } else {
|
|
|
+ node = n
|
|
|
+ }
|
|
|
+ costs := &CostData{
|
|
|
Name: c.ContainerName,
|
|
|
PodName: c.PodName,
|
|
|
+ NodeName: c.NodeName,
|
|
|
+ NodeData: node,
|
|
|
Namespace: c.Namespace,
|
|
|
RAMReq: RAMReqV,
|
|
|
RAMUsed: RAMUsedV,
|
|
|
@@ -1058,27 +1194,39 @@ type ContainerMetric struct {
|
|
|
Namespace string
|
|
|
PodName string
|
|
|
ContainerName string
|
|
|
+ NodeName string
|
|
|
}
|
|
|
|
|
|
func (c *ContainerMetric) Key() string {
|
|
|
- return c.Namespace + "," + c.PodName + "," + c.ContainerName
|
|
|
+ return c.Namespace + "," + c.PodName + "," + c.ContainerName + "," + c.NodeName
|
|
|
}
|
|
|
|
|
|
func newContainerMetricFromKey(key string) (*ContainerMetric, error) {
|
|
|
s := strings.Split(key, ",")
|
|
|
- if len(s) == 3 {
|
|
|
+ if len(s) == 4 {
|
|
|
return &ContainerMetric{
|
|
|
Namespace: s[0],
|
|
|
PodName: s[1],
|
|
|
ContainerName: s[2],
|
|
|
+ NodeName: s[3],
|
|
|
}, nil
|
|
|
}
|
|
|
return nil, fmt.Errorf("Not a valid key")
|
|
|
}
|
|
|
|
|
|
+func newContainerMetricFromValues(ns string, podName string, containerName string, nodeName string) *ContainerMetric {
|
|
|
+ return &ContainerMetric{
|
|
|
+ Namespace: ns,
|
|
|
+ PodName: podName,
|
|
|
+ ContainerName: containerName,
|
|
|
+ NodeName: nodeName,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func newContainerMetricsFromPod(pod v1.Pod) ([]*ContainerMetric, error) {
|
|
|
podName := pod.GetObjectMeta().GetName()
|
|
|
ns := pod.GetObjectMeta().GetNamespace()
|
|
|
+ node := pod.Spec.NodeName
|
|
|
var cs []*ContainerMetric
|
|
|
for _, container := range pod.Spec.Containers {
|
|
|
containerName := container.Name
|
|
|
@@ -1086,6 +1234,7 @@ func newContainerMetricsFromPod(pod v1.Pod) ([]*ContainerMetric, error) {
|
|
|
Namespace: ns,
|
|
|
PodName: podName,
|
|
|
ContainerName: containerName,
|
|
|
+ NodeName: node,
|
|
|
})
|
|
|
}
|
|
|
return cs, nil
|
|
|
@@ -1116,10 +1265,19 @@ func newContainerMetricFromPrometheus(metrics map[string]interface{}) (*Containe
|
|
|
if !ok {
|
|
|
return nil, fmt.Errorf("Prometheus vector does not have string namespace")
|
|
|
}
|
|
|
+ node, ok := metrics["node"]
|
|
|
+ if !ok {
|
|
|
+ return nil, fmt.Errorf("Prometheus vector does not have node name")
|
|
|
+ }
|
|
|
+ nodeName, ok := node.(string)
|
|
|
+ if !ok {
|
|
|
+ return nil, fmt.Errorf("Prometheus vector does not have string nodename")
|
|
|
+ }
|
|
|
return &ContainerMetric{
|
|
|
ContainerName: containerName,
|
|
|
PodName: podName,
|
|
|
Namespace: namespace,
|
|
|
+ NodeName: nodeName,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
@@ -1163,7 +1321,7 @@ func getContainerMetricVector(qr interface{}, normalize bool, normalizationValue
|
|
|
Timestamp: dataPoint[0].(float64),
|
|
|
Value: v,
|
|
|
}
|
|
|
- klog.V(2).Info("key: " + containerMetric.Key())
|
|
|
+ klog.V(4).Info("key: " + containerMetric.Key())
|
|
|
containerData[containerMetric.Key()] = []*Vector{toReturn}
|
|
|
}
|
|
|
return containerData, nil
|