Skip to content

Commit 7a80cea

Browse files
authored
Support GP3 volume type (#2130)
1 parent eb9b30a commit 7a80cea

File tree

11 files changed

+345
-183
lines changed

11 files changed

+345
-183
lines changed

cli/cmd/cluster.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
"github.com/cortexlabs/cortex/pkg/lib/exit"
4040
"github.com/cortexlabs/cortex/pkg/lib/files"
4141
libjson "github.com/cortexlabs/cortex/pkg/lib/json"
42+
libmath "github.com/cortexlabs/cortex/pkg/lib/math"
4243
"github.com/cortexlabs/cortex/pkg/lib/pointer"
4344
"github.com/cortexlabs/cortex/pkg/lib/prompt"
4445
s "github.com/cortexlabs/cortex/pkg/lib/strings"
@@ -786,8 +787,8 @@ func getInfoOperatorResponse(operatorEndpoint string) (*schema.InfoResponse, err
786787
func printInfoPricing(infoResponse *schema.InfoResponse, clusterConfig clusterconfig.Config) {
787788
eksPrice := aws.EKSPrices[clusterConfig.Region]
788789
operatorInstancePrice := aws.InstanceMetadatas[clusterConfig.Region]["t3.medium"].Price
789-
operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 20 / 30 / 24
790-
metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 40 / 30 / 24
790+
operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp3"].PriceGB * 20 / 30 / 24
791+
metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * (40 + 2) / 30 / 24
791792
nlbPrice := aws.NLBMetadatas[clusterConfig.Region].Price
792793
natUnitPrice := aws.NATMetadatas[clusterConfig.Region].Price
793794

@@ -811,9 +812,13 @@ func printInfoPricing(infoResponse *schema.InfoResponse, clusterConfig clusterco
811812
numInstances := len(nodesInfo)
812813

813814
ebsPrice := aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceGB * float64(ng.InstanceVolumeSize) / 30 / 24
814-
if ng.InstanceVolumeType.String() == "io1" && ng.InstanceVolumeIOPS != nil {
815+
if ng.InstanceVolumeType == clusterconfig.IO1VolumeType && ng.InstanceVolumeIOPS != nil {
815816
ebsPrice += aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS * float64(*ng.InstanceVolumeIOPS) / 30 / 24
816817
}
818+
if ng.InstanceVolumeType == clusterconfig.GP3VolumeType && ng.InstanceVolumeIOPS != nil && ng.InstanceVolumeThroughput != nil {
819+
ebsPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS-3000)*float64(*ng.InstanceVolumeIOPS)/30/24)
820+
ebsPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceThroughput-125)*float64(*ng.InstanceVolumeThroughput)/30/24)
821+
}
817822
totalEBSPrice := ebsPrice * float64(numInstances)
818823

819824
totalInstancePrice := float64(0)
@@ -833,12 +838,12 @@ func printInfoPricing(infoResponse *schema.InfoResponse, clusterConfig clusterco
833838
} else if clusterConfig.NATGateway == clusterconfig.HighlyAvailableNATGateway {
834839
natTotalPrice = natUnitPrice * float64(len(clusterConfig.AvailabilityZones))
835840
}
836-
totalPrice := eksPrice + totalNodeGroupsPrice + operatorInstancePrice*2 + operatorEBSPrice + metricsEBSPrice + nlbPrice*2 + natTotalPrice
841+
totalPrice := eksPrice + totalNodeGroupsPrice + 2*(operatorInstancePrice+operatorEBSPrice) + metricsEBSPrice + nlbPrice*2 + natTotalPrice
837842
fmt.Printf(console.Bold("\nyour cluster currently costs %s per hour\n\n"), s.DollarsAndCents(totalPrice))
838843

839844
rows = append(rows, []interface{}{"2 t3.medium instances for cortex", s.DollarsMaxPrecision(operatorInstancePrice * 2)})
840-
rows = append(rows, []interface{}{"1 20gb ebs volume for the operator", s.DollarsAndTenthsOfCents(operatorEBSPrice)})
841-
rows = append(rows, []interface{}{"1 40gb ebs volume for prometheus", s.DollarsAndTenthsOfCents(metricsEBSPrice)})
845+
rows = append(rows, []interface{}{"2 20gb ebs volumes for the operator", s.DollarsAndTenthsOfCents(operatorEBSPrice * 2)})
846+
rows = append(rows, []interface{}{"1 40+2gb ebs volumes for metrics (prometheus and grafana)", s.DollarsAndTenthsOfCents(metricsEBSPrice)})
842847
rows = append(rows, []interface{}{"2 network load balancers", s.DollarsMaxPrecision(nlbPrice*2) + " total"})
843848

844849
if clusterConfig.NATGateway == clusterconfig.SingleNATGateway {

cli/cmd/lib_cluster_config.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/cortexlabs/cortex/pkg/lib/errors"
2929
"github.com/cortexlabs/cortex/pkg/lib/files"
3030
"github.com/cortexlabs/cortex/pkg/lib/maps"
31+
libmath "github.com/cortexlabs/cortex/pkg/lib/math"
3132
"github.com/cortexlabs/cortex/pkg/lib/pointer"
3233
"github.com/cortexlabs/cortex/pkg/lib/prompt"
3334
s "github.com/cortexlabs/cortex/pkg/lib/strings"
@@ -149,8 +150,8 @@ func getInstallClusterConfig(awsClient *aws.Client, clusterConfigFile string, di
149150
func confirmInstallClusterConfig(clusterConfig *clusterconfig.Config, awsClient *aws.Client, disallowPrompt bool) {
150151
eksPrice := aws.EKSPrices[clusterConfig.Region]
151152
operatorInstancePrice := aws.InstanceMetadatas[clusterConfig.Region]["t3.medium"].Price
152-
operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 20 / 30 / 24
153-
metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 40 / 30 / 24
153+
operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp3"].PriceGB * 20 / 30 / 24
154+
metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * (40 + 2) / 30 / 24
154155
nlbPrice := aws.NLBMetadatas[clusterConfig.Region].Price
155156
natUnitPrice := aws.NATMetadatas[clusterConfig.Region].Price
156157

@@ -170,15 +171,19 @@ func confirmInstallClusterConfig(clusterConfig *clusterconfig.Config, awsClient
170171
rows = append(rows, []interface{}{"1 eks cluster", s.DollarsMaxPrecision(eksPrice)})
171172

172173
ngNameToSpotInstancesUsed := map[string]int{}
173-
fixedPrice := eksPrice + 2*operatorInstancePrice + operatorEBSPrice + metricsEBSPrice + 2*nlbPrice + natTotalPrice
174+
fixedPrice := eksPrice + 2*(operatorInstancePrice+operatorEBSPrice) + metricsEBSPrice + 2*nlbPrice + natTotalPrice
174175
totalMinPrice := fixedPrice
175176
totalMaxPrice := fixedPrice
176177
for _, ng := range clusterConfig.NodeGroups {
177178
apiInstancePrice := aws.InstanceMetadatas[clusterConfig.Region][ng.InstanceType].Price
178179
apiEBSPrice := aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceGB * float64(ng.InstanceVolumeSize) / 30 / 24
179-
if ng.InstanceVolumeType.String() == "io1" && ng.InstanceVolumeIOPS != nil {
180+
if ng.InstanceVolumeType == clusterconfig.IO1VolumeType && ng.InstanceVolumeIOPS != nil {
180181
apiEBSPrice += aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS * float64(*ng.InstanceVolumeIOPS) / 30 / 24
181182
}
183+
if ng.InstanceVolumeType == clusterconfig.GP3VolumeType && ng.InstanceVolumeIOPS != nil && ng.InstanceVolumeThroughput != nil {
184+
apiEBSPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS-3000)*float64(*ng.InstanceVolumeIOPS)/30/24)
185+
apiEBSPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceThroughput-125)*float64(*ng.InstanceVolumeThroughput)/30/24)
186+
}
182187

183188
totalMinPrice += float64(ng.MinInstances) * (apiInstancePrice + apiEBSPrice)
184189
totalMaxPrice += float64(ng.MaxInstances) * (apiInstancePrice + apiEBSPrice)
@@ -212,8 +217,8 @@ func confirmInstallClusterConfig(clusterConfig *clusterconfig.Config, awsClient
212217
}
213218

214219
rows = append(rows, []interface{}{"2 t3.medium instances for cortex", s.DollarsMaxPrecision(operatorInstancePrice * 2)})
215-
rows = append(rows, []interface{}{"1 20gb ebs volume for the operator", s.DollarsAndTenthsOfCents(operatorEBSPrice)})
216-
rows = append(rows, []interface{}{"1 40gb ebs volume for prometheus", s.DollarsAndTenthsOfCents(metricsEBSPrice)})
220+
rows = append(rows, []interface{}{"2 20gb ebs volumes for the operator", s.DollarsAndTenthsOfCents(operatorEBSPrice * 2)})
221+
rows = append(rows, []interface{}{"1 40+2gb ebs volumes for metrics (prometheus and grafana)", s.DollarsAndTenthsOfCents(metricsEBSPrice)})
217222
rows = append(rows, []interface{}{"2 network load balancers", s.DollarsMaxPrecision(nlbPrice) + " each"})
218223

219224
if clusterConfig.NATGateway == clusterconfig.SingleNATGateway {

docs/clusters/management/create.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,17 @@ node_groups:
3636
min_instances: 1 # minimum number of instances
3737
max_instances: 5 # maximum number of instances
3838
instance_volume_size: 50 # disk storage size per instance (GB)
39-
instance_volume_type: gp2 # instance volume type [gp2 | io1 | st1 | sc1]
40-
# instance_volume_iops: 3000 # instance volume iops (only applicable to io1)
39+
instance_volume_type: gp3 # instance volume type [gp2 | gp3 | io1 | st1 | sc1]
40+
# instance_volume_iops: 3000 # instance volume iops (only applicable to io1/gp3)
41+
# instance_volume_throughput: 125 # instance volume throughput (only applicable to gp3)
4142
spot: false # whether to use spot instances
4243

4344
- name: ng-gpu
4445
instance_type: g4dn.xlarge
4546
min_instances: 1
4647
max_instances: 5
4748
instance_volume_size: 50
48-
instance_volume_type: gp2
49-
# instance_volume_iops: 3000
49+
instance_volume_type: gp3
5050
spot: false
5151
...
5252

manager/generate_eks.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,11 @@ def apply_clusterconfig(nodegroup, config):
8585
"volumeType": config["instance_volume_type"],
8686
"desiredCapacity": 1 if config["min_instances"] == 0 else config["min_instances"],
8787
}
88-
# add iops to settings if volume_type is io1
89-
if config["instance_volume_type"] == "io1":
88+
# add iops to settings if volume_type is io1/gp3
89+
if config["instance_volume_type"] in ["io1", "gp3"]:
9090
clusterconfig_settings["volumeIOPS"] = config["instance_volume_iops"]
91+
if config["instance_volume_type"] == "gp3":
92+
clusterconfig_settings["volumeThroughput"] = config["instance_volume_throughput"]
9193

9294
return merge_override(nodegroup, clusterconfig_settings)
9395

@@ -213,6 +215,10 @@ def generate_eks(cluster_config_path, ami_json_path):
213215
"minSize": 2,
214216
"maxSize": 2,
215217
"desiredCapacity": 2,
218+
"volumeType": "gp3",
219+
"volumeSize": 20,
220+
"volumeIOPS": 3000,
221+
"volumeThroughput": 125,
216222
}
217223
operator_nodegroup = merge_override(operator_nodegroup, operator_settings)
218224

pkg/lib/aws/gen_resource_metadata.py

+41-1
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,48 @@ def get_ebs_metadata(pricing):
181181
price = list(price_dimensions.values())[0]["pricePerUnit"]["USD"]
182182

183183
metadata["price_iops"] = price
184+
metadata["price_throughput"] = 0
184185
metadata["iops_configurable"] = "true"
186+
metadata["throughput_configurable"] = "false"
187+
188+
elif product["attributes"].get("volumeApiName") == "gp3":
189+
# go through pricing data until found data about IOPS and throughput pricing
190+
for _, product_iops in pricing["products"].items():
191+
if product_iops.get("attributes") is None:
192+
continue
193+
if product_iops.get("productFamily") not in [
194+
"System Operation",
195+
"Provisioned Throughput",
196+
]:
197+
continue
198+
if product_iops["attributes"].get("volumeApiName") != "gp3":
199+
continue
200+
if product_iops["attributes"].get("group") not in ["EBS IOPS", "EBS Throughput"]:
201+
continue
202+
if product_iops["attributes"].get("provisioned") != "Yes":
203+
continue
204+
205+
price_dimensions = list(pricing["terms"]["OnDemand"][product_iops["sku"]].values())[
206+
0
207+
]["priceDimensions"]
208+
if product_iops["attributes"].get("group") == "EBS IOPS":
209+
price_iops = list(price_dimensions.values())[0]["pricePerUnit"]["USD"]
210+
else:
211+
price_throughput = (
212+
float(list(price_dimensions.values())[0]["pricePerUnit"]["USD"]) / 1000
213+
)
214+
215+
metadata["price_iops"] = price_iops
216+
metadata["price_throughput"] = price_throughput
217+
metadata["throughput_configurable"] = "true"
218+
metadata["iops_configurable"] = "true"
185219

186220
# set default values for all other storage types
187221
else:
188222
metadata["price_iops"] = 0
223+
metadata["price_throughput"] = 0
189224
metadata["iops_configurable"] = "false"
225+
metadata["throughput_configurable"] = "false"
190226

191227
storage_mapping[product["attributes"]["volumeApiName"]] = metadata
192228

@@ -265,7 +301,9 @@ def get_eks_price(region):
265301
Region string `json:"region"`
266302
PriceGB float64 `json:"price_gb"`
267303
PriceIOPS float64 `json:"price_iops"`
304+
PriceThroughput float64 `json:"price_throughput"`
268305
IOPSConfigurable bool `json:"iops_configurable"`
306+
ThroughputConfigurable bool `json:"throughput_configurable"`
269307
Type string `json:"type"`
270308
}
271309
@@ -326,7 +364,7 @@ def get_eks_price(region):
326364
)
327365

328366
ebs_type_map_template = Template(
329-
""""${type}": {Region: "${region}",Type: "${type}", PriceGB: ${price_gb}, PriceIOPS: ${price_iops}, IOPSConfigurable: ${iops_configurable}},
367+
""""${type}": {Region: "${region}",Type: "${type}", PriceGB: ${price_gb}, PriceIOPS: ${price_iops}, PriceThroughput: ${price_throughput}, IOPSConfigurable: ${iops_configurable}, ThroughputConfigurable: ${throughput_configurable}},
330368
"""
331369
)
332370

@@ -381,7 +419,9 @@ def main():
381419
"type": ebs_type,
382420
"price_gb": metadata["price_gb"],
383421
"price_iops": metadata["price_iops"],
422+
"price_throughput": metadata["price_throughput"],
384423
"iops_configurable": metadata["iops_configurable"],
424+
"throughput_configurable": metadata["throughput_configurable"],
385425
}
386426
)
387427

0 commit comments

Comments
 (0)