Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion cli/cmd/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ 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
operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp3"].PriceGB * 20 / 30 / 24
metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 40 / 30 / 24
nlbPrice := aws.NLBMetadatas[clusterConfig.Region].Price
natUnitPrice := aws.NATMetadatas[clusterConfig.Region].Price
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/lib_cluster_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ 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
operatorEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp3"].PriceGB * 20 / 30 / 24
metricsEBSPrice := aws.EBSMetadatas[clusterConfig.Region]["gp2"].PriceGB * 40 / 30 / 24
nlbPrice := aws.NLBMetadatas[clusterConfig.Region].Price
natUnitPrice := aws.NATMetadatas[clusterConfig.Region].Price
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: gp2 # 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
9 changes: 7 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,9 @@ def generate_eks(cluster_config_path, ami_json_path):
"minSize": 2,
"maxSize": 2,
"desiredCapacity": 2,
"volumeType": "gp3",
"volumeIOPS": 3000,
"volumeThroughput": 125,
}
operator_nodegroup = merge_override(operator_nodegroup, operator_settings)

Expand Down
5 changes: 5 additions & 0 deletions pkg/lib/aws/gen_resource_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ def get_ebs_metadata(pricing):
metadata["price_iops"] = price
metadata["iops_configurable"] = "true"

elif product["attributes"].get("volumeApiName") == "gp3":
# not correct but will do for now
metadata["price_iops"] = 0
metadata["iops_configurable"] = "true"

# set default values for all other storage types
else:
metadata["price_iops"] = 0
Expand Down
48 changes: 25 additions & 23 deletions pkg/lib/aws/resource_metadata.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/operator/operator/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ 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
operatorEBSPrice := aws.EBSMetadatas[config.CoreConfig.Region]["gp3"].PriceGB * 20 / 30 / 24
metricsEBSPrice := aws.EBSMetadatas[config.CoreConfig.Region]["gp2"].PriceGB * 40 / 30 / 24
nlbPrice := aws.NLBMetadatas[config.CoreConfig.Region].Price
natUnitPrice := aws.NATMetadatas[config.CoreConfig.Region].Price
Expand Down
72 changes: 54 additions & 18 deletions pkg/types/clusterconfig/cluster_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ 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:"}
_highestIOPSForIO1VolumeType = int64(64000)
_highestIOPSForGP3VolumeType = int64(16000)
// This regex is stricter than the actual S3 rules
_strictS3BucketRegex = regexp.MustCompile(`^([a-z0-9])+(-[a-z0-9]+)*$`)
)
Expand Down Expand Up @@ -115,15 +117,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 {
Expand Down Expand Up @@ -478,7 +481,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
Expand All @@ -488,7 +491,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(100),
LessThanOrEqualTo: pointer.Int64(1000),
AllowExplicitNull: true,
},
},
Expand Down Expand Up @@ -935,19 +945,41 @@ 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 ng.InstanceVolumeIOPS != nil {
if ng.InstanceVolumeType == IO1VolumeType {
if *ng.InstanceVolumeIOPS > _highestIOPSForIO1VolumeType {
return ErrorIOPSTooLarge(*ng.InstanceVolumeIOPS, _highestIOPSForIO1VolumeType)
}
if *ng.InstanceVolumeIOPS > ng.InstanceVolumeSize*50 {
return ErrorIOPSTooLargeDueToVolumeSize(*ng.InstanceVolumeIOPS, ng.InstanceVolumeSize)
}
} else {
if *ng.InstanceVolumeIOPS > _highestIOPSForGP3VolumeType {
return ErrorIOPSTooLarge(*ng.InstanceVolumeIOPS, _highestIOPSForGP3VolumeType)
}
}
}

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 = pointer.Int64(3000)
} else {
ng.InstanceVolumeIOPS = pointer.Int64(libmath.MinInt64(ng.InstanceVolumeSize*50, 3000))
}
}

if ng.InstanceVolumeType == GP3VolumeType && ng.InstanceVolumeThroughput == nil {
ng.InstanceVolumeThroughput = pointer.Int64(125)
}

if ng.Spot {
Expand Down Expand Up @@ -1370,7 +1402,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
Expand Down
1 change: 1 addition & 0 deletions pkg/types/clusterconfig/config_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
20 changes: 18 additions & 2 deletions pkg/types/clusterconfig/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ const (
ErrS3RegionDiffersFromCluster = "clusterconfig.s3_region_differs_from_cluster"
ErrInvalidInstanceType = "clusterconfig.invalid_instance_type"
ErrIOPSNotSupported = "clusterconfig.iops_not_supported"
ErrThroughputNotSupported = "clusterconfig.throughput_not_supported"
ErrIOPSTooLarge = "clusterconfig.iops_too_large"
ErrIOPSTooLargeDueToVolumeSize = "clusterconfig.iops_too_large_due_to_volume_siz"
ErrCantOverrideDefaultTag = "clusterconfig.cant_override_default_tag"
ErrSSLCertificateARNNotFound = "clusterconfig.ssl_certificate_arn_not_found"
ErrIAMPolicyARNNotFound = "clusterconfig.iam_policy_arn_not_found"
Expand Down Expand Up @@ -322,13 +324,27 @@ 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 ErrorIOPSTooLarge(iops, maxIOPS int64) error {
return errors.WithStack(&errors.Error{
Kind: ErrIOPSTooLarge,
Message: fmt.Sprintf("%s (%d) cannot be larger than %d", InstanceVolumeIOPSKey, iops, maxIOPS),
})
}

func ErrorIOPSTooLargeDueToVolumeSize(iops int64, volumeSize int64) error {
return errors.WithStack(&errors.Error{
Kind: ErrIOPSTooLargeDueToVolumeSize,
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),
})
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/types/clusterconfig/volume_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type VolumeType int
const (
UnknownVolumeType VolumeType = iota
GP2VolumeType
GP3VolumeType
IO1VolumeType
SC1VolumeType
ST1VolumeType
Expand All @@ -29,6 +30,7 @@ const (
var _availableVolumeTypes = []string{
"unknown",
"gp2",
"gp3",
"io1",
"sc1",
"st1",
Expand Down