Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support GP3 volume type #2130

Merged
merged 16 commits into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions cli/cmd/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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 {
Expand Down
17 changes: 11 additions & 6 deletions cli/cmd/lib_cluster_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions docs/clusters/management/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ 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
instance_type: g4dn.xlarge
min_instances: 1
max_instances: 5
instance_volume_size: 50
instance_volume_type: gp2
# instance_volume_iops: 3000
instance_volume_type: gp3
spot: false
...

Expand Down
10 changes: 8 additions & 2 deletions manager/generate_eks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
42 changes: 41 additions & 1 deletion pkg/lib/aws/gen_resource_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"`
}

Expand Down Expand Up @@ -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}},
"""
)

Expand Down Expand Up @@ -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"],
}
)

Expand Down
Loading