Skip to content

Commit 6446346

Browse files
committed
GCE: base pricing of custom instances on their instance family type
1 parent 24b4df0 commit 6446346

File tree

2 files changed

+143
-11
lines changed

2 files changed

+143
-11
lines changed

cluster-autoscaler/cloudprovider/gce/gce_price_model.go

Lines changed: 101 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package gce
1818

1919
import (
2020
"math"
21+
"strings"
2122
"time"
2223

2324
apiv1 "k8s.io/api/core/v1"
@@ -31,15 +32,59 @@ type GcePriceModel struct {
3132

3233
const (
3334
//TODO: Move it to a config file.
34-
cpuPricePerHour = 0.033174
35-
memoryPricePerHourPerGb = 0.004446
36-
preemptibleDiscount = 0.00698 / 0.033174
35+
cpuPricePerHour = 0.022890
36+
memoryPricePerHourPerGb = 0.003067
37+
preemptibleDiscount = 0.006867 / 0.022890
3738
gpuPricePerHour = 0.700
3839

3940
preemptibleLabel = "cloud.google.com/gke-preemptible"
4041
)
4142

4243
var (
44+
predefinedCpuPricePerHour = map[string]float64{
45+
"c2": 0.03398,
46+
"e2": 0.021811,
47+
"m1": 0.0348,
48+
"n1": 0.031611,
49+
"n2": 0.031611,
50+
"n2d": 0.027502,
51+
}
52+
predefinedMemoryPricePerHourPerGb = map[string]float64{
53+
"c2": 0.00455,
54+
"e2": 0.002923,
55+
"m1": 0.0051,
56+
"n1": 0.004237,
57+
"n2": 0.004237,
58+
"n2d": 0.003686,
59+
}
60+
predefinedPreemptibleDiscount = map[string]float64{
61+
"c2": 0.00822 / 0.03398,
62+
"e2": 0.006543 / 0.021811,
63+
"m1": 0.00733 / 0.0348,
64+
"n1": 0.006655 / 0.031611,
65+
"n2": 0.007650 / 0.031611,
66+
"n2d": 0.006655 / 0.027502,
67+
}
68+
69+
customCpuPricePerHour = map[string]float64{
70+
"e2": 0.022890,
71+
"n1": 0.033174,
72+
"n2": 0.033174,
73+
"n2d": 0.028877,
74+
}
75+
customMemoryPricePerHourPerGb = map[string]float64{
76+
"e2": 0.003067,
77+
"n1": 0.004446,
78+
"n2": 0.004446,
79+
"n2d": 0.003870,
80+
}
81+
customPreemptibleDiscount = map[string]float64{
82+
"e2": 0.006867 / 0.022890,
83+
"n1": 0.00698 / 0.033174,
84+
"n2": 0.00802 / 0.033174,
85+
"n2d": 0.006980 / 0.028877,
86+
}
87+
4388
// e2-micro and e2-small have allocatable set too high resulting in
4489
// overcommit. To make cluster autoscaler prefer e2-medium given the choice
4590
// between the three machine types, the prices for e2-micro and e2-small
@@ -278,10 +323,8 @@ func (model *GcePriceModel) NodePrice(node *apiv1.Node, startTime time.Time, end
278323
}
279324
}
280325
if !basePriceFound {
281-
price = getBasePrice(node.Status.Capacity, startTime, endTime)
282-
if node.Labels != nil && node.Labels[preemptibleLabel] == "true" {
283-
price = price * preemptibleDiscount
284-
}
326+
price = getBasePrice(node.Status.Capacity, node.Labels[apiv1.LabelInstanceType], startTime, endTime)
327+
price = price * getPreemptibleDiscount(node)
285328
}
286329
// TODO: handle SSDs.
287330

@@ -295,27 +338,74 @@ func getHours(startTime time.Time, endTime time.Time) float64 {
295338
return hours
296339
}
297340

341+
func getInstanceFamily(instanceType string) string {
342+
return strings.Split(instanceType, "-")[0]
343+
}
344+
345+
func isInstanceCustom(instanceType string) bool {
346+
return strings.Contains(instanceType, "custom")
347+
}
348+
349+
func getPreemptibleDiscount(node *apiv1.Node) float64 {
350+
if node.Labels[preemptibleLabel] != "true" {
351+
return 1.0
352+
}
353+
instanceType := node.Labels[apiv1.LabelInstanceType]
354+
instanceFamily := getInstanceFamily(instanceType)
355+
356+
discountMap := predefinedPreemptibleDiscount
357+
if isInstanceCustom(instanceType) {
358+
discountMap = customPreemptibleDiscount
359+
}
360+
361+
if _, found := discountMap[instanceFamily]; found {
362+
return discountMap[instanceFamily]
363+
}
364+
return preemptibleDiscount
365+
}
366+
298367
// PodPrice returns a theoretical minimum price of running a pod for a given
299368
// period of time on a perfectly matching machine.
300369
func (model *GcePriceModel) PodPrice(pod *apiv1.Pod, startTime time.Time, endTime time.Time) (float64, error) {
301370
price := 0.0
302371
for _, container := range pod.Spec.Containers {
303-
price += getBasePrice(container.Resources.Requests, startTime, endTime)
372+
price += getBasePrice(container.Resources.Requests, "", startTime, endTime)
304373
price += getAdditionalPrice(container.Resources.Requests, startTime, endTime)
305374
}
306375
return price, nil
307376
}
308377

309-
func getBasePrice(resources apiv1.ResourceList, startTime time.Time, endTime time.Time) float64 {
378+
func getBasePrice(resources apiv1.ResourceList, instanceType string, startTime time.Time, endTime time.Time) float64 {
310379
if len(resources) == 0 {
311380
return 0
312381
}
313382
hours := getHours(startTime, endTime)
383+
instanceFamily := getInstanceFamily(instanceType)
384+
isCustom := isInstanceCustom(instanceType)
314385
price := 0.0
386+
315387
cpu := resources[apiv1.ResourceCPU]
388+
cpuPrice := cpuPricePerHour
389+
cpuPriceMap := predefinedCpuPricePerHour
390+
if isCustom {
391+
cpuPriceMap = customCpuPricePerHour
392+
}
393+
if _, found := cpuPriceMap[instanceFamily]; found {
394+
cpuPrice = cpuPriceMap[instanceFamily]
395+
}
396+
price += float64(cpu.MilliValue()) / 1000.0 * cpuPrice * hours
397+
316398
mem := resources[apiv1.ResourceMemory]
317-
price += float64(cpu.MilliValue()) / 1000.0 * cpuPricePerHour * hours
318-
price += float64(mem.Value()) / float64(units.GiB) * memoryPricePerHourPerGb * hours
399+
memPrice := memoryPricePerHourPerGb
400+
memPriceMap := predefinedMemoryPricePerHourPerGb
401+
if isCustom {
402+
memPriceMap = customMemoryPricePerHourPerGb
403+
}
404+
if _, found := memPriceMap[instanceFamily]; found {
405+
memPrice = memPriceMap[instanceFamily]
406+
}
407+
price += float64(mem.Value()) / float64(units.GiB) * memPrice * hours
408+
319409
return price
320410
}
321411

cluster-autoscaler/cloudprovider/gce/gce_price_model_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,30 @@ func TestGetNodePrice(t *testing.T) {
4747
OperatingSystemLinux)
4848
labels2[preemptibleLabel] = "true"
4949

50+
labels3, _ := BuildGenericLabels(GceRef{
51+
Name: "kubernetes-minion-group",
52+
Project: "mwielgus-proj",
53+
Zone: "us-central1-b"},
54+
"n1-custom",
55+
"sillyname",
56+
OperatingSystemLinux)
57+
58+
labels4, _ := BuildGenericLabels(GceRef{
59+
Name: "kubernetes-minion-group",
60+
Project: "mwielgus-proj",
61+
Zone: "us-central1-b"},
62+
"n1-unknown",
63+
"sillyname",
64+
OperatingSystemLinux)
65+
66+
labels5, _ := BuildGenericLabels(GceRef{
67+
Name: "kubernetes-minion-group",
68+
Project: "mwielgus-proj",
69+
Zone: "us-central1-b"},
70+
"e2-custom",
71+
"sillyname",
72+
OperatingSystemLinux)
73+
5074
model := &GcePriceModel{}
5175
now := time.Now()
5276

@@ -66,6 +90,7 @@ func TestGetNodePrice(t *testing.T) {
6690

6791
// custom node
6892
node3 := BuildTestNode("sillyname3", 8000, 30*units.GiB)
93+
node3.Labels = labels3
6994
price3, err := model.NodePrice(node3, now, now.Add(time.Hour))
7095
assert.NoError(t, err)
7196
// custom nodes should be slightly more expensive than regular.
@@ -92,10 +117,27 @@ func TestGetNodePrice(t *testing.T) {
92117

93118
// small custom node
94119
node6 := BuildTestNode("sillyname6", 1000, 3750*units.MiB)
120+
node6.Labels = labels3
95121
price6, err := model.NodePrice(node6, now, now.Add(time.Hour))
96122
assert.NoError(t, err)
97123
// 8 times smaller node should be 8 times less expensive.
98124
assert.True(t, math.Abs(price3-8*price6) < 0.1)
125+
126+
// unknown instance type
127+
node7 := BuildTestNode("sillyname7", 8000, 30*units.GiB)
128+
node7.Labels = labels4
129+
price7, err := model.NodePrice(node7, now, now.Add(time.Hour))
130+
assert.NoError(t, err)
131+
// Unknown instance type should have similar pricing to its node family
132+
assert.True(t, math.Abs(price1-price7) < 0.1)
133+
134+
// custom node from cheaper family
135+
node8 := BuildTestNode("sillyname8", 9000, 32*units.GiB)
136+
node8.Labels = labels5
137+
price8, err := model.NodePrice(node8, now, now.Add(time.Hour))
138+
assert.NoError(t, err)
139+
// Bigger custom e2 node should be cheaper than smaller custom n1 node
140+
assert.True(t, price8 < price3)
99141
}
100142

101143
func TestGetPodPrice(t *testing.T) {

0 commit comments

Comments
 (0)