Skip to content

Commit

Permalink
Add ephemeral storage price to NodePrice
Browse files Browse the repository at this point in the history
  • Loading branch information
yaroslava-serdiuk committed May 25, 2022
1 parent b4cedfb commit e930b1a
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 4 deletions.
26 changes: 23 additions & 3 deletions cluster-autoscaler/cloudprovider/gce/gce_price_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package gce

import (
"math"
"strconv"
"strings"
"time"

Expand All @@ -43,8 +44,9 @@ func NewGcePriceModel(info PriceInfo, ephemeralStorageSupport bool) *GcePriceMod
}

const (
preemptibleLabel = "cloud.google.com/gke-preemptible"
spotLabel = "cloud.google.com/gke-spot"
preemptibleLabel = "cloud.google.com/gke-preemptible"
spotLabel = "cloud.google.com/gke-spot"
ephemeralStorageLocalSsdLabel = "cloud.google.com/gke-ephemeral-storage-local-ssd"
)

// NodePrice returns a price of running the given node for a given period of time.
Expand Down Expand Up @@ -75,6 +77,25 @@ func (model *GcePriceModel) NodePrice(node *apiv1.Node, startTime time.Time, end
}
}

// Ephemeral Storage
if model.EphemeralStorageSupport {
var ephemeralStoragePrice float64
if node.Labels[ephemeralStorageLocalSsdLabel] == "true" || node.Annotations[EphemeralStorageLocalSsd] == "true" {
localSsdCount, _ := strconv.ParseFloat(node.Annotations[LocalSsdCount], 64)
localSsdPrice := model.PriceInfo.LocalSsdPricePerHour()
if hasPreemptiblePricing(node) {
localSsdPrice = model.PriceInfo.SpotLocalSsdPricePerHour()
}
ephemeralStoragePrice = localSsdCount * float64(LocalSSDDiskSizeInGiB) * localSsdPrice
} else {
bootDiskSize, _ := strconv.ParseFloat(node.Annotations[BootDiskSize], 64)
bootDiskPrice := model.PriceInfo.BootDiskPricePerHour()[node.Annotations[BootDiskType]]
ephemeralStoragePrice = bootDiskPrice * bootDiskSize
}

price += ephemeralStoragePrice * getHours(startTime, endTime)
}

// GPUs
if gpuRequest, found := node.Status.Capacity[gpu.ResourceNvidiaGPU]; found {
gpuPrice := model.PriceInfo.BaseGpuPricePerHour()
Expand All @@ -94,7 +115,6 @@ func (model *GcePriceModel) NodePrice(node *apiv1.Node, startTime time.Time, end
price += float64(gpuRequest.MilliValue()) / 1000.0 * gpuPrice * getHours(startTime, endTime)
}

// TODO: handle SSDs.
return price, nil
}

Expand Down
50 changes: 49 additions & 1 deletion cluster-autoscaler/cloudprovider/gce/gce_price_model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package gce

import (
"math"
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -58,13 +59,41 @@ func testNode(t *testing.T, nodeName string, instanceType string, millicpu int64
return node
}

func testNodeEphemeralStorage(t *testing.T, nodeName string, isEphemeralStorageLocalSsd bool, localSsdCount int, bootDiskType string, bootDiskSize int, isSpot bool) *apiv1.Node {
node := BuildTestNode(nodeName, 8000, 30*units.GiB)
labels, err := BuildGenericLabels(GceRef{
Name: "kubernetes-minion-group",
Project: "mwielgus-proj",
Zone: "us-central1-b"},
"n1-standard-8",
nodeName,
OperatingSystemLinux,
DefaultArch)
assert.NoError(t, err)
if isSpot {
labels[spotLabel] = "true"
}
if isEphemeralStorageLocalSsd {
labels[ephemeralStorageLocalSsdLabel] = "true"
}
node.Labels = labels
node.Annotations = make(map[string]string)
if localSsdCount > 0 {
node.Annotations[LocalSsdCount] = strconv.Itoa(localSsdCount)
}
node.Annotations[BootDiskSize] = strconv.Itoa(bootDiskSize)
node.Annotations[BootDiskType] = bootDiskType
return node
}

// this test is meant to cover all the branches in pricing logic, not all possible types of instances
func TestGetNodePrice(t *testing.T) {
// tests assert that price(cheaperNode) < priceComparisonCoefficient * price(expensiveNode)
cases := map[string]struct {
cheaperNode *apiv1.Node
expensiveNode *apiv1.Node
priceComparisonCoefficient float64
expanderSupport bool
}{
// instance types
"e2 is cheaper than n1": {
Expand Down Expand Up @@ -172,11 +201,30 @@ func TestGetNodePrice(t *testing.T) {
expensiveNode: testNode(t, "known", "n1-custom", 8000, 30*units.GiB, "", 0, false, false),
priceComparisonCoefficient: 1.001,
},
// Ephemeral storage support
"ephemeral storage support: less local SSD count is cheaper": {
cheaperNode: testNodeEphemeralStorage(t, "cheapNode", true, 2, "pd-standard", 100, false),
expensiveNode: testNodeEphemeralStorage(t, "expensiveNode", true, 4, "pd-standard", 100, false),
priceComparisonCoefficient: 1,
expanderSupport: true,
},
"ephemeral storage support: local SSD cheaper than boot disk": {
cheaperNode: testNodeEphemeralStorage(t, "cheapNode", true, 1, "pd-standard", 100, true),
expensiveNode: testNodeEphemeralStorage(t, "expensiveNode", false, 0, "pd-ssd", 100, false),
priceComparisonCoefficient: 1,
expanderSupport: true,
},
"ephemeral storage support: node with cheper boot disk option is cheaper": {
cheaperNode: testNodeEphemeralStorage(t, "cheapNode", false, 0, "pd-standard", 100, false),
expensiveNode: testNodeEphemeralStorage(t, "expensiveNode", false, 0, "pd-ssd", 100, false),
priceComparisonCoefficient: 1,
expanderSupport: true,
},
}

for tn, tc := range cases {
t.Run(tn, func(t *testing.T) {
model := NewGcePriceModel(NewGcePriceInfo(), false)
model := NewGcePriceModel(NewGcePriceInfo(), tc.expanderSupport)
now := time.Now()

price1, err := model.NodePrice(tc.cheaperNode, now, now.Add(time.Hour))
Expand Down

0 comments on commit e930b1a

Please sign in to comment.