Skip to content

Commit c6da22c

Browse files
authored
feat: Add support for creating efa-only network interfaces (#3196)
1 parent ac14562 commit c6da22c

File tree

10 files changed

+196
-79
lines changed

10 files changed

+196
-79
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/antonbabenko/pre-commit-terraform
3-
rev: v1.96.1
3+
rev: v1.96.2
44
hooks:
55
- id: terraform_fmt
66
- id: terraform_docs

modules/eks-managed-node-group/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,11 @@ module "eks_managed_node_group" {
132132
| <a name="input_disable_api_termination"></a> [disable\_api\_termination](#input\_disable\_api\_termination) | If true, enables EC2 instance termination protection | `bool` | `null` | no |
133133
| <a name="input_disk_size"></a> [disk\_size](#input\_disk\_size) | Disk size in GiB for nodes. Defaults to `20`. Only valid when `use_custom_launch_template` = `false` | `number` | `null` | no |
134134
| <a name="input_ebs_optimized"></a> [ebs\_optimized](#input\_ebs\_optimized) | If true, the launched EC2 instance(s) will be EBS-optimized | `bool` | `null` | no |
135+
| <a name="input_efa_indices"></a> [efa\_indices](#input\_efa\_indices) | The indices of the network interfaces that should be EFA-enabled. Only valid when `enable_efa_support` = `true` | `list(number)` | <pre>[<br/> 0<br/>]</pre> | no |
135136
| <a name="input_elastic_gpu_specifications"></a> [elastic\_gpu\_specifications](#input\_elastic\_gpu\_specifications) | The elastic GPU to attach to the instance | `any` | `{}` | no |
136137
| <a name="input_elastic_inference_accelerator"></a> [elastic\_inference\_accelerator](#input\_elastic\_inference\_accelerator) | Configuration block containing an Elastic Inference Accelerator to attach to the instance | `map(string)` | `{}` | no |
137138
| <a name="input_enable_bootstrap_user_data"></a> [enable\_bootstrap\_user\_data](#input\_enable\_bootstrap\_user\_data) | Determines whether the bootstrap configurations are populated within the user data template. Only valid when using a custom AMI via `ami_id` | `bool` | `false` | no |
139+
| <a name="input_enable_efa_only"></a> [enable\_efa\_only](#input\_enable\_efa\_only) | Determines whether to enable EFA (`false`, default) or EFA and EFA-only (`true`) network interfaces. Note: requires vpc-cni version `v1.18.4` or later | `bool` | `false` | no |
138140
| <a name="input_enable_efa_support"></a> [enable\_efa\_support](#input\_enable\_efa\_support) | Determines whether to enable Elastic Fabric Adapter (EFA) support | `bool` | `false` | no |
139141
| <a name="input_enable_monitoring"></a> [enable\_monitoring](#input\_enable\_monitoring) | Enables/disables detailed monitoring | `bool` | `true` | no |
140142
| <a name="input_enclave_options"></a> [enclave\_options](#input\_enclave\_options) | Enable Nitro Enclaves on launched instances | `map(string)` | `{}` | no |

modules/eks-managed-node-group/main.tf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ locals {
4444
efa_instance_type = try(element(var.instance_types, 0), "")
4545
num_network_cards = try(data.aws_ec2_instance_type.this[0].maximum_network_cards, 0)
4646

47+
# Primary network interface must be EFA, remaining can be EFA or EFA-only
4748
efa_network_interfaces = [
4849
for i in range(local.num_network_cards) : {
4950
associate_public_ip_address = false
5051
delete_on_termination = true
5152
device_index = i == 0 ? 0 : 1
5253
network_card_index = i
53-
interface_type = "efa"
54+
interface_type = var.enable_efa_only ? contains(concat([0], var.efa_indices), i) ? "efa" : "efa-only" : "efa"
5455
}
5556
]
5657

modules/eks-managed-node-group/variables.tf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,19 @@ variable "enable_efa_support" {
285285
default = false
286286
}
287287

288+
# TODO - make this true by default at next breaking change (remove variable, only pass indices)
289+
variable "enable_efa_only" {
290+
description = "Determines whether to enable EFA (`false`, default) or EFA and EFA-only (`true`) network interfaces. Note: requires vpc-cni version `v1.18.4` or later"
291+
type = bool
292+
default = false
293+
}
294+
295+
variable "efa_indices" {
296+
description = "The indices of the network interfaces that should be EFA-enabled. Only valid when `enable_efa_support` = `true`"
297+
type = list(number)
298+
default = [0]
299+
}
300+
288301
variable "network_interfaces" {
289302
description = "Customize network interfaces to be attached at instance boot time"
290303
type = list(any)

modules/self-managed-node-group/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,10 @@ module "self_managed_node_group" {
120120
| <a name="input_desired_size_type"></a> [desired\_size\_type](#input\_desired\_size\_type) | The unit of measurement for the value specified for `desired_size`. Supported for attribute-based instance type selection only. Valid values: `units`, `vcpu`, `memory-mib` | `string` | `null` | no |
121121
| <a name="input_disable_api_termination"></a> [disable\_api\_termination](#input\_disable\_api\_termination) | If true, enables EC2 instance termination protection | `bool` | `null` | no |
122122
| <a name="input_ebs_optimized"></a> [ebs\_optimized](#input\_ebs\_optimized) | If true, the launched EC2 instance will be EBS-optimized | `bool` | `null` | no |
123+
| <a name="input_efa_indices"></a> [efa\_indices](#input\_efa\_indices) | The indices of the network interfaces that should be EFA-enabled. Only valid when `enable_efa_support` = `true` | `list(number)` | <pre>[<br/> 0<br/>]</pre> | no |
123124
| <a name="input_elastic_gpu_specifications"></a> [elastic\_gpu\_specifications](#input\_elastic\_gpu\_specifications) | The elastic GPU to attach to the instance | `any` | `{}` | no |
124125
| <a name="input_elastic_inference_accelerator"></a> [elastic\_inference\_accelerator](#input\_elastic\_inference\_accelerator) | Configuration block containing an Elastic Inference Accelerator to attach to the instance | `map(string)` | `{}` | no |
126+
| <a name="input_enable_efa_only"></a> [enable\_efa\_only](#input\_enable\_efa\_only) | Determines whether to enable EFA (`false`, default) or EFA and EFA-only (`true`) network interfaces. Note: requires vpc-cni version `v1.18.4` or later | `bool` | `false` | no |
125127
| <a name="input_enable_efa_support"></a> [enable\_efa\_support](#input\_enable\_efa\_support) | Determines whether to enable Elastic Fabric Adapter (EFA) support | `bool` | `false` | no |
126128
| <a name="input_enable_monitoring"></a> [enable\_monitoring](#input\_enable\_monitoring) | Enables/disables detailed monitoring | `bool` | `true` | no |
127129
| <a name="input_enabled_metrics"></a> [enabled\_metrics](#input\_enabled\_metrics) | A list of metrics to collect. The allowed values are `GroupDesiredCapacity`, `GroupInServiceCapacity`, `GroupPendingCapacity`, `GroupMinSize`, `GroupMaxSize`, `GroupInServiceInstances`, `GroupPendingInstances`, `GroupStandbyInstances`, `GroupStandbyCapacity`, `GroupTerminatingCapacity`, `GroupTerminatingInstances`, `GroupTotalCapacity`, `GroupTotalInstances` | `list(string)` | `[]` | no |

modules/self-managed-node-group/main.tf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ module "user_data" {
9090
################################################################################
9191

9292
data "aws_ec2_instance_type" "this" {
93-
count = local.enable_efa_support ? 1 : 0
93+
count = var.create && var.enable_efa_support ? 1 : 0
9494

9595
instance_type = var.instance_type
9696
}
@@ -101,13 +101,14 @@ locals {
101101
instance_type_provided = var.instance_type != ""
102102
num_network_cards = try(data.aws_ec2_instance_type.this[0].maximum_network_cards, 0)
103103

104+
# Primary network interface must be EFA, remaining can be EFA or EFA-only
104105
efa_network_interfaces = [
105106
for i in range(local.num_network_cards) : {
106107
associate_public_ip_address = false
107108
delete_on_termination = true
108109
device_index = i == 0 ? 0 : 1
109110
network_card_index = i
110-
interface_type = "efa"
111+
interface_type = var.enable_efa_only ? contains(concat([0], var.efa_indices), i) ? "efa" : "efa-only" : "efa"
111112
}
112113
]
113114

modules/self-managed-node-group/variables.tf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,19 @@ variable "enable_efa_support" {
334334
default = false
335335
}
336336

337+
# TODO - make this true by default at next breaking change (remove variable, only pass indices)
338+
variable "enable_efa_only" {
339+
description = "Determines whether to enable EFA (`false`, default) or EFA and EFA-only (`true`) network interfaces. Note: requires vpc-cni version `v1.18.4` or later"
340+
type = bool
341+
default = false
342+
}
343+
344+
variable "efa_indices" {
345+
description = "The indices of the network interfaces that should be EFA-enabled. Only valid when `enable_efa_support` = `true`"
346+
type = list(number)
347+
default = [0]
348+
}
349+
337350
variable "metadata_options" {
338351
description = "Customize the metadata options for the instance"
339352
type = map(string)

node_groups.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@ module "eks_managed_node_group" {
375375
metadata_options = try(each.value.metadata_options, var.eks_managed_node_group_defaults.metadata_options, local.metadata_options)
376376
enable_monitoring = try(each.value.enable_monitoring, var.eks_managed_node_group_defaults.enable_monitoring, true)
377377
enable_efa_support = try(each.value.enable_efa_support, var.eks_managed_node_group_defaults.enable_efa_support, false)
378+
enable_efa_only = try(each.value.enable_efa_only, var.eks_managed_node_group_defaults.enable_efa_only, false)
379+
efa_indices = try(each.value.efa_indices, var.eks_managed_node_group_defaults.efa_indices, [0])
378380
create_placement_group = try(each.value.create_placement_group, var.eks_managed_node_group_defaults.create_placement_group, false)
379381
placement = try(each.value.placement, var.eks_managed_node_group_defaults.placement, {})
380382
placement_group_az = try(each.value.placement_group_az, var.eks_managed_node_group_defaults.placement_group_az, null)
@@ -526,6 +528,8 @@ module "self_managed_node_group" {
526528
metadata_options = try(each.value.metadata_options, var.self_managed_node_group_defaults.metadata_options, local.metadata_options)
527529
enable_monitoring = try(each.value.enable_monitoring, var.self_managed_node_group_defaults.enable_monitoring, true)
528530
enable_efa_support = try(each.value.enable_efa_support, var.self_managed_node_group_defaults.enable_efa_support, false)
531+
enable_efa_only = try(each.value.enable_efa_only, var.self_managed_node_group_defaults.enable_efa_only, false)
532+
efa_indices = try(each.value.efa_indices, var.self_managed_node_group_defaults.efa_indices, [0])
529533
network_interfaces = try(each.value.network_interfaces, var.self_managed_node_group_defaults.network_interfaces, [])
530534
placement = try(each.value.placement, var.self_managed_node_group_defaults.placement, {})
531535
maintenance_options = try(each.value.maintenance_options, var.self_managed_node_group_defaults.maintenance_options, {})

tests/eks-managed-node-group/main.tf

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ module "eks" {
7474
control_plane_subnet_ids = module.vpc.intra_subnets
7575

7676
eks_managed_node_group_defaults = {
77-
ami_type = "AL2_x86_64"
77+
ami_type = "AL2023_x86_64_STANDARD"
7878
instance_types = ["m6i.large", "m5.large", "m5n.large", "m5zn.large"]
7979
}
8080

@@ -184,7 +184,7 @@ module "eks" {
184184

185185
# Use a custom AMI
186186
custom_ami = {
187-
ami_type = "AL2_ARM_64"
187+
ami_type = "AL2023_ARM_64_STANDARD"
188188
# Current default AMI used by managed node groups - pseudo "custom"
189189
ami_id = data.aws_ami.eks_default_arm.image_id
190190

@@ -211,13 +211,28 @@ module "eks" {
211211
ami_id = data.aws_ami.eks_default.image_id
212212
enable_bootstrap_user_data = true
213213

214-
pre_bootstrap_user_data = <<-EOT
215-
export FOO=bar
216-
EOT
217-
218-
post_bootstrap_user_data = <<-EOT
219-
echo "you are free little kubelet!"
220-
EOT
214+
cloudinit_pre_nodeadm = [{
215+
content = <<-EOT
216+
---
217+
apiVersion: node.eks.aws/v1alpha1
218+
kind: NodeConfig
219+
spec:
220+
kubelet:
221+
config:
222+
shutdownGracePeriod: 30s
223+
featureGates:
224+
DisableKubeletCloudCredentialProviders: true
225+
EOT
226+
content_type = "application/node.eks.aws"
227+
}]
228+
229+
# This is only possible with a custom AMI or self-managed node group
230+
cloudinit_post_nodeadm = [{
231+
content = <<-EOT
232+
echo "All done"
233+
EOT
234+
content_type = "text/x-shellscript; charset=\"us-ascii\""
235+
}]
221236

222237
capacity_type = "SPOT"
223238
force_update_version = true
@@ -227,14 +242,6 @@ module "eks" {
227242
GithubOrg = "terraform-aws-modules"
228243
}
229244

230-
taints = [
231-
{
232-
key = "dedicated"
233-
value = "gpuGroup"
234-
effect = "NO_SCHEDULE"
235-
}
236-
]
237-
238245
update_config = {
239246
max_unavailable_percentage = 33 # or set `max_unavailable`
240247
}
@@ -306,19 +313,53 @@ module "eks" {
306313
# Can be enabled when appropriate for testing/validation
307314
create = false
308315

309-
ami_type = "AL2_x86_64_GPU"
310-
instance_types = ["trn1n.32xlarge"]
316+
# The EKS AL2023 NVIDIA AMI provides all of the necessary components
317+
# for accelerated workloads w/ EFA
318+
ami_type = "AL2023_x86_64_NVIDIA"
319+
instance_types = ["p5e.48xlarge"]
311320

312-
enable_efa_support = true
313-
pre_bootstrap_user_data = <<-EOT
314-
# Mount NVME instance store volumes since they are typically
315-
# available on instances that support EFA
316-
setup-local-disks raid0
317-
EOT
321+
# Mount instance store volumes in RAID-0 for kubelet and containerd
322+
# https://github.com/awslabs/amazon-eks-ami/blob/master/doc/USER_GUIDE.md#raid-0-for-kubelet-and-containerd-raid0
323+
cloudinit_pre_nodeadm = [
324+
{
325+
content_type = "application/node.eks.aws"
326+
content = <<-EOT
327+
---
328+
apiVersion: node.eks.aws/v1alpha1
329+
kind: NodeConfig
330+
spec:
331+
instance:
332+
localStorage:
333+
strategy: RAID0
334+
EOT
335+
}
336+
]
337+
338+
# This will:
339+
# 1. Create a placement group to place the instances close to one another
340+
# 2. Ignore subnets that reside in AZs that do not support the instance type
341+
# 3. Expose all of the available EFA interfaces on the launch template
342+
enable_efa_support = true
343+
enable_efa_only = true
344+
efa_indices = [0, 4, 8, 12]
318345

319-
min_size = 2
320-
max_size = 2
321-
desired_size = 2
346+
min_size = 1
347+
max_size = 1
348+
desired_size = 1
349+
350+
labels = {
351+
"vpc.amazonaws.com/efa.present" = "true"
352+
"nvidia.com/gpu.present" = "true"
353+
}
354+
355+
taints = {
356+
# Ensure only GPU workloads are scheduled on this node group
357+
gpu = {
358+
key = "nvidia.com/gpu"
359+
value = "true"
360+
effect = "NO_SCHEDULE"
361+
}
362+
}
322363
}
323364
}
324365

@@ -532,7 +573,7 @@ data "aws_ami" "eks_default" {
532573

533574
filter {
534575
name = "name"
535-
values = ["amazon-eks-node-${local.cluster_version}-v*"]
576+
values = ["amazon-eks-node-al2023-x86_64-standard-${local.cluster_version}-v*"]
536577
}
537578
}
538579

@@ -542,7 +583,7 @@ data "aws_ami" "eks_default_arm" {
542583

543584
filter {
544585
name = "name"
545-
values = ["amazon-eks-arm64-node-${local.cluster_version}-v*"]
586+
values = ["amazon-eks-node-al2023-arm64-standard-${local.cluster_version}-v*"]
546587
}
547588
}
548589

0 commit comments

Comments
 (0)