diff --git a/cli/cmd/cluster.go b/cli/cmd/cluster.go index 0b784f4432..8a2e3ba1f0 100644 --- a/cli/cmd/cluster.go +++ b/cli/cmd/cluster.go @@ -39,6 +39,7 @@ import ( "github.com/cortexlabs/cortex/pkg/lib/exit" "github.com/cortexlabs/cortex/pkg/lib/files" libjson "github.com/cortexlabs/cortex/pkg/lib/json" + libmath "github.com/cortexlabs/cortex/pkg/lib/math" "github.com/cortexlabs/cortex/pkg/lib/pointer" "github.com/cortexlabs/cortex/pkg/lib/prompt" s "github.com/cortexlabs/cortex/pkg/lib/strings" @@ -786,8 +787,8 @@ func getInfoOperatorResponse(operatorEndpoint string) (*schema.InfoResponse, err func printInfoPricing(infoResponse *schema.InfoResponse, clusterConfig clusterconfig.Config) { eksPrice := aws.EKSPrices[clusterConfig.Region] operatorInstancePrice := aws.InstanceMetadatas[clusterConfig.Region]["t3.medium"].Price - operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 20 / 30 / 24 - metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 40 / 30 / 24 + operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp3"].PriceGB * 20 / 30 / 24 + metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * (40 + 2) / 30 / 24 nlbPrice := aws.NLBMetadatas[clusterConfig.Region].Price natUnitPrice := aws.NATMetadatas[clusterConfig.Region].Price @@ -811,9 +812,13 @@ func printInfoPricing(infoResponse *schema.InfoResponse, clusterConfig clusterco numInstances := len(nodesInfo) ebsPrice := aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceGB * float64(ng.InstanceVolumeSize) / 30 / 24 - if ng.InstanceVolumeType.String() == "io1" && ng.InstanceVolumeIOPS != nil { + if ng.InstanceVolumeType == clusterconfig.IO1VolumeType && ng.InstanceVolumeIOPS != nil { ebsPrice += aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS * float64(*ng.InstanceVolumeIOPS) / 30 / 24 } + if ng.InstanceVolumeType == clusterconfig.GP3VolumeType && ng.InstanceVolumeIOPS != nil && ng.InstanceVolumeThroughput != nil { + ebsPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS-3000)*float64(*ng.InstanceVolumeIOPS)/30/24) + ebsPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceThroughput-125)*float64(*ng.InstanceVolumeThroughput)/30/24) + } totalEBSPrice := ebsPrice * float64(numInstances) totalInstancePrice := float64(0) @@ -833,12 +838,12 @@ func printInfoPricing(infoResponse *schema.InfoResponse, clusterConfig clusterco } else if clusterConfig.NATGateway == clusterconfig.HighlyAvailableNATGateway { natTotalPrice = natUnitPrice * float64(len(clusterConfig.AvailabilityZones)) } - totalPrice := eksPrice + totalNodeGroupsPrice + operatorInstancePrice*2 + operatorEBSPrice + metricsEBSPrice + nlbPrice*2 + natTotalPrice + totalPrice := eksPrice + totalNodeGroupsPrice + 2*(operatorInstancePrice+operatorEBSPrice) + metricsEBSPrice + nlbPrice*2 + natTotalPrice fmt.Printf(console.Bold("\nyour cluster currently costs %s per hour\n\n"), s.DollarsAndCents(totalPrice)) rows = append(rows, []interface{}{"2 t3.medium instances for cortex", s.DollarsMaxPrecision(operatorInstancePrice * 2)}) - rows = append(rows, []interface{}{"1 20gb ebs volume for the operator", s.DollarsAndTenthsOfCents(operatorEBSPrice)}) - rows = append(rows, []interface{}{"1 40gb ebs volume for prometheus", s.DollarsAndTenthsOfCents(metricsEBSPrice)}) + rows = append(rows, []interface{}{"2 20gb ebs volumes for the operator", s.DollarsAndTenthsOfCents(operatorEBSPrice * 2)}) + rows = append(rows, []interface{}{"1 40+2gb ebs volumes for metrics (prometheus and grafana)", s.DollarsAndTenthsOfCents(metricsEBSPrice)}) rows = append(rows, []interface{}{"2 network load balancers", s.DollarsMaxPrecision(nlbPrice*2) + " total"}) if clusterConfig.NATGateway == clusterconfig.SingleNATGateway { diff --git a/cli/cmd/lib_cluster_config.go b/cli/cmd/lib_cluster_config.go index cc63a59550..2f4c8df22b 100644 --- a/cli/cmd/lib_cluster_config.go +++ b/cli/cmd/lib_cluster_config.go @@ -28,6 +28,7 @@ import ( "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/files" "github.com/cortexlabs/cortex/pkg/lib/maps" + libmath "github.com/cortexlabs/cortex/pkg/lib/math" "github.com/cortexlabs/cortex/pkg/lib/pointer" "github.com/cortexlabs/cortex/pkg/lib/prompt" s "github.com/cortexlabs/cortex/pkg/lib/strings" @@ -149,8 +150,8 @@ func getInstallClusterConfig(awsClient *aws.Client, clusterConfigFile string, di func confirmInstallClusterConfig(clusterConfig *clusterconfig.Config, awsClient *aws.Client, disallowPrompt bool) { eksPrice := aws.EKSPrices[clusterConfig.Region] operatorInstancePrice := aws.InstanceMetadatas[clusterConfig.Region]["t3.medium"].Price - operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 20 / 30 / 24 - metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 40 / 30 / 24 + operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp3"].PriceGB * 20 / 30 / 24 + metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * (40 + 2) / 30 / 24 nlbPrice := aws.NLBMetadatas[clusterConfig.Region].Price natUnitPrice := aws.NATMetadatas[clusterConfig.Region].Price @@ -170,15 +171,19 @@ func confirmInstallClusterConfig(clusterConfig *clusterconfig.Config, awsClient rows = append(rows, []interface{}{"1 eks cluster", s.DollarsMaxPrecision(eksPrice)}) ngNameToSpotInstancesUsed := map[string]int{} - fixedPrice := eksPrice + 2*operatorInstancePrice + operatorEBSPrice + metricsEBSPrice + 2*nlbPrice + natTotalPrice + fixedPrice := eksPrice + 2*(operatorInstancePrice+operatorEBSPrice) + metricsEBSPrice + 2*nlbPrice + natTotalPrice totalMinPrice := fixedPrice totalMaxPrice := fixedPrice for _, ng := range clusterConfig.NodeGroups { apiInstancePrice := aws.InstanceMetadatas[clusterConfig.Region][ng.InstanceType].Price apiEBSPrice := aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceGB * float64(ng.InstanceVolumeSize) / 30 / 24 - if ng.InstanceVolumeType.String() == "io1" && ng.InstanceVolumeIOPS != nil { + if ng.InstanceVolumeType == clusterconfig.IO1VolumeType && ng.InstanceVolumeIOPS != nil { apiEBSPrice += aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS * float64(*ng.InstanceVolumeIOPS) / 30 / 24 } + if ng.InstanceVolumeType == clusterconfig.GP3VolumeType && ng.InstanceVolumeIOPS != nil && ng.InstanceVolumeThroughput != nil { + apiEBSPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS-3000)*float64(*ng.InstanceVolumeIOPS)/30/24) + apiEBSPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[clusterConfig.Region][ng.InstanceVolumeType.String()].PriceThroughput-125)*float64(*ng.InstanceVolumeThroughput)/30/24) + } totalMinPrice += float64(ng.MinInstances) * (apiInstancePrice + apiEBSPrice) totalMaxPrice += float64(ng.MaxInstances) * (apiInstancePrice + apiEBSPrice) @@ -212,8 +217,8 @@ func confirmInstallClusterConfig(clusterConfig *clusterconfig.Config, awsClient } rows = append(rows, []interface{}{"2 t3.medium instances for cortex", s.DollarsMaxPrecision(operatorInstancePrice * 2)}) - rows = append(rows, []interface{}{"1 20gb ebs volume for the operator", s.DollarsAndTenthsOfCents(operatorEBSPrice)}) - rows = append(rows, []interface{}{"1 40gb ebs volume for prometheus", s.DollarsAndTenthsOfCents(metricsEBSPrice)}) + rows = append(rows, []interface{}{"2 20gb ebs volumes for the operator", s.DollarsAndTenthsOfCents(operatorEBSPrice * 2)}) + rows = append(rows, []interface{}{"1 40+2gb ebs volumes for metrics (prometheus and grafana)", s.DollarsAndTenthsOfCents(metricsEBSPrice)}) rows = append(rows, []interface{}{"2 network load balancers", s.DollarsMaxPrecision(nlbPrice) + " each"}) if clusterConfig.NATGateway == clusterconfig.SingleNATGateway { diff --git a/docs/clusters/management/create.md b/docs/clusters/management/create.md index 5e778707f7..b6c7e90505 100644 --- a/docs/clusters/management/create.md +++ b/docs/clusters/management/create.md @@ -36,8 +36,9 @@ node_groups: min_instances: 1 # minimum number of instances max_instances: 5 # maximum number of instances instance_volume_size: 50 # disk storage size per instance (GB) - instance_volume_type: gp2 # instance volume type [gp2 | io1 | st1 | sc1] - # instance_volume_iops: 3000 # instance volume iops (only applicable to io1) + instance_volume_type: gp3 # instance volume type [gp2 | gp3 | io1 | st1 | sc1] + # instance_volume_iops: 3000 # instance volume iops (only applicable to io1/gp3) + # instance_volume_throughput: 125 # instance volume throughput (only applicable to gp3) spot: false # whether to use spot instances - name: ng-gpu @@ -45,8 +46,7 @@ node_groups: min_instances: 1 max_instances: 5 instance_volume_size: 50 - instance_volume_type: gp2 - # instance_volume_iops: 3000 + instance_volume_type: gp3 spot: false ... diff --git a/manager/generate_eks.py b/manager/generate_eks.py index 5800d84247..141e79d915 100644 --- a/manager/generate_eks.py +++ b/manager/generate_eks.py @@ -85,9 +85,11 @@ def apply_clusterconfig(nodegroup, config): "volumeType": config["instance_volume_type"], "desiredCapacity": 1 if config["min_instances"] == 0 else config["min_instances"], } - # add iops to settings if volume_type is io1 - if config["instance_volume_type"] == "io1": + # add iops to settings if volume_type is io1/gp3 + if config["instance_volume_type"] in ["io1", "gp3"]: clusterconfig_settings["volumeIOPS"] = config["instance_volume_iops"] + if config["instance_volume_type"] == "gp3": + clusterconfig_settings["volumeThroughput"] = config["instance_volume_throughput"] return merge_override(nodegroup, clusterconfig_settings) @@ -213,6 +215,10 @@ def generate_eks(cluster_config_path, ami_json_path): "minSize": 2, "maxSize": 2, "desiredCapacity": 2, + "volumeType": "gp3", + "volumeSize": 20, + "volumeIOPS": 3000, + "volumeThroughput": 125, } operator_nodegroup = merge_override(operator_nodegroup, operator_settings) diff --git a/pkg/lib/aws/gen_resource_metadata.py b/pkg/lib/aws/gen_resource_metadata.py index 7fa0278682..9e74ed739d 100644 --- a/pkg/lib/aws/gen_resource_metadata.py +++ b/pkg/lib/aws/gen_resource_metadata.py @@ -181,12 +181,48 @@ def get_ebs_metadata(pricing): price = list(price_dimensions.values())[0]["pricePerUnit"]["USD"] metadata["price_iops"] = price + metadata["price_throughput"] = 0 metadata["iops_configurable"] = "true" + metadata["throughput_configurable"] = "false" + + elif product["attributes"].get("volumeApiName") == "gp3": + # go through pricing data until found data about IOPS and throughput pricing + for _, product_iops in pricing["products"].items(): + if product_iops.get("attributes") is None: + continue + if product_iops.get("productFamily") not in [ + "System Operation", + "Provisioned Throughput", + ]: + continue + if product_iops["attributes"].get("volumeApiName") != "gp3": + continue + if product_iops["attributes"].get("group") not in ["EBS IOPS", "EBS Throughput"]: + continue + if product_iops["attributes"].get("provisioned") != "Yes": + continue + + price_dimensions = list(pricing["terms"]["OnDemand"][product_iops["sku"]].values())[ + 0 + ]["priceDimensions"] + if product_iops["attributes"].get("group") == "EBS IOPS": + price_iops = list(price_dimensions.values())[0]["pricePerUnit"]["USD"] + else: + price_throughput = ( + float(list(price_dimensions.values())[0]["pricePerUnit"]["USD"]) / 1000 + ) + + metadata["price_iops"] = price_iops + metadata["price_throughput"] = price_throughput + metadata["throughput_configurable"] = "true" + metadata["iops_configurable"] = "true" # set default values for all other storage types else: metadata["price_iops"] = 0 + metadata["price_throughput"] = 0 metadata["iops_configurable"] = "false" + metadata["throughput_configurable"] = "false" storage_mapping[product["attributes"]["volumeApiName"]] = metadata @@ -265,7 +301,9 @@ def get_eks_price(region): Region string `json:"region"` PriceGB float64 `json:"price_gb"` PriceIOPS float64 `json:"price_iops"` + PriceThroughput float64 `json:"price_throughput"` IOPSConfigurable bool `json:"iops_configurable"` + ThroughputConfigurable bool `json:"throughput_configurable"` Type string `json:"type"` } @@ -326,7 +364,7 @@ def get_eks_price(region): ) ebs_type_map_template = Template( - """"${type}": {Region: "${region}",Type: "${type}", PriceGB: ${price_gb}, PriceIOPS: ${price_iops}, IOPSConfigurable: ${iops_configurable}}, + """"${type}": {Region: "${region}",Type: "${type}", PriceGB: ${price_gb}, PriceIOPS: ${price_iops}, PriceThroughput: ${price_throughput}, IOPSConfigurable: ${iops_configurable}, ThroughputConfigurable: ${throughput_configurable}}, """ ) @@ -381,7 +419,9 @@ def main(): "type": ebs_type, "price_gb": metadata["price_gb"], "price_iops": metadata["price_iops"], + "price_throughput": metadata["price_throughput"], "iops_configurable": metadata["iops_configurable"], + "throughput_configurable": metadata["throughput_configurable"], } ) diff --git a/pkg/lib/aws/resource_metadata.go b/pkg/lib/aws/resource_metadata.go index cae7f047ab..80d976375b 100644 --- a/pkg/lib/aws/resource_metadata.go +++ b/pkg/lib/aws/resource_metadata.go @@ -43,11 +43,13 @@ type NATMetadata struct { } type EBSMetadata struct { - Region string `json:"region"` - PriceGB float64 `json:"price_gb"` - PriceIOPS float64 `json:"price_iops"` - IOPSConfigurable bool `json:"iops_configurable"` - Type string `json:"type"` + Region string `json:"region"` + PriceGB float64 `json:"price_gb"` + PriceIOPS float64 `json:"price_iops"` + PriceThroughput float64 `json:"price_throughput"` + IOPSConfigurable bool `json:"iops_configurable"` + ThroughputConfigurable bool `json:"throughput_configurable"` + Type string `json:"type"` } // region -> instance type -> instance metadata @@ -943,6 +945,7 @@ var InstanceMetadatas = map[string]map[string]InstanceMetadata{ "i3.4xlarge": {Region: "ap-northeast-3", Type: "i3.4xlarge", Memory: kresource.MustParse("124928Mi"), CPU: kresource.MustParse("16"), GPU: 0, Inf: 0, Price: 1.464}, "i3.8xlarge": {Region: "ap-northeast-3", Type: "i3.8xlarge", Memory: kresource.MustParse("249856Mi"), CPU: kresource.MustParse("32"), GPU: 0, Inf: 0, Price: 2.928}, "i3.large": {Region: "ap-northeast-3", Type: "i3.large", Memory: kresource.MustParse("15616Mi"), CPU: kresource.MustParse("2"), GPU: 0, Inf: 0, Price: 0.183}, + "i3.metal": {Region: "ap-northeast-3", Type: "i3.metal", Memory: kresource.MustParse("524288Mi"), CPU: kresource.MustParse("64"), GPU: 0, Inf: 0, Price: 5.856}, "i3.xlarge": {Region: "ap-northeast-3", Type: "i3.xlarge", Memory: kresource.MustParse("31232Mi"), CPU: kresource.MustParse("4"), GPU: 0, Inf: 0, Price: 0.366}, "i3en.12xlarge": {Region: "ap-northeast-3", Type: "i3en.12xlarge", Memory: kresource.MustParse("393216Mi"), CPU: kresource.MustParse("48"), GPU: 0, Inf: 0, Price: 6.384}, "i3en.24xlarge": {Region: "ap-northeast-3", Type: "i3en.24xlarge", Memory: kresource.MustParse("786432Mi"), CPU: kresource.MustParse("96"), GPU: 0, Inf: 0, Price: 12.768}, @@ -4699,6 +4702,7 @@ var InstanceMetadatas = map[string]map[string]InstanceMetadata{ "i3.4xlarge": {Region: "us-gov-east-1", Type: "i3.4xlarge", Memory: kresource.MustParse("124928Mi"), CPU: kresource.MustParse("16"), GPU: 0, Inf: 0, Price: 1.504}, "i3.8xlarge": {Region: "us-gov-east-1", Type: "i3.8xlarge", Memory: kresource.MustParse("249856Mi"), CPU: kresource.MustParse("32"), GPU: 0, Inf: 0, Price: 3.008}, "i3.large": {Region: "us-gov-east-1", Type: "i3.large", Memory: kresource.MustParse("15616Mi"), CPU: kresource.MustParse("2"), GPU: 0, Inf: 0, Price: 0.188}, + "i3.metal": {Region: "us-gov-east-1", Type: "i3.metal", Memory: kresource.MustParse("524288Mi"), CPU: kresource.MustParse("64"), GPU: 0, Inf: 0, Price: 6.016}, "i3.xlarge": {Region: "us-gov-east-1", Type: "i3.xlarge", Memory: kresource.MustParse("31232Mi"), CPU: kresource.MustParse("4"), GPU: 0, Inf: 0, Price: 0.376}, "i3en.12xlarge": {Region: "us-gov-east-1", Type: "i3en.12xlarge", Memory: kresource.MustParse("393216Mi"), CPU: kresource.MustParse("48"), GPU: 0, Inf: 0, Price: 6.552}, "i3en.24xlarge": {Region: "us-gov-east-1", Type: "i3en.24xlarge", Memory: kresource.MustParse("786432Mi"), CPU: kresource.MustParse("96"), GPU: 0, Inf: 0, Price: 13.104}, @@ -5863,181 +5867,181 @@ var NATMetadatas = map[string]NATMetadata{ // region -> EBS metadata var EBSMetadatas = map[string]map[string]EBSMetadata{ "af-south-1": { - "gp2": {Region: "af-south-1", Type: "gp2", PriceGB: 0.1309, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "af-south-1", Type: "gp3", PriceGB: 0.1047, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "af-south-1", Type: "io1", PriceGB: 0.16422, PriceIOPS: 0.0856800000, IOPSConfigurable: true}, - "sc1": {Region: "af-south-1", Type: "sc1", PriceGB: 0.019992, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "af-south-1", Type: "st1", PriceGB: 0.0595, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "af-south-1", Type: "gp2", PriceGB: 0.1309, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "af-south-1", Type: "gp3", PriceGB: 0.1047, PriceIOPS: 0.0065000000, PriceThroughput: 0.0536576, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "af-south-1", Type: "io1", PriceGB: 0.16422, PriceIOPS: 0.0856800000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "sc1": {Region: "af-south-1", Type: "sc1", PriceGB: 0.019992, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "af-south-1", Type: "st1", PriceGB: 0.0595, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "ap-east-1": { - "gp2": {Region: "ap-east-1", Type: "gp2", PriceGB: 0.132, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "ap-east-1", Type: "gp3", PriceGB: 0.1056, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "ap-east-1", Type: "io1", PriceGB: 0.1518, PriceIOPS: 0.0792000000, IOPSConfigurable: true}, - "io2": {Region: "ap-east-1", Type: "io2", PriceGB: 0.1518, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "ap-east-1", Type: "sc1", PriceGB: 0.0198, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "ap-east-1", Type: "st1", PriceGB: 0.0594, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "ap-east-1", Type: "gp2", PriceGB: 0.132, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "ap-east-1", Type: "gp3", PriceGB: 0.1056, PriceIOPS: 0.0066000000, PriceThroughput: 0.0540672, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "ap-east-1", Type: "io1", PriceGB: 0.1518, PriceIOPS: 0.0792000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "ap-east-1", Type: "io2", PriceGB: 0.1518, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "ap-east-1", Type: "sc1", PriceGB: 0.0198, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "ap-east-1", Type: "st1", PriceGB: 0.0594, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "ap-northeast-1": { - "gp2": {Region: "ap-northeast-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "ap-northeast-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "ap-northeast-1", Type: "io1", PriceGB: 0.142, PriceIOPS: 0.0740000000, IOPSConfigurable: true}, - "io2": {Region: "ap-northeast-1", Type: "io2", PriceGB: 0.142, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "ap-northeast-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "ap-northeast-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "ap-northeast-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "ap-northeast-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0.0060000000, PriceThroughput: 0.049152, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "ap-northeast-1", Type: "io1", PriceGB: 0.142, PriceIOPS: 0.0740000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "ap-northeast-1", Type: "io2", PriceGB: 0.142, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "ap-northeast-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "ap-northeast-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "ap-northeast-2": { - "gp2": {Region: "ap-northeast-2", Type: "gp2", PriceGB: 0.114, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "ap-northeast-2", Type: "gp3", PriceGB: 0.0912, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "ap-northeast-2", Type: "io1", PriceGB: 0.1278, PriceIOPS: 0.0666000000, IOPSConfigurable: true}, - "io2": {Region: "ap-northeast-2", Type: "io2", PriceGB: 0.1278, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "ap-northeast-2", Type: "sc1", PriceGB: 0.0174, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "ap-northeast-2", Type: "st1", PriceGB: 0.051, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "ap-northeast-2", Type: "gp2", PriceGB: 0.114, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "ap-northeast-2", Type: "gp3", PriceGB: 0.0912, PriceIOPS: 0.0057000000, PriceThroughput: 0.046694400000000004, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "ap-northeast-2", Type: "io1", PriceGB: 0.1278, PriceIOPS: 0.0666000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "ap-northeast-2", Type: "io2", PriceGB: 0.1278, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "ap-northeast-2", Type: "sc1", PriceGB: 0.0174, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "ap-northeast-2", Type: "st1", PriceGB: 0.051, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "ap-northeast-3": { - "gp2": {Region: "ap-northeast-3", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "ap-northeast-3", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "ap-northeast-3", Type: "io1", PriceGB: 0.142, PriceIOPS: 0.0740000000, IOPSConfigurable: true}, - "sc1": {Region: "ap-northeast-3", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "ap-northeast-3", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "ap-northeast-3", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "ap-northeast-3", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0.0060000000, PriceThroughput: 0.049152, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "ap-northeast-3", Type: "io1", PriceGB: 0.142, PriceIOPS: 0.0740000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "sc1": {Region: "ap-northeast-3", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "ap-northeast-3", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "ap-south-1": { - "gp2": {Region: "ap-south-1", Type: "gp2", PriceGB: 0.114, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "ap-south-1", Type: "gp3", PriceGB: 0.0912, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "ap-south-1", Type: "io1", PriceGB: 0.131, PriceIOPS: 0.0680000000, IOPSConfigurable: true}, - "io2": {Region: "ap-south-1", Type: "io2", PriceGB: 0.131, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "ap-south-1", Type: "sc1", PriceGB: 0.0174, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "ap-south-1", Type: "st1", PriceGB: 0.051, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "ap-south-1", Type: "gp2", PriceGB: 0.114, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "ap-south-1", Type: "gp3", PriceGB: 0.0912, PriceIOPS: 0.0057000000, PriceThroughput: 0.046694400000000004, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "ap-south-1", Type: "io1", PriceGB: 0.131, PriceIOPS: 0.0680000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "ap-south-1", Type: "io2", PriceGB: 0.131, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "ap-south-1", Type: "sc1", PriceGB: 0.0174, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "ap-south-1", Type: "st1", PriceGB: 0.051, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "ap-southeast-1": { - "gp2": {Region: "ap-southeast-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "ap-southeast-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "ap-southeast-1", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, IOPSConfigurable: true}, - "io2": {Region: "ap-southeast-1", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "ap-southeast-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "ap-southeast-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "ap-southeast-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "ap-southeast-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0.0060000000, PriceThroughput: 0.049152, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "ap-southeast-1", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "ap-southeast-1", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "ap-southeast-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "ap-southeast-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "ap-southeast-2": { - "gp2": {Region: "ap-southeast-2", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "ap-southeast-2", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "ap-southeast-2", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, IOPSConfigurable: true}, - "io2": {Region: "ap-southeast-2", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "ap-southeast-2", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "ap-southeast-2", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "ap-southeast-2", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "ap-southeast-2", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0.0060000000, PriceThroughput: 0.049152, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "ap-southeast-2", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "ap-southeast-2", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "ap-southeast-2", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "ap-southeast-2", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "ca-central-1": { - "gp2": {Region: "ca-central-1", Type: "gp2", PriceGB: 0.11, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "ca-central-1", Type: "gp3", PriceGB: 0.088, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "ca-central-1", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, IOPSConfigurable: true}, - "io2": {Region: "ca-central-1", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "ca-central-1", Type: "sc1", PriceGB: 0.0168, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "ca-central-1", Type: "st1", PriceGB: 0.05, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "ca-central-1", Type: "gp2", PriceGB: 0.11, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "ca-central-1", Type: "gp3", PriceGB: 0.088, PriceIOPS: 0.0055000000, PriceThroughput: 0.045056, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "ca-central-1", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "ca-central-1", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "ca-central-1", Type: "sc1", PriceGB: 0.0168, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "ca-central-1", Type: "st1", PriceGB: 0.05, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "eu-central-1": { - "gp2": {Region: "eu-central-1", Type: "gp2", PriceGB: 0.119, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "eu-central-1", Type: "gp3", PriceGB: 0.0952, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "eu-central-1", Type: "io1", PriceGB: 0.149, PriceIOPS: 0.0780000000, IOPSConfigurable: true}, - "io2": {Region: "eu-central-1", Type: "io2", PriceGB: 0.149, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "eu-central-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "eu-central-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "eu-central-1", Type: "gp2", PriceGB: 0.119, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "eu-central-1", Type: "gp3", PriceGB: 0.0952, PriceIOPS: 0.0060000000, PriceThroughput: 0.048742400000000005, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "eu-central-1", Type: "io1", PriceGB: 0.149, PriceIOPS: 0.0780000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "eu-central-1", Type: "io2", PriceGB: 0.149, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "eu-central-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "eu-central-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "eu-north-1": { - "gp2": {Region: "eu-north-1", Type: "gp2", PriceGB: 0.1045, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "eu-north-1", Type: "gp3", PriceGB: 0.0836, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "eu-north-1", Type: "io1", PriceGB: 0.1311, PriceIOPS: 0.0684000000, IOPSConfigurable: true}, - "io2": {Region: "eu-north-1", Type: "io2", PriceGB: 0.1311, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "eu-north-1", Type: "sc1", PriceGB: 0.01596, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "eu-north-1", Type: "st1", PriceGB: 0.0475, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "eu-north-1", Type: "gp2", PriceGB: 0.1045, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "eu-north-1", Type: "gp3", PriceGB: 0.0836, PriceIOPS: 0.0052000000, PriceThroughput: 0.0428032, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "eu-north-1", Type: "io1", PriceGB: 0.1311, PriceIOPS: 0.0684000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "eu-north-1", Type: "io2", PriceGB: 0.1311, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "eu-north-1", Type: "sc1", PriceGB: 0.01596, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "eu-north-1", Type: "st1", PriceGB: 0.0475, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "eu-south-1": { - "gp2": {Region: "eu-south-1", Type: "gp2", PriceGB: 0.1155, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "eu-south-1", Type: "gp3", PriceGB: 0.0924, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "eu-south-1", Type: "io1", PriceGB: 0.1449, PriceIOPS: 0.0756000000, IOPSConfigurable: true}, - "sc1": {Region: "eu-south-1", Type: "sc1", PriceGB: 0.01764, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "eu-south-1", Type: "st1", PriceGB: 0.0525, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "eu-south-1", Type: "gp2", PriceGB: 0.1155, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "eu-south-1", Type: "gp3", PriceGB: 0.0924, PriceIOPS: 0.0058000000, PriceThroughput: 0.0473088, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "eu-south-1", Type: "io1", PriceGB: 0.1449, PriceIOPS: 0.0756000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "sc1": {Region: "eu-south-1", Type: "sc1", PriceGB: 0.01764, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "eu-south-1", Type: "st1", PriceGB: 0.0525, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "eu-west-1": { - "gp2": {Region: "eu-west-1", Type: "gp2", PriceGB: 0.11, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "eu-west-1", Type: "gp3", PriceGB: 0.088, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "eu-west-1", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, IOPSConfigurable: true}, - "io2": {Region: "eu-west-1", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "eu-west-1", Type: "sc1", PriceGB: 0.0168, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "eu-west-1", Type: "st1", PriceGB: 0.05, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "eu-west-1", Type: "gp2", PriceGB: 0.11, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "eu-west-1", Type: "gp3", PriceGB: 0.088, PriceIOPS: 0.0055000000, PriceThroughput: 0.045056, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "eu-west-1", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "eu-west-1", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "eu-west-1", Type: "sc1", PriceGB: 0.0168, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "eu-west-1", Type: "st1", PriceGB: 0.05, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "eu-west-2": { - "gp2": {Region: "eu-west-2", Type: "gp2", PriceGB: 0.116, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "eu-west-2", Type: "gp3", PriceGB: 0.0928, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "eu-west-2", Type: "io1", PriceGB: 0.145, PriceIOPS: 0.0760000000, IOPSConfigurable: true}, - "io2": {Region: "eu-west-2", Type: "io2", PriceGB: 0.145, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "eu-west-2", Type: "sc1", PriceGB: 0.0174, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "eu-west-2", Type: "st1", PriceGB: 0.053, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "eu-west-2", Type: "gp2", PriceGB: 0.116, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "eu-west-2", Type: "gp3", PriceGB: 0.0928, PriceIOPS: 0.0058000000, PriceThroughput: 0.047513599999999996, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "eu-west-2", Type: "io1", PriceGB: 0.145, PriceIOPS: 0.0760000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "eu-west-2", Type: "io2", PriceGB: 0.145, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "eu-west-2", Type: "sc1", PriceGB: 0.0174, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "eu-west-2", Type: "st1", PriceGB: 0.053, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "eu-west-3": { - "gp2": {Region: "eu-west-3", Type: "gp2", PriceGB: 0.116, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "eu-west-3", Type: "gp3", PriceGB: 0.0928, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "eu-west-3", Type: "io1", PriceGB: 0.145, PriceIOPS: 0.0760000000, IOPSConfigurable: true}, - "sc1": {Region: "eu-west-3", Type: "sc1", PriceGB: 0.0174, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "eu-west-3", Type: "st1", PriceGB: 0.053, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "eu-west-3", Type: "gp2", PriceGB: 0.116, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "eu-west-3", Type: "gp3", PriceGB: 0.0928, PriceIOPS: 0.0058000000, PriceThroughput: 0.047513599999999996, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "eu-west-3", Type: "io1", PriceGB: 0.145, PriceIOPS: 0.0760000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "sc1": {Region: "eu-west-3", Type: "sc1", PriceGB: 0.0174, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "eu-west-3", Type: "st1", PriceGB: 0.053, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "me-south-1": { - "gp2": {Region: "me-south-1", Type: "gp2", PriceGB: 0.121, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "me-south-1", Type: "gp3", PriceGB: 0.0968, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "me-south-1", Type: "io1", PriceGB: 0.1518, PriceIOPS: 0.0792000000, IOPSConfigurable: true}, - "io2": {Region: "me-south-1", Type: "io2", PriceGB: 0.1518, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "me-south-1", Type: "sc1", PriceGB: 0.01848, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "me-south-1", Type: "st1", PriceGB: 0.055, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "me-south-1", Type: "gp2", PriceGB: 0.121, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "me-south-1", Type: "gp3", PriceGB: 0.0968, PriceIOPS: 0.0061000000, PriceThroughput: 0.0495616, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "me-south-1", Type: "io1", PriceGB: 0.1518, PriceIOPS: 0.0792000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "me-south-1", Type: "io2", PriceGB: 0.1518, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "me-south-1", Type: "sc1", PriceGB: 0.01848, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "me-south-1", Type: "st1", PriceGB: 0.055, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "sa-east-1": { - "gp2": {Region: "sa-east-1", Type: "gp2", PriceGB: 0.19, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "sa-east-1", Type: "gp3", PriceGB: 0.152, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "sa-east-1", Type: "io1", PriceGB: 0.238, PriceIOPS: 0.0910000000, IOPSConfigurable: true}, - "sc1": {Region: "sa-east-1", Type: "sc1", PriceGB: 0.0288, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "sa-east-1", Type: "st1", PriceGB: 0.086, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "sa-east-1", Type: "gp2", PriceGB: 0.19, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "sa-east-1", Type: "gp3", PriceGB: 0.152, PriceIOPS: 0.0095000000, PriceThroughput: 0.077824, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "sa-east-1", Type: "io1", PriceGB: 0.238, PriceIOPS: 0.0910000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "sc1": {Region: "sa-east-1", Type: "sc1", PriceGB: 0.0288, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "sa-east-1", Type: "st1", PriceGB: 0.086, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "us-east-1": { - "gp2": {Region: "us-east-1", Type: "gp2", PriceGB: 0.1, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "us-east-1", Type: "gp3", PriceGB: 0.08, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "us-east-1", Type: "io1", PriceGB: 0.125, PriceIOPS: 0.0650000000, IOPSConfigurable: true}, - "io2": {Region: "us-east-1", Type: "io2", PriceGB: 0.125, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "us-east-1", Type: "sc1", PriceGB: 0.015, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "us-east-1", Type: "st1", PriceGB: 0.045, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "us-east-1", Type: "gp2", PriceGB: 0.1, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "us-east-1", Type: "gp3", PriceGB: 0.08, PriceIOPS: 0.0050000000, PriceThroughput: 0.04096, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "us-east-1", Type: "io1", PriceGB: 0.125, PriceIOPS: 0.0650000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "us-east-1", Type: "io2", PriceGB: 0.125, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "us-east-1", Type: "sc1", PriceGB: 0.015, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "us-east-1", Type: "st1", PriceGB: 0.045, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "us-east-2": { - "gp2": {Region: "us-east-2", Type: "gp2", PriceGB: 0.1, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "us-east-2", Type: "gp3", PriceGB: 0.08, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "us-east-2", Type: "io1", PriceGB: 0.125, PriceIOPS: 0.0650000000, IOPSConfigurable: true}, - "io2": {Region: "us-east-2", Type: "io2", PriceGB: 0.125, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "us-east-2", Type: "sc1", PriceGB: 0.015, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "us-east-2", Type: "st1", PriceGB: 0.045, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "us-east-2", Type: "gp2", PriceGB: 0.1, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "us-east-2", Type: "gp3", PriceGB: 0.08, PriceIOPS: 0.0050000000, PriceThroughput: 0.04096, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "us-east-2", Type: "io1", PriceGB: 0.125, PriceIOPS: 0.0650000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "us-east-2", Type: "io2", PriceGB: 0.125, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "us-east-2", Type: "sc1", PriceGB: 0.015, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "us-east-2", Type: "st1", PriceGB: 0.045, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "us-gov-east-1": { - "gp2": {Region: "us-gov-east-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "us-gov-east-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "us-gov-east-1", Type: "io1", PriceGB: 0.15, PriceIOPS: 0.0780000000, IOPSConfigurable: true}, - "sc1": {Region: "us-gov-east-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "us-gov-east-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "us-gov-east-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "us-gov-east-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0.0060000000, PriceThroughput: 0.049152, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "us-gov-east-1", Type: "io1", PriceGB: 0.15, PriceIOPS: 0.0780000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "sc1": {Region: "us-gov-east-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "us-gov-east-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "us-gov-west-1": { - "gp2": {Region: "us-gov-west-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "us-gov-west-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "us-gov-west-1", Type: "io1", PriceGB: 0.15, PriceIOPS: 0.0780000000, IOPSConfigurable: true}, - "sc1": {Region: "us-gov-west-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "us-gov-west-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "us-gov-west-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "us-gov-west-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0.0060000000, PriceThroughput: 0.049152, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "us-gov-west-1", Type: "io1", PriceGB: 0.15, PriceIOPS: 0.0780000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "sc1": {Region: "us-gov-west-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "us-gov-west-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "us-west-1": { - "gp2": {Region: "us-west-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "us-west-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "us-west-1", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, IOPSConfigurable: true}, - "io2": {Region: "us-west-1", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "us-west-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "us-west-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "us-west-1", Type: "gp2", PriceGB: 0.12, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "us-west-1", Type: "gp3", PriceGB: 0.096, PriceIOPS: 0.0060000000, PriceThroughput: 0.049152, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "us-west-1", Type: "io1", PriceGB: 0.138, PriceIOPS: 0.0720000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "us-west-1", Type: "io2", PriceGB: 0.138, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "us-west-1", Type: "sc1", PriceGB: 0.018, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "us-west-1", Type: "st1", PriceGB: 0.054, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, "us-west-2": { - "gp2": {Region: "us-west-2", Type: "gp2", PriceGB: 0.1, PriceIOPS: 0, IOPSConfigurable: false}, - "gp3": {Region: "us-west-2", Type: "gp3", PriceGB: 0.08, PriceIOPS: 0, IOPSConfigurable: false}, - "io1": {Region: "us-west-2", Type: "io1", PriceGB: 0.125, PriceIOPS: 0.0650000000, IOPSConfigurable: true}, - "io2": {Region: "us-west-2", Type: "io2", PriceGB: 0.125, PriceIOPS: 0, IOPSConfigurable: false}, - "sc1": {Region: "us-west-2", Type: "sc1", PriceGB: 0.015, PriceIOPS: 0, IOPSConfigurable: false}, - "st1": {Region: "us-west-2", Type: "st1", PriceGB: 0.045, PriceIOPS: 0, IOPSConfigurable: false}, + "gp2": {Region: "us-west-2", Type: "gp2", PriceGB: 0.1, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "gp3": {Region: "us-west-2", Type: "gp3", PriceGB: 0.08, PriceIOPS: 0.0050000000, PriceThroughput: 0.04096, IOPSConfigurable: true, ThroughputConfigurable: true}, + "io1": {Region: "us-west-2", Type: "io1", PriceGB: 0.125, PriceIOPS: 0.0650000000, PriceThroughput: 0, IOPSConfigurable: true, ThroughputConfigurable: false}, + "io2": {Region: "us-west-2", Type: "io2", PriceGB: 0.125, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "sc1": {Region: "us-west-2", Type: "sc1", PriceGB: 0.015, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, + "st1": {Region: "us-west-2", Type: "st1", PriceGB: 0.045, PriceIOPS: 0, PriceThroughput: 0, IOPSConfigurable: false, ThroughputConfigurable: false}, }, } diff --git a/pkg/operator/operator/cron.go b/pkg/operator/operator/cron.go index 4344cca5b7..223b8f409e 100644 --- a/pkg/operator/operator/cron.go +++ b/pkg/operator/operator/cron.go @@ -22,6 +22,7 @@ import ( "github.com/cortexlabs/cortex/pkg/lib/aws" "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/k8s" + libmath "github.com/cortexlabs/cortex/pkg/lib/math" "github.com/cortexlabs/cortex/pkg/lib/sets/strset" "github.com/cortexlabs/cortex/pkg/lib/telemetry" "github.com/cortexlabs/cortex/pkg/operator/config" @@ -184,9 +185,13 @@ func getEBSPriceForNodeGroupInstance(ngs []*clusterconfig.NodeGroup, ngName stri } if ng.Name == ngNamePrefix+ngName { ebsPrice = aws.EBSMetadatas[config.CoreConfig.Region][ng.InstanceVolumeType.String()].PriceGB * float64(ng.InstanceVolumeSize) / 30 / 24 - if ng.InstanceVolumeType.String() == "io1" && ng.InstanceVolumeIOPS != nil { + if ng.InstanceVolumeType == clusterconfig.IO1VolumeType && ng.InstanceVolumeIOPS != nil { ebsPrice += aws.EBSMetadatas[config.CoreConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS * float64(*ng.InstanceVolumeIOPS) / 30 / 24 } + if ng.InstanceVolumeType == clusterconfig.GP3VolumeType && ng.InstanceVolumeIOPS != nil && ng.InstanceVolumeThroughput != nil { + ebsPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[config.CoreConfig.Region][ng.InstanceVolumeType.String()].PriceIOPS-3000)*float64(*ng.InstanceVolumeIOPS)/30/24) + ebsPrice += libmath.MaxFloat64(0, (aws.EBSMetadatas[config.CoreConfig.Region][ng.InstanceVolumeType.String()].PriceThroughput-125)*float64(*ng.InstanceVolumeThroughput)/30/24) + } break } } @@ -196,8 +201,8 @@ func getEBSPriceForNodeGroupInstance(ngs []*clusterconfig.NodeGroup, ngName stri func clusterFixedPrice() float64 { eksPrice := aws.EKSPrices[config.CoreConfig.Region] operatorInstancePrice := aws.InstanceMetadatas[config.CoreConfig.Region]["t3.medium"].Price - operatorEBSPrice := aws.EBSMetadatas[config.CoreConfig.Region]["gp2"].PriceGB * 20 / 30 / 24 - metricsEBSPrice := aws.EBSMetadatas[config.CoreConfig.Region]["gp2"].PriceGB * 40 / 30 / 24 + operatorEBSPrice := aws.EBSMetadatas[config.CoreConfig.Region]["gp3"].PriceGB * 20 / 30 / 24 + metricsEBSPrice := aws.EBSMetadatas[config.CoreConfig.Region]["gp2"].PriceGB * (40 + 2) / 30 / 24 nlbPrice := aws.NLBMetadatas[config.CoreConfig.Region].Price natUnitPrice := aws.NATMetadatas[config.CoreConfig.Region].Price var natTotalPrice float64 @@ -208,7 +213,7 @@ func clusterFixedPrice() float64 { natTotalPrice = natUnitPrice * float64(len(config.ManagedConfig.AvailabilityZones)) } - return eksPrice + 2*operatorInstancePrice + operatorEBSPrice + metricsEBSPrice + 2*nlbPrice + natTotalPrice + return eksPrice + 2*(operatorInstancePrice+operatorEBSPrice) + metricsEBSPrice + 2*nlbPrice + natTotalPrice } func ErrorHandler(cronName string) func(error) { diff --git a/pkg/types/clusterconfig/cluster_config.go b/pkg/types/clusterconfig/cluster_config.go index 4629353393..5bcf323a41 100644 --- a/pkg/types/clusterconfig/cluster_config.go +++ b/pkg/types/clusterconfig/cluster_config.go @@ -60,6 +60,16 @@ var ( _cachedCNISupportedInstances *string _defaultIAMPolicies = []string{"arn:aws:iam::aws:policy/AmazonS3FullAccess"} _invalidTagPrefixes = []string{"kubernetes.io/", "k8s.io/", "eksctl.", "alpha.eksctl.", "beta.eksctl.", "aws:", "Aws:", "aWs:", "awS:", "aWS:", "AwS:", "aWS:", "AWS:"} + + _smallestIOPSForIO1VolumeType = int64(100) + _highestIOPSForIO1VolumeType = int64(64000) + _smallestIOPSForGP3VolumeType = int64(3000) + _highestIOPSForGP3VolumeType = int64(16000) + + _maxIOPSToVolumeSizeRatioForIO1 = int64(50) + _maxIOPSToVolumeSizeRatioForGP3 = int64(500) + _minIOPSToThroughputRatioForGP3 = int64(4) + // This regex is stricter than the actual S3 rules _strictS3BucketRegex = regexp.MustCompile(`^([a-z0-9])+(-[a-z0-9]+)*$`) ) @@ -115,15 +125,16 @@ type ManagedConfig struct { } type NodeGroup struct { - Name string `json:"name" yaml:"name"` - InstanceType string `json:"instance_type" yaml:"instance_type"` - MinInstances int64 `json:"min_instances" yaml:"min_instances"` - MaxInstances int64 `json:"max_instances" yaml:"max_instances"` - InstanceVolumeSize int64 `json:"instance_volume_size" yaml:"instance_volume_size"` - InstanceVolumeType VolumeType `json:"instance_volume_type" yaml:"instance_volume_type"` - InstanceVolumeIOPS *int64 `json:"instance_volume_iops" yaml:"instance_volume_iops"` - Spot bool `json:"spot" yaml:"spot"` - SpotConfig *SpotConfig `json:"spot_config" yaml:"spot_config"` + Name string `json:"name" yaml:"name"` + InstanceType string `json:"instance_type" yaml:"instance_type"` + MinInstances int64 `json:"min_instances" yaml:"min_instances"` + MaxInstances int64 `json:"max_instances" yaml:"max_instances"` + InstanceVolumeSize int64 `json:"instance_volume_size" yaml:"instance_volume_size"` + InstanceVolumeType VolumeType `json:"instance_volume_type" yaml:"instance_volume_type"` + InstanceVolumeIOPS *int64 `json:"instance_volume_iops" yaml:"instance_volume_iops"` + InstanceVolumeThroughput *int64 `json:"instance_volume_throughput" yaml:"instance_volume_throughput"` + Spot bool `json:"spot" yaml:"spot"` + SpotConfig *SpotConfig `json:"spot_config" yaml:"spot_config"` } type SpotConfig struct { @@ -478,7 +489,7 @@ var ManagedConfigStructFieldValidations = []*cr.StructFieldValidation{ StructField: "InstanceVolumeType", StringValidation: &cr.StringValidation{ AllowedValues: VolumeTypesStrings(), - Default: GP2VolumeType.String(), + Default: GP3VolumeType.String(), }, Parser: func(str string) (interface{}, error) { return VolumeTypeFromString(str), nil @@ -487,8 +498,14 @@ var ManagedConfigStructFieldValidations = []*cr.StructFieldValidation{ { StructField: "InstanceVolumeIOPS", Int64PtrValidation: &cr.Int64PtrValidation{ - GreaterThanOrEqualTo: pointer.Int64(100), - LessThanOrEqualTo: pointer.Int64(64000), + AllowExplicitNull: true, + }, + }, + { + StructField: "InstanceVolumeThroughput", + Int64PtrValidation: &cr.Int64PtrValidation{ + GreaterThanOrEqualTo: pointer.Int64(125), + LessThanOrEqualTo: pointer.Int64(1000), AllowExplicitNull: true, }, }, @@ -932,19 +949,51 @@ func (ng *NodeGroup) validateNodeGroup(awsClient *aws.Client, region string) err return errors.Wrap(ErrorInstanceTypeNotSupportedInRegion(primaryInstanceType, region), InstanceTypeKey) } - // Throw error if IOPS defined for other storage than io1 - if ng.InstanceVolumeType != IO1VolumeType && ng.InstanceVolumeIOPS != nil { + // throw error if IOPS defined for other storage than io1/gp3 + if ng.InstanceVolumeType != IO1VolumeType && ng.InstanceVolumeType != GP3VolumeType && ng.InstanceVolumeIOPS != nil { return ErrorIOPSNotSupported(ng.InstanceVolumeType) } - if ng.InstanceVolumeType == IO1VolumeType && ng.InstanceVolumeIOPS != nil { - if *ng.InstanceVolumeIOPS > ng.InstanceVolumeSize*50 { - return ErrorIOPSTooLarge(*ng.InstanceVolumeIOPS, ng.InstanceVolumeSize) - } + // throw error if throughput defined for other storage than gp3 + if ng.InstanceVolumeType != GP3VolumeType && ng.InstanceVolumeThroughput != nil { + return ErrorThroughputNotSupported(ng.InstanceVolumeType) } - if aws.EBSMetadatas[region][ng.InstanceVolumeType.String()].IOPSConfigurable && ng.InstanceVolumeIOPS == nil { - ng.InstanceVolumeIOPS = pointer.Int64(libmath.MinInt64(ng.InstanceVolumeSize*50, 3000)) + if ng.InstanceVolumeType == GP3VolumeType && ((ng.InstanceVolumeIOPS != nil && ng.InstanceVolumeThroughput == nil) || (ng.InstanceVolumeIOPS == nil && ng.InstanceVolumeThroughput != nil)) { + return ErrorSpecifyTwoOrNone(InstanceVolumeIOPSKey, InstanceVolumeThroughputKey) + } + + if ng.InstanceVolumeIOPS != nil { + if ng.InstanceVolumeType == IO1VolumeType { + if *ng.InstanceVolumeIOPS < _smallestIOPSForIO1VolumeType { + return ErrorIOPSTooSmall(ng.InstanceVolumeType, *ng.InstanceVolumeIOPS, _smallestIOPSForIO1VolumeType) + } + if *ng.InstanceVolumeIOPS > _highestIOPSForIO1VolumeType { + return ErrorIOPSTooLarge(ng.InstanceVolumeType, *ng.InstanceVolumeIOPS, _highestIOPSForIO1VolumeType) + } + if *ng.InstanceVolumeIOPS > ng.InstanceVolumeSize*_maxIOPSToVolumeSizeRatioForIO1 { + return ErrorIOPSToVolumeSizeRatio(ng.InstanceVolumeType, _maxIOPSToVolumeSizeRatioForIO1, *ng.InstanceVolumeIOPS, ng.InstanceVolumeSize) + } + } else { + if *ng.InstanceVolumeIOPS < _smallestIOPSForGP3VolumeType { + return ErrorIOPSTooSmall(ng.InstanceVolumeType, *ng.InstanceVolumeIOPS, _smallestIOPSForGP3VolumeType) + } + if *ng.InstanceVolumeIOPS > _highestIOPSForGP3VolumeType { + return ErrorIOPSTooLarge(ng.InstanceVolumeType, *ng.InstanceVolumeIOPS, _highestIOPSForGP3VolumeType) + } + if *ng.InstanceVolumeIOPS > ng.InstanceVolumeSize*_maxIOPSToVolumeSizeRatioForGP3 { + return ErrorIOPSToVolumeSizeRatio(ng.InstanceVolumeType, _maxIOPSToVolumeSizeRatioForGP3, *ng.InstanceVolumeIOPS, ng.InstanceVolumeSize) + } + iopsToThroughputRatio := float64(*ng.InstanceVolumeIOPS) / float64(*ng.InstanceVolumeThroughput) + if iopsToThroughputRatio < float64(_minIOPSToThroughputRatioForGP3) { + return ErrorIOPSToThroughputRatio(ng.InstanceVolumeType, _minIOPSToThroughputRatioForGP3, *ng.InstanceVolumeIOPS, *ng.InstanceVolumeThroughput) + } + } + } else if ng.InstanceVolumeType == GP3VolumeType { + ng.InstanceVolumeIOPS = pointer.Int64(3000) + ng.InstanceVolumeThroughput = pointer.Int64(125) + } else if ng.InstanceVolumeType == IO1VolumeType { + ng.InstanceVolumeIOPS = pointer.Int64(libmath.MinInt64(ng.InstanceVolumeSize*_maxIOPSToVolumeSizeRatioForIO1, 3000)) } if ng.Spot { @@ -1367,7 +1416,11 @@ func (mc *ManagedConfig) TelemetryEvent() map[string]interface{} { event[nodeGroupKey("instance_volume_type")] = ng.InstanceVolumeType if ng.InstanceVolumeIOPS != nil { event[nodeGroupKey("instance_volume_iops.is_defined")] = true - event[nodeGroupKey("instance_volume_iops")] = ng.InstanceVolumeIOPS + event[nodeGroupKey("instance_volume_iops")] = *ng.InstanceVolumeIOPS + } + if ng.InstanceVolumeThroughput != nil { + event[nodeGroupKey("instance_volume_throughput.is_defined")] = true + event[nodeGroupKey("instance_volume_throughput")] = *ng.InstanceVolumeThroughput } event[nodeGroupKey("spot")] = ng.Spot diff --git a/pkg/types/clusterconfig/config_key.go b/pkg/types/clusterconfig/config_key.go index 345fd44315..220251adfe 100644 --- a/pkg/types/clusterconfig/config_key.go +++ b/pkg/types/clusterconfig/config_key.go @@ -32,6 +32,7 @@ const ( InstanceVolumeSizeKey = "instance_volume_size" InstanceVolumeTypeKey = "instance_volume_type" InstanceVolumeIOPSKey = "instance_volume_iops" + InstanceVolumeThroughputKey = "instance_volume_throughput" InstancePoolsKey = "instance_pools" MaxPriceKey = "max_price" NetworkKey = "network" diff --git a/pkg/types/clusterconfig/errors.go b/pkg/types/clusterconfig/errors.go index 414e6fd81b..dc22215fbd 100644 --- a/pkg/types/clusterconfig/errors.go +++ b/pkg/types/clusterconfig/errors.go @@ -56,6 +56,7 @@ const ( ErrNotEnoughValidDefaultAvailibilityZones = "clusterconfig.not_enough_valid_default_availability_zones" ErrNoNATGatewayWithSubnets = "clusterconfig.no_nat_gateway_with_subnets" ErrSpecifyOneOrNone = "clusterconfig.specify_one_or_none" + ErrSpecifyTwoOrNone = "clusterconfig.specify_two_or_none" ErrDependentFieldMustBeSpecified = "clusterconfig.dependent_field_must_be_specified" ErrFieldConfigurationDependentOnCondition = "clusterconfig.field_configuration_dependent_on_condition" ErrDidNotMatchStrictS3Regex = "clusterconfig.did_not_match_strict_s3_regex" @@ -63,7 +64,11 @@ const ( ErrS3RegionDiffersFromCluster = "clusterconfig.s3_region_differs_from_cluster" ErrInvalidInstanceType = "clusterconfig.invalid_instance_type" ErrIOPSNotSupported = "clusterconfig.iops_not_supported" + ErrThroughputNotSupported = "clusterconfig.throughput_not_supported" + ErrIOPSTooSmall = "clusterconfig.iops_too_small" ErrIOPSTooLarge = "clusterconfig.iops_too_large" + ErrIOPSToVolumeSizeRatio = "clusterconfig.iops_to_volume_size_ratio" + ErrIOPSToThroughputRatio = "clusterconfig.iops_to_throughput_ratio" ErrCantOverrideDefaultTag = "clusterconfig.cant_override_default_tag" ErrSSLCertificateARNNotFound = "clusterconfig.ssl_certificate_arn_not_found" ErrIAMPolicyARNNotFound = "clusterconfig.iam_policy_arn_not_found" @@ -277,6 +282,14 @@ func ErrorSpecifyOneOrNone(fieldName1 string, fieldName2 string, fieldNames ...s }) } +func ErrorSpecifyTwoOrNone(fieldName1 string, fieldName2 string, fieldNames ...string) error { + fieldNames = append([]string{fieldName1, fieldName2}, fieldNames...) + return errors.WithStack(&errors.Error{ + Kind: ErrSpecifyTwoOrNone, + Message: fmt.Sprintf("specify exactly two or none of the following fields: %s", s.StrsAnd(fieldNames)), + }) +} + func ErrorDependentFieldMustBeSpecified(configuredField string, dependencyField string) error { return errors.WithStack(&errors.Error{ Kind: ErrDependentFieldMustBeSpecified, @@ -322,14 +335,42 @@ func ErrorInvalidInstanceType(instanceType string) error { func ErrorIOPSNotSupported(volumeType VolumeType) error { return errors.WithStack(&errors.Error{ Kind: ErrIOPSNotSupported, - Message: fmt.Sprintf("IOPS cannot be configured for volume type %s; set `%s: %s` or remove `%s` from your cluster configuration file", volumeType, InstanceVolumeTypeKey, IO1VolumeType, InstanceVolumeIOPSKey), + Message: fmt.Sprintf("IOPS cannot be configured for volume type %s; set `%s: %s`, `%s: %s`, or remove `%s` from your cluster configuration file", volumeType, InstanceVolumeTypeKey, IO1VolumeType, InstanceVolumeTypeKey, GP3VolumeType, InstanceVolumeIOPSKey), }) } -func ErrorIOPSTooLarge(iops int64, volumeSize int64) error { +func ErrorThroughputNotSupported(volumeType VolumeType) error { + return errors.WithStack(&errors.Error{ + Kind: ErrThroughputNotSupported, + Message: fmt.Sprintf("throughput cannot be configured for volume type %s; set `%s: %s` or remove `%s` from your cluster configuration file", volumeType, InstanceVolumeTypeKey, GP3VolumeType, InstanceVolumeThroughputKey), + }) +} + +func ErrorIOPSTooSmall(volumeType VolumeType, iops, minIOPS int64) error { + return errors.WithStack(&errors.Error{ + Kind: ErrIOPSTooSmall, + Message: fmt.Sprintf("for %s volume type, %s (%d) cannot be smaller than %d", volumeType, InstanceVolumeIOPSKey, iops, minIOPS), + }) +} + +func ErrorIOPSTooLarge(volumeType VolumeType, iops, maxIOPS int64) error { return errors.WithStack(&errors.Error{ Kind: ErrIOPSTooLarge, - Message: fmt.Sprintf("%s (%d) cannot be more than 50 times larger than %s (%d); increase `%s` or decrease `%s` in your cluster configuration file", InstanceVolumeIOPSKey, iops, InstanceVolumeSizeKey, volumeSize, InstanceVolumeSizeKey, InstanceVolumeIOPSKey), + Message: fmt.Sprintf("for %s volume type, %s (%d) cannot be larger than %d", volumeType, InstanceVolumeIOPSKey, iops, maxIOPS), + }) +} + +func ErrorIOPSToVolumeSizeRatio(volumeType VolumeType, ratio, iops int64, volumeSize int64) error { + return errors.WithStack(&errors.Error{ + Kind: ErrIOPSToVolumeSizeRatio, + Message: fmt.Sprintf("for %s volume type, %s (%d) cannot be more than %d times larger than %s (%d); increase `%s` or decrease `%s` in your cluster configuration file", volumeType, InstanceVolumeIOPSKey, iops, ratio, InstanceVolumeSizeKey, volumeSize, InstanceVolumeSizeKey, InstanceVolumeIOPSKey), + }) +} + +func ErrorIOPSToThroughputRatio(volumeType VolumeType, ratio, iops, throughput int64) error { + return errors.WithStack(&errors.Error{ + Kind: ErrIOPSToThroughputRatio, + Message: fmt.Sprintf("for %s volume type, %s (%d) must be at least %d times larger than %s (%d); decrease `%s` or increase `%s` in your cluster configuration file", volumeType, InstanceVolumeIOPSKey, iops, ratio, InstanceVolumeThroughputKey, throughput, InstanceVolumeThroughputKey, InstanceVolumeIOPSKey), }) } diff --git a/pkg/types/clusterconfig/volume_types.go b/pkg/types/clusterconfig/volume_types.go index 74bc83c6d7..6c36d06733 100644 --- a/pkg/types/clusterconfig/volume_types.go +++ b/pkg/types/clusterconfig/volume_types.go @@ -21,6 +21,7 @@ type VolumeType int const ( UnknownVolumeType VolumeType = iota GP2VolumeType + GP3VolumeType IO1VolumeType SC1VolumeType ST1VolumeType @@ -29,6 +30,7 @@ const ( var _availableVolumeTypes = []string{ "unknown", "gp2", + "gp3", "io1", "sc1", "st1",