- 云原生
- 容器编排
- DevOps
【免费下载链接】eksctl
The official CLI for Amazon EKS
本篇技术指南以 eksctl v0.174.0 版本发布说明为线索,聚焦该版本唯一的改进项——“Assign EFA network interface device index 1 to additional network cards”(PR #7643),并结合仓库中pkg/cfn/builder/network_interfaces.go的实现源码与测试用例,完整讲清 EFA(Elastic Fabric Adapter)节点组在 CloudFormation 启动模板中如何枚举网卡、分配DeviceIndex与NetworkCardIndex。读完本文,你可以掌握:efaEnabled配置项的完整约束与校验规则、多网卡实例(如 c5n.18xlarge)的网卡映射原理,以及 eksctl 如何在创建/校验阶段提前拦截不支持 EFA 的实例类型。
v0.174.0 版本概览
根据 v0.174.0 发布说明,本版本包含两部分变更:
| 分类 | 变更项 | 说明 |
|---|---|---|
| Improvements | Assign EFA network interface device index 1 to additional network cards (#7643) | 启用 EFA 的节点组中,为主网卡之外的额外网卡统一分配设备索引 1 |
| Maintenance | Refactor release workflows (#7632) | 重构发布(release)相关的工作流,属于 CI/发布流程的内部维护 |
下面以改进项为主线展开。
背景:eksctl 中的 EFA 配置
在 eksctl 的 ClusterConfig API(eksctl.io/v1alpha5)中,节点组(nodegroup)通过efaEnabled字段启用 EFA。API 类型的定义见 types.go:
// EFAEnabled creates the maximum allowed number of EFA-enabled network // cards on nodes in this group. // +optional EFAEnabled *bool `json:"efaEnabled,omitempty"`从这段注释可以推断出两个关键点:
- 启用 EFA 后,eksctl 会为该实例类型“创建允许的最大数量”的 EFA 网卡——即网卡数量不是用户指定的,而是由实例类型的
MaximumNetworkCards决定; efaEnabled是节点组级别(NodeGroupBase)的字段,因此托管与非托管节点组均适用。
在配置校验层面,validation.go 对 EFA 节点组施加了明确的拓扑约束:
if IsEnabled(ng.EFAEnabled) { if len(ng.AvailabilityZones) > 1 || len(ng.Subnets) > 1 { return fmt.Errorf("%s.efaEnabled nodegroups must have only one subnet or one availability zone", path) } }即启用 EFA 的节点组只能使用单个子网或单个可用区,否则在配置校验阶段直接报错。这与 EFA 通常配合cluster策略放置组(placement group)使用的场景一致——跨可用区部署会破坏低延迟 RDMA 网络的物理拓扑前提。
一个可复制的完整示例(来自 托管节点组用户文档):
apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: flux-cluster region: us-east-2 version: "1.23" availabilityZones: ["us-east-2b", "us-east-2c"] managedNodeGroups: - name: workers instanceType: hpc6a.48xlarge minSize: 64 maxSize: 64 labels: { "fluxoperator": "true" } availabilityZones: ["us-east-2b"] # 满足“单个可用区”约束 efaEnabled: true placement: groupName: eks-efa-testing # 显式指定放置组,跳过自动创建核心改进:额外网卡的 Device Index 分配为 1
网卡构建入口:buildNetworkInterfaces
启动模板的网络接口由 network_interfaces.go 中的buildNetworkInterfaces统一构建,它在节点组栈构建链路中被调用。以非托管节点组为例,nodegroup.go 中:
if err := buildNetworkInterfaces(ctx, launchTemplateData, ng.InstanceTypeList(), api.IsEnabled(ng.EFAEnabled), n.securityGroups, ng.ConnectionTracking, n.ec2API); err != nil { return nil, fmt.Errorf("couldn't build network interfaces for launch template data: %w", err) } if api.IsEnabled(ng.EFAEnabled) && ng.Placement == nil { groupName := n.newResource("NodeGroupPlacementGroup", &gfnec2.PlacementGroup{ Strategy: gfnt.NewString("cluster"), }) launchTemplateData.Placement = &gfnec2.LaunchTemplate_Placement{ GroupName: groupName, } }注意这里的行为:当efaEnabled: true且用户未显式指定placement.groupName时,eksctl 会自动在 CloudFormation 栈中创建一个cluster策略的放置组资源(NodeGroupPlacementGroup),并挂到启动模板上。上面示例中显式指定placement.groupName: eks-efa-testing正是为了避免这一自动创建。
EFA 支持性校验与网卡数量计算
buildNetworkInterfaces内部对 EFA 节点组做两件事(network_interfaces.go):
if efaEnabled { var numEFAs = math.MaxFloat64 for _, it := range instanceTypeInfo { networkInfo := it.NetworkInfo numEFAs = math.Min(float64(aws.ToInt32(networkInfo.MaximumNetworkCards)), numEFAs) if !aws.ToBool(networkInfo.EfaSupported) { return fmt.Errorf("instance type %s does not support EFA", it.InstanceType) } } firstNI.InterfaceType = gfnt.NewString("efa") nis := []gfnec2.LaunchTemplate_NetworkInterface{firstNI} // The primary network interface (device index 0) must be assigned to network card index 0 // device index 1 for additional cards for i := 1; i < int(numEFAs); i++ { ni := defaultNetworkInterface(securityGroups, 1, i, connectionTracking) ni.InterfaceType = gfnt.NewString("efa") nis = append(nis, ni) } launchTemplateData.NetworkInterfaces = nis }这段代码体现了 v0.174.0 改进项的实质:
- 主网卡(
firstNI):DeviceIndex = 0、NetworkCardIndex = 0,即第一张网卡必须绑定设备索引 0; - 额外网卡:循环中调用
defaultNetworkInterface(securityGroups, 1, i, ...),即设备索引固定为1、网卡索引取1..numEFAs-1——这正是发布说明中 “assign device index 1 to additional network cards” 的落地位置; - 网卡数量:取节点组内所有实例类型
MaximumNetworkCards的最小值(math.Min逐类型收敛),保证混合实例类型场景下,启动模板的网卡布局对每一种实例类型都合法; - 支持性校验:任一实例类型
EfaSupported为 false 时,立即返回instance type %s does not support EFA错误,而不是等到节点启动失败。
实例类型信息来自 EC2DescribeInstanceTypesAPI,错误处理为:
if err != nil { return fmt.Errorf("couldn't retrieve instance type description for %v: %w", instanceTypes, err) }单张网卡的字段细节
defaultNetworkInterface生成的每张网卡(network_interfaces.go):
func defaultNetworkInterface(securityGroups []*gfnt.Value, device, card int, connectionTracking *api.ConnectionTracking) gfnec2.LaunchTemplate_NetworkInterface { return gfnec2.LaunchTemplate_NetworkInterface{ // Explicitly un-setting this so that it doesn't get defaulted to true AssociatePublicIpAddress: nil, DeviceIndex: gfnt.NewInteger(device), Groups: gfnt.NewSlice(securityGroups...), NetworkCardIndex: gfnt.NewInteger(card), ConnectionTrackingSpecification: makeConnectionTrackingSpecification(connectionTracking), } }几个值得注意的细节:
AssociatePublicIpAddress被显式置 nil,源码注释说明这是为了避免 CloudFormation 将其默认为 true,从而让子网的 auto-assign public IPv4 设置继续生效;- 每张网卡都独立携带完整的
Groups(安全组列表),因此额外网卡与主网卡共享相同的安全组集合; ConnectionTrackingSpecification同样下发到每张网卡,连接跟踪超时的配置会作用于全部网卡。
测试用例印证
网络接口测试文件 用表驱动用例覆盖了上述全部行为,关键断言包括:
| 测试场景 | 输入 | 预期结果 |
|---|---|---|
| EFA 单网卡实例 | c5n.large(1 张卡) | 1 张网卡,InterfaceType = "efa" |
| EFA 多网卡实例 | c5n.18xlarge(4 张卡) | 4 张网卡,InterfaceType = "efa" |
| 混合实例类型 | c5n.large(1 卡)+c5n.xlarge(2 卡) | 1 张网卡,注释明确 “Should use minimum across instance types” |
| 不支持 EFA 的实例 | m5.large(EfaSupported: false) | 报错instance type m5.large does not support EFA |
| EC2 API 调用失败 | mock 返回错误 | 报错包含couldn't retrieve instance type description |
对于多网卡场景,测试还逐张校验了设备索引与网卡索引的映射(network_interfaces_test.go):
// Verify additional network interfaces for multi-card instances for i := 1; i < tt.expectedNetworkInterfaces; i++ { ni := launchTemplateData.NetworkInterfaces[i] assert.Equal(t, gfnt.Integer(1), ni.DeviceIndex.Raw()) assert.Equal(t, gfnt.Integer(i), ni.NetworkCardIndex.Raw()) assert.Nil(t, ni.AssociatePublicIpAddress) ... assert.Equal(t, gfnt.String("efa"), ni.InterfaceType.Raw()) // Verify security groups are correctly assigned to additional interfaces ... }即:第i张额外网卡的DeviceIndex恒为 1、NetworkCardIndex为i,且安全组与 EFA 接口类型与主网卡一致——这与上文源码中defaultNetworkInterface(securityGroups, 1, i, ...)的调用完全对应。
托管节点组的 EFA 路径
托管节点组的启动模板构建位于 managed_launch_template.go,与 EFA 相关的行为有两层:
第一层:EFA 安全组策略。托管节点组在启用 EFA 时,先通过efa.ProcessSecurityGroup决定安全组策略(efa.go):
- 若集群 Kubernetes 版本 ≥ 1.33(常量
api.EFABuiltInSupportVersion,见 types.go 中EFABuiltInSupportVersion = Version1_33)且节点组为托管类型,则复用默认安全组的内置 EFA 支持,不再创建自定义 EFA 安全组; - 否则(低版本或非托管)自动创建一个自定义 EFA 安全组并加入网卡的安全组列表。版本解析失败时会回退到创建自定义安全组的保守路径。
第二层:网卡构建与放置组。随后同样调用buildNetworkInterfaces(ctx, launchTemplateData, mng.InstanceTypeList(), true, securityGroupIDs, mng.ConnectionTracking, m.ec2API)(EFA 参数固定为true)。与托管/非托管路径一致,若用户既未指定placement也未指定capacityReservation,则自动创建cluster策略放置组;若配置了容量预留,源码注释说明放置组应已由预留侧创建,eksctl 不再重复创建。
维护项:重构发布工作流
发布说明的另一条目 “Refactor release workflows (#7632)” 属于 CI/发布流程的内部维护,不涉及用户可见行为或配置项的变化,此处仅作为版本完整性记录列出,不再展开。
小结
eksctl v0.174.0 的核心价值在于修正了 EFA 节点组启动模板中额外网卡的设备索引分配:主网卡保持DeviceIndex 0 / NetworkCardIndex 0,其余 EFA 网卡统一使用DeviceIndex 1并依次递增NetworkCardIndex。这一行为的实现入口是 buildNetworkInterfaces,其校验逻辑(单可用区约束、EFA 支持性检查、混合实例取最小网卡数)与完整断言均可在上述源码与 测试文件 中复核。对于在 EKS 上部署 RDMA/HPC 工作负载(HPC6A 等实例族)的用户,该版本确保了多网卡 EFA 实例在 CloudFormation 层面生成合法、一致的网卡布局。
- 云原生
- 容器编排
- DevOps
【免费下载链接】eksctl
The official CLI for Amazon EKS
相关推荐
eksctl 0.1.32 解析:VPC IPv6 自动分配、AMI 根设备动态探测与节点组 Launch Template 化改造
eksctl 0.1.32 解析:VPC IPv6 自动分配、AMI 根设备动态探测与节点组 Launch Template 化改造 本篇技术指南围绕 eksc
云原生容器编排DevOpsThingsBoard OPC-UA Gateway 设备节点路径(Device Node Path)配置详解
ThingsBoard OPC UA Gateway 设备节点路径(Device Node Path)配置详解 导读 在 ThingsBoard 的 OPC U
物联网后端数据可视化消息队列KubeVirt 主机设备直通与设备插件机制详解:PCI / Mediated Device 分配实战指南
KubeVirt 主机设备直通与设备插件机制详解:PCI / Mediated Device 分配实战指南 KubeVirt 提供了一套通用的主机设备分配(Ho
云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考