Quellcode durchsuchen

Example flat vs hierarchical pricing function

Niko Kovacevic vor 6 Monaten
Ursprung
Commit
578fe31ec8

+ 5 - 0
core/pkg/model/kubemodel/cluster.go

@@ -5,4 +5,9 @@ type Cluster struct {
 	Provider Provider
 	Account  string
 	Name     string
+
+	// NOTE: Alternate hierarchical structure
+	Namespaces        map[string]*Namespace
+	Nodes             map[string]*Node
+	PersistentVolumes map[string]*PersistentVolume
 }

+ 5 - 4
core/pkg/model/kubemodel/container.go

@@ -2,8 +2,9 @@ package kubemodel
 
 // TODO complete
 type Container struct {
-	UID       string
-	PodUID    string
-	Name      string
-	Resources ResourceQuantities
+	UID          string
+	PodUID       string
+	Name         string
+	Resources    ResourceQuantities
+	VolumeMounts map[string]ResourceQuantity
 }

+ 131 - 0
core/pkg/model/kubemodel/example.go

@@ -0,0 +1,131 @@
+package kubemodel
+
+import "github.com/opencost/opencost/core/pkg/stats"
+
+type ContainerCosts struct {
+	Container
+	CPUCost     float64
+	RAMCost     float64
+	StorageCost float64
+}
+
+// ApplyContainerCosts is a simplified example of a consumer of the
+// KubeModelSet data structure.
+func ApplyContainerCosts(kms *KubeModelSet, pm PricingModel, out chan map[string]*ContainerCosts) {
+	// 1. Option that uses the flat structure
+	flatAlgorithm(kms, pm, out)
+
+	// 2. Option that uses the cost-priority hierarchical structure
+	hierarchyAlgorithm(kms, pm, out)
+}
+
+// O(C * avg(PVC))
+// 42 lines
+// 2 loops
+// 3 max indentation
+func flatAlgorithm(kms *KubeModelSet, pm PricingModel, out chan map[string]*ContainerCosts) {
+	// O(1) -- slight advantage in being able to allocate memory for the whole set of Containers
+	ccs := make(map[string]*ContainerCosts, len(kms.Containers))
+
+	// O(C)
+	for _, container := range kms.Containers {
+		// O(1)
+		cc := &ContainerCosts{Container: *container}
+
+		// O(1)
+		pod := kms.Pods[container.PodUID]
+
+		// O(1)
+		node := kms.Nodes[pod.NodeUID]
+
+		// O(1) -- presumably
+		nodePricing := pm.GetNodePricing(node)
+
+		// O(1)
+		cc.CPUCost = container.Resources[ResourceCPU].Values[stats.Val] * nodePricing[ResourceCPU].Price
+		cc.RAMCost = container.Resources[ResourceMemory].Values[stats.Val] * nodePricing[ResourceMemory].Price
+
+		// O(PVC)
+		for pvcUID, storage := range container.VolumeMounts {
+			// O(1)
+			pvc := kms.PersistentVolumeClaims[pvcUID]
+
+			// O(1)
+			pv := kms.PersistentVolumes[pvc.PersistentVolumeUID]
+
+			// O(1)
+			pvPricing := pm.GetPersistentVolumePricing(pv)
+
+			// O(1)
+			cc.StorageCost += storage.Values[stats.Val] * pvPricing[ResourceStorage].Price
+		}
+
+		// O(1)
+		ccs[container.UID] = cc
+	}
+
+	// O(1)
+	out <- ccs
+}
+
+// O(N * avg(P) * avg(C) * avg(PVC)) == O(C * avg(PVC))
+// 42 lines
+// 4 loops
+// 5 max indentation
+func hierarchyAlgorithm(kms *KubeModelSet, pm PricingModel, out chan map[string]*ContainerCosts) {
+	// O(1) -- total number of containers would have to be indexed / cached
+	ccs := map[string]*ContainerCosts{}
+
+	// O(N)
+	for _, node := range kms.Cluster.Nodes {
+		// O(avg(P))
+		for _, pod := range node.Pods {
+			// O(avg(C))
+			for _, container := range pod.Containers {
+				// O(1)
+				cc := &ContainerCosts{Container: *container}
+
+				// O(1) -- presumably
+				nodePricing := pm.GetNodePricing(node)
+
+				// O(1)
+				cc.CPUCost = container.Resources[ResourceCPU].Values[stats.Val] * nodePricing[ResourceCPU].Price
+				cc.RAMCost = container.Resources[ResourceMemory].Values[stats.Val] * nodePricing[ResourceMemory].Price
+
+				// O(PVC) -- going to need to use the same access pattern as the
+				// flat data structure, anyways, unless somehow PVCs / PVs will'
+				// be nested within Containers (e.g. under VolumeMounts?)
+				for pvcUID, storage := range container.VolumeMounts {
+					// O(1)
+					pvc := kms.PersistentVolumeClaims[pvcUID]
+
+					// O(1)
+					pv := kms.PersistentVolumes[pvc.PersistentVolumeUID]
+
+					// O(1)
+					pvPricing := pm.GetPersistentVolumePricing(pv)
+
+					// O(1)
+					cc.StorageCost += storage.Values[stats.Val] * pvPricing[ResourceStorage].Price
+				}
+
+				ccs[container.UID] = cc
+			}
+		}
+	}
+
+	out <- ccs
+}
+
+type PricingModel interface {
+	GetNodePricing(node *Node) Pricing
+	GetPersistentVolumePricing(pv *PersistentVolume) Pricing
+}
+
+type Pricing map[Resource]ResourcePricing
+
+type ResourcePricing struct {
+	Resource Resource
+	Unit     Unit
+	Price    float64
+}

+ 11 - 9
core/pkg/model/kubemodel/kubemodel.go

@@ -7,15 +7,17 @@ import (
 )
 
 type KubeModelSet struct {
-	Metadata       *KubeModelSetMetadata
-	Window         Window
-	Cluster        *Cluster
-	Containers     map[string]*Container
-	Namespaces     map[string]*Namespace
-	Nodes          map[string]*Node
-	Pods           map[string]*Pod
-	ResourceQuotas map[string]*ResourceQuota
-	indexes        *kubeModelSetIndexes
+	Metadata               *KubeModelSetMetadata
+	Window                 Window
+	Cluster                *Cluster
+	Containers             map[string]*Container
+	Nodes                  map[string]*Node
+	Namespaces             map[string]*Namespace
+	PersistentVolumes      map[string]*PersistentVolume
+	PersistentVolumeClaims map[string]*PersistentVolumeClaim
+	Pods                   map[string]*Pod
+	ResourceQuotas         map[string]*ResourceQuota
+	indexes                *kubeModelSetIndexes
 }
 
 func NewKubeModelSet(start, end time.Time) *KubeModelSet {

+ 3 - 0
core/pkg/model/kubemodel/node.go

@@ -6,4 +6,7 @@ type Node struct {
 	ClusterUID string
 	Name       string
 	Resources  ResourceQuantities
+
+	// NOTE: Alternate hierarchical structure
+	Pods map[string]*Pod
 }

+ 4 - 0
core/pkg/model/kubemodel/pod.go

@@ -8,4 +8,8 @@ type Pod struct {
 	OwnerUID                  string
 	PersistentVolumeClaimUIDs []string
 	Name                      string
+
+	// NOTE: Alternate hierarchical structure
+	Containers             map[string]*Container
+	PersistentVolumeClaims map[string]*PersistentVolumeClaim
 }