From 5a9e3e6bf5cb520713842d15501aea78bc8fb112 Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:21:43 +0200 Subject: [PATCH 01/11] add support for AL2023 for EKS-managed and self-managed nodes --- pkg/ami/auto_resolver.go | 6 +- pkg/ami/ssm_resolver.go | 3 + .../eksctl.io/v1alpha5/assets/schema.json | 10 +- pkg/apis/eksctl.io/v1alpha5/types.go | 14 ++- pkg/nodebootstrap/al2023.go | 109 ++++++++++++++++++ pkg/nodebootstrap/managed_al2.go | 24 +++- pkg/nodebootstrap/userdata.go | 12 +- 7 files changed, 160 insertions(+), 18 deletions(-) create mode 100644 pkg/nodebootstrap/al2023.go diff --git a/pkg/ami/auto_resolver.go b/pkg/ami/auto_resolver.go index 1781c3982b..70dca4d02b 100644 --- a/pkg/ami/auto_resolver.go +++ b/pkg/ami/auto_resolver.go @@ -23,6 +23,10 @@ const ( // MakeImageSearchPatterns creates a map of image search patterns by image OS family and class func MakeImageSearchPatterns(version string) map[string]map[int]string { return map[string]map[int]string{ + api.NodeImageFamilyAmazonLinux2023: { + ImageClassGeneral: fmt.Sprintf("amazon-eks-node-al2023-x86_64-standard-%s-v*", version), + ImageClassARM: fmt.Sprintf("amazon-eks-node-al2023-arm64-standard-%s-v*", version), + }, api.NodeImageFamilyAmazonLinux2: { ImageClassGeneral: fmt.Sprintf("amazon-eks-node-%s-v*", version), ImageClassGPU: fmt.Sprintf("amazon-eks-gpu-node-%s-*", version), @@ -59,7 +63,7 @@ func OwnerAccountID(imageFamily, region string) (string, error) { switch imageFamily { case api.NodeImageFamilyUbuntu2204, api.NodeImageFamilyUbuntu2004, api.NodeImageFamilyUbuntu1804: return ownerIDUbuntuFamily, nil - case api.NodeImageFamilyAmazonLinux2: + case api.NodeImageFamilyAmazonLinux2023, api.NodeImageFamilyAmazonLinux2: return api.EKSResourceAccountID(region), nil default: if api.IsWindowsImage(imageFamily) { diff --git a/pkg/ami/ssm_resolver.go b/pkg/ami/ssm_resolver.go index ad062196bf..84e9d67257 100644 --- a/pkg/ami/ssm_resolver.go +++ b/pkg/ami/ssm_resolver.go @@ -54,6 +54,9 @@ func MakeSSMParameterName(version, instanceType, imageFamily string) (string, er const fieldName = "image_id" switch imageFamily { + case api.NodeImageFamilyAmazonLinux2023: + return fmt.Sprintf("/aws/service/eks/optimized-ami/%s/%s/%s/standard/recommended/%s", + version, utils.ToKebabCase(imageFamily), instanceEC2ArchName(instanceType), fieldName), nil case api.NodeImageFamilyAmazonLinux2: return fmt.Sprintf("/aws/service/eks/optimized-ami/%s/%s/recommended/%s", version, imageType(imageFamily, instanceType, version), fieldName), nil case api.NodeImageFamilyWindowsServer2019CoreContainer, diff --git a/pkg/apis/eksctl.io/v1alpha5/assets/schema.json b/pkg/apis/eksctl.io/v1alpha5/assets/schema.json index 8bfc0edecb..06afbb7c65 100755 --- a/pkg/apis/eksctl.io/v1alpha5/assets/schema.json +++ b/pkg/apis/eksctl.io/v1alpha5/assets/schema.json @@ -1256,11 +1256,12 @@ }, "amiFamily": { "type": "string", - "description": "Valid variants are: `\"AmazonLinux2\"` (default), `\"Ubuntu2204\"`, `\"Ubuntu2004\"`, `\"Ubuntu1804\"`, `\"Bottlerocket\"`, `\"WindowsServer2019CoreContainer\"`, `\"WindowsServer2019FullContainer\"`, `\"WindowsServer2022CoreContainer\"`, `\"WindowsServer2022FullContainer\"`.", - "x-intellij-html-description": "Valid variants are: "AmazonLinux2" (default), "Ubuntu2204", "Ubuntu2004", "Ubuntu1804", "Bottlerocket", "WindowsServer2019CoreContainer", "WindowsServer2019FullContainer", "WindowsServer2022CoreContainer", "WindowsServer2022FullContainer".", + "description": "Valid variants are: `\"AmazonLinux2\"` (default), `\"AmazonLinux2023\"`, `\"Ubuntu2204\"`, `\"Ubuntu2004\"`, `\"Ubuntu1804\"`, `\"Bottlerocket\"`, `\"WindowsServer2019CoreContainer\"`, `\"WindowsServer2019FullContainer\"`, `\"WindowsServer2022CoreContainer\"`, `\"WindowsServer2022FullContainer\"`.", + "x-intellij-html-description": "Valid variants are: "AmazonLinux2" (default), "AmazonLinux2023", "Ubuntu2204", "Ubuntu2004", "Ubuntu1804", "Bottlerocket", "WindowsServer2019CoreContainer", "WindowsServer2019FullContainer", "WindowsServer2022CoreContainer", "WindowsServer2022FullContainer".", "default": "AmazonLinux2", "enum": [ "AmazonLinux2", + "AmazonLinux2023", "Ubuntu2204", "Ubuntu2004", "Ubuntu1804", @@ -1589,11 +1590,12 @@ }, "amiFamily": { "type": "string", - "description": "Valid variants are: `\"AmazonLinux2\"` (default), `\"Ubuntu2204\"`, `\"Ubuntu2004\"`, `\"Ubuntu1804\"`, `\"Bottlerocket\"`, `\"WindowsServer2019CoreContainer\"`, `\"WindowsServer2019FullContainer\"`, `\"WindowsServer2022CoreContainer\"`, `\"WindowsServer2022FullContainer\"`.", - "x-intellij-html-description": "Valid variants are: "AmazonLinux2" (default), "Ubuntu2204", "Ubuntu2004", "Ubuntu1804", "Bottlerocket", "WindowsServer2019CoreContainer", "WindowsServer2019FullContainer", "WindowsServer2022CoreContainer", "WindowsServer2022FullContainer".", + "description": "Valid variants are: `\"AmazonLinux2\"` (default), `\"AmazonLinux2023\"`, `\"Ubuntu2204\"`, `\"Ubuntu2004\"`, `\"Ubuntu1804\"`, `\"Bottlerocket\"`, `\"WindowsServer2019CoreContainer\"`, `\"WindowsServer2019FullContainer\"`, `\"WindowsServer2022CoreContainer\"`, `\"WindowsServer2022FullContainer\"`.", + "x-intellij-html-description": "Valid variants are: "AmazonLinux2" (default), "AmazonLinux2023", "Ubuntu2204", "Ubuntu2004", "Ubuntu1804", "Bottlerocket", "WindowsServer2019CoreContainer", "WindowsServer2019FullContainer", "WindowsServer2022CoreContainer", "WindowsServer2022FullContainer".", "default": "AmazonLinux2", "enum": [ "AmazonLinux2", + "AmazonLinux2023", "Ubuntu2204", "Ubuntu2004", "Ubuntu1804", diff --git a/pkg/apis/eksctl.io/v1alpha5/types.go b/pkg/apis/eksctl.io/v1alpha5/types.go index 07c956fa21..53bc098e99 100644 --- a/pkg/apis/eksctl.io/v1alpha5/types.go +++ b/pkg/apis/eksctl.io/v1alpha5/types.go @@ -222,12 +222,13 @@ const ( // All valid values of supported families should go in this block const ( // DefaultNodeImageFamily (default) - DefaultNodeImageFamily = NodeImageFamilyAmazonLinux2 - NodeImageFamilyAmazonLinux2 = "AmazonLinux2" - NodeImageFamilyUbuntu2204 = "Ubuntu2204" - NodeImageFamilyUbuntu2004 = "Ubuntu2004" - NodeImageFamilyUbuntu1804 = "Ubuntu1804" - NodeImageFamilyBottlerocket = "Bottlerocket" + DefaultNodeImageFamily = NodeImageFamilyAmazonLinux2 + NodeImageFamilyAmazonLinux2023 = "AmazonLinux2023" + NodeImageFamilyAmazonLinux2 = "AmazonLinux2" + NodeImageFamilyUbuntu2204 = "Ubuntu2204" + NodeImageFamilyUbuntu2004 = "Ubuntu2004" + NodeImageFamilyUbuntu1804 = "Ubuntu1804" + NodeImageFamilyBottlerocket = "Bottlerocket" NodeImageFamilyWindowsServer2019CoreContainer = "WindowsServer2019CoreContainer" NodeImageFamilyWindowsServer2019FullContainer = "WindowsServer2019FullContainer" @@ -606,6 +607,7 @@ func SupportedNodeVolumeTypes() []string { // supportedAMIFamilies are the AMI families supported by EKS func supportedAMIFamilies() []string { return []string{ + NodeImageFamilyAmazonLinux2023, NodeImageFamilyAmazonLinux2, NodeImageFamilyUbuntu2204, NodeImageFamilyUbuntu2004, diff --git a/pkg/nodebootstrap/al2023.go b/pkg/nodebootstrap/al2023.go new file mode 100644 index 0000000000..347a14b86b --- /dev/null +++ b/pkg/nodebootstrap/al2023.go @@ -0,0 +1,109 @@ +package nodebootstrap + +import ( + "bytes" + "encoding/base64" + + api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5" + "github.com/weaveworks/eksctl/pkg/nodebootstrap/assets" + "github.com/weaveworks/eksctl/pkg/nodebootstrap/utils" +) + +// AL2023 is a bootstrapper for both EKS-managed and self-managed AmazonLinux2023 nodegroups +type AL2023 struct { + cfg *api.ClusterConfig + ng *api.NodeGroupBase + taints []api.NodeGroupTaint + clusterDNS string + UserDataMimeBoundary string +} + +func NewAL2023Bootstrapper(cfg *api.ClusterConfig, np api.NodePool, clusterDNS string) *AL2023 { + return &AL2023{ + cfg: cfg, + ng: np.BaseNodeGroup(), + taints: np.NGTaints(), + clusterDNS: clusterDNS, + } +} + +func (m *AL2023) UserData() (string, error) { + var ( + buf bytes.Buffer + cloudboot []string + ) + + if api.IsEnabled(m.ng.EFAEnabled) { + cloudboot = append(cloudboot, assets.EfaManagedBoothook) + } + + if err := createMimeMessage(&buf, []string{}, cloudboot, m.makeNodeConfig(), m.UserDataMimeBoundary); err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(buf.Bytes()), nil +} + +func (m *AL2023) makeNodeConfig() *NodeConfig { + nodeConfig := &NodeConfig{ + ApiVersion: "node.eks.aws/v1alpha1", + Kind: "NodeConfig", + Spec: NodeSpec{ + Cluster: ClusterSpec{ + Name: m.cfg.Metadata.Name, + ApiServerEndpoint: m.cfg.Status.Endpoint, + CertificateAuthority: base64.StdEncoding.EncodeToString(m.cfg.Status.CertificateAuthorityData), + CIDR: m.cfg.Status.KubernetesNetworkConfig.ServiceIPv4CIDR, + }, + Kubelet: KubeletSpec{ + Config: KubeletConfig{ + ClusterDNS: []string{m.clusterDNS}, + }, + Flags: []string{"--node-labels=" + formatLabels(m.ng.Labels)}, + }, + }, + } + + if m.ng.MaxPodsPerNode > 0 { + nodeConfig.Spec.Kubelet.Config.MaxPods = &m.ng.MaxPodsPerNode + } + + if len(m.taints) > 0 { + nodeConfig.Spec.Kubelet.Flags = append(nodeConfig.Spec.Kubelet.Flags, utils.FormatTaints(m.taints)) + } + + return nodeConfig +} + +// NodeConfig represents the core EKS node configuration +type NodeConfig struct { + ApiVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Spec NodeSpec `yaml:"spec"` +} + +// NodeSpec encapsulates the 'spec' section of the YAML +type NodeSpec struct { + Cluster ClusterSpec `yaml:"cluster"` + Kubelet KubeletSpec `yaml:"kubelet"` +} + +// ClusterSpec holds cluster-related parameters +type ClusterSpec struct { + ApiServerEndpoint string `yaml:"apiServerEndpoint"` + CertificateAuthority string `yaml:"certificateAuthority"` + CIDR string `yaml:"cidr"` + Name string `yaml:"name"` +} + +// KubeletSpec captures Kubelet parameters and flags +type KubeletSpec struct { + Config KubeletConfig `yaml:"config"` + Flags []string `yaml:"flags"` +} + +// KubeletConfig holds the 'config' section +type KubeletConfig struct { + MaxPods *int `yaml:"maxPods,omitempty"` + ClusterDNS []string `yaml:"clusterDNS"` +} diff --git a/pkg/nodebootstrap/managed_al2.go b/pkg/nodebootstrap/managed_al2.go index 34739430f4..a047354b92 100644 --- a/pkg/nodebootstrap/managed_al2.go +++ b/pkg/nodebootstrap/managed_al2.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/pkg/errors" + "gopkg.in/yaml.v2" api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5" "github.com/weaveworks/eksctl/pkg/nodebootstrap/assets" @@ -60,7 +61,7 @@ func (m *ManagedAL2) UserData() (string, error) { return "", nil } - if err := createMimeMessage(&buf, scripts, cloudboot, m.UserDataMimeBoundary); err != nil { + if err := createMimeMessage(&buf, scripts, cloudboot, nil, m.UserDataMimeBoundary); err != nil { return "", err } @@ -85,7 +86,7 @@ func makeCustomAMIUserData(ng *api.NodeGroupBase, mimeBoundary string) (string, return "", nil } - if err := createMimeMessage(&buf, scripts, nil, mimeBoundary); err != nil { + if err := createMimeMessage(&buf, scripts, nil, nil, mimeBoundary); err != nil { return "", err } @@ -100,7 +101,7 @@ set -ex return script } -func createMimeMessage(writer io.Writer, scripts, cloudboots []string, mimeBoundary string) error { +func createMimeMessage(writer io.Writer, scripts, cloudboots []string, nodeConfig *NodeConfig, mimeBoundary string) error { mw := multipart.NewWriter(writer) if mimeBoundary != "" { if err := mw.SetBoundary(mimeBoundary); err != nil { @@ -136,5 +137,22 @@ func createMimeMessage(writer io.Writer, scripts, cloudboots []string, mimeBound return err } } + + if nodeConfig != nil { + yamlData, err := yaml.Marshal(nodeConfig) + if err != nil { + return fmt.Errorf("error marshalling node configuration: %w", err) + } + part, err := mw.CreatePart(map[string][]string{ + "Content-Type": {"application/node.eks.aws"}, + }) + if err != nil { + return err + } + if _, err = part.Write(yamlData); err != nil { + return err + } + } + return mw.Close() } diff --git a/pkg/nodebootstrap/userdata.go b/pkg/nodebootstrap/userdata.go index a5ddd00a3e..15adc20c33 100644 --- a/pkg/nodebootstrap/userdata.go +++ b/pkg/nodebootstrap/userdata.go @@ -52,6 +52,8 @@ func NewBootstrapper(clusterConfig *api.ClusterConfig, ng *api.NodeGroup) (Boots return NewUbuntuBootstrapper(clusterConfig, ng, clusterDNS), nil case api.NodeImageFamilyBottlerocket: return NewBottlerocketBootstrapper(clusterConfig, ng), nil + case api.NodeImageFamilyAmazonLinux2023: + return NewAL2023Bootstrapper(clusterConfig, ng, clusterDNS), nil case api.NodeImageFamilyAmazonLinux2: return NewAL2Bootstrapper(clusterConfig, ng, clusterDNS), nil default: @@ -62,21 +64,23 @@ func NewBootstrapper(clusterConfig *api.ClusterConfig, ng *api.NodeGroup) (Boots // NewManagedBootstrapper creates a new bootstrapper for managed nodegroups based on the AMI family func NewManagedBootstrapper(clusterConfig *api.ClusterConfig, ng *api.ManagedNodeGroup) (Bootstrapper, error) { + clusterDNS, err := GetClusterDNS(clusterConfig) + if err != nil { + return nil, err + } if api.IsWindowsImage(ng.AMIFamily) { return &ManagedWindows{ NodeGroup: ng, }, nil } switch ng.AMIFamily { + case api.NodeImageFamilyAmazonLinux2023: + return NewAL2023Bootstrapper(clusterConfig, ng, clusterDNS), nil case api.NodeImageFamilyAmazonLinux2: return NewManagedAL2Bootstrapper(ng), nil case api.NodeImageFamilyBottlerocket: return NewManagedBottlerocketBootstrapper(clusterConfig, ng), nil case api.NodeImageFamilyUbuntu1804, api.NodeImageFamilyUbuntu2004, api.NodeImageFamilyUbuntu2204: - clusterDNS, err := GetClusterDNS(clusterConfig) - if err != nil { - return nil, err - } return NewUbuntuBootstrapper(clusterConfig, ng, clusterDNS), nil } return nil, nil From b29ad527698c71f4d10c446b4a1e3caa15f156d0 Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:02:34 +0200 Subject: [PATCH 02/11] ensure AL2023 only supports containerd --- pkg/apis/eksctl.io/v1alpha5/defaults.go | 22 ++++++++++++------- pkg/apis/eksctl.io/v1alpha5/defaults_test.go | 11 ++++++++++ pkg/apis/eksctl.io/v1alpha5/types.go | 4 ++-- pkg/apis/eksctl.io/v1alpha5/validation.go | 9 +++++--- .../eksctl.io/v1alpha5/validation_test.go | 17 +++++++++++++- 5 files changed, 49 insertions(+), 14 deletions(-) diff --git a/pkg/apis/eksctl.io/v1alpha5/defaults.go b/pkg/apis/eksctl.io/v1alpha5/defaults.go index 8ac085cc80..417ea56a5a 100644 --- a/pkg/apis/eksctl.io/v1alpha5/defaults.go +++ b/pkg/apis/eksctl.io/v1alpha5/defaults.go @@ -249,17 +249,23 @@ func setContainerRuntimeDefault(ng *NodeGroup, clusterVersion string) { return } - // since clusterVersion is standardised beforehand, we can safely ignore the error - isDockershimDeprecated, _ := utils.IsMinVersion(DockershimDeprecationVersion, clusterVersion) + if ng.AMIFamily == NodeImageFamilyAmazonLinux2023 { + ng.ContainerRuntime = aws.String(ContainerRuntimeContainerD) + return + } - if isDockershimDeprecated { + // since clusterVersion is standardised beforehand, we can safely ignore the error + if isDockershimDeprecated, _ := utils.IsMinVersion(DockershimDeprecationVersion, clusterVersion); isDockershimDeprecated { ng.ContainerRuntime = aws.String(ContainerRuntimeContainerD) - } else { - ng.ContainerRuntime = aws.String(ContainerRuntimeDockerD) - if IsWindowsImage(ng.AMIFamily) { - ng.ContainerRuntime = aws.String(ContainerRuntimeDockerForWindows) - } + return } + + if IsWindowsImage(ng.AMIFamily) { + ng.ContainerRuntime = aws.String(ContainerRuntimeDockerForWindows) + return + } + + ng.ContainerRuntime = aws.String(ContainerRuntimeDockerD) } func setIAMDefaults(iamConfig *NodeGroupIAM) { diff --git a/pkg/apis/eksctl.io/v1alpha5/defaults_test.go b/pkg/apis/eksctl.io/v1alpha5/defaults_test.go index f865880db3..6d100f9e3d 100644 --- a/pkg/apis/eksctl.io/v1alpha5/defaults_test.go +++ b/pkg/apis/eksctl.io/v1alpha5/defaults_test.go @@ -282,6 +282,17 @@ var _ = Describe("ClusterConfig validation", func() { Expect(*testNodeGroup.ContainerRuntime).To(Equal(ContainerRuntimeDockerForWindows)) }) }) + When("ami family is AmazonLinux2023", func() { + It("defaults to containerd as a container runtime", func() { + testNodeGroup := NodeGroup{ + NodeGroupBase: &NodeGroupBase{ + AMIFamily: NodeImageFamilyAmazonLinux2023, + }, + } + SetNodeGroupDefaults(&testNodeGroup, &ClusterMeta{Version: Version1_23}, false) + Expect(*testNodeGroup.ContainerRuntime).To(Equal(ContainerRuntimeContainerD)) + }) + }) }) Context("Kubernetes version 1.24 or greater", func() { diff --git a/pkg/apis/eksctl.io/v1alpha5/types.go b/pkg/apis/eksctl.io/v1alpha5/types.go index 53bc098e99..4b2345c7dd 100644 --- a/pkg/apis/eksctl.io/v1alpha5/types.go +++ b/pkg/apis/eksctl.io/v1alpha5/types.go @@ -604,8 +604,8 @@ func SupportedNodeVolumeTypes() []string { } } -// supportedAMIFamilies are the AMI families supported by EKS -func supportedAMIFamilies() []string { +// SupportedAMIFamilies are the AMI families supported by EKS +func SupportedAMIFamilies() []string { return []string{ NodeImageFamilyAmazonLinux2023, NodeImageFamilyAmazonLinux2, diff --git a/pkg/apis/eksctl.io/v1alpha5/validation.go b/pkg/apis/eksctl.io/v1alpha5/validation.go index f3b2a5c975..caf62acfbc 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation.go @@ -625,7 +625,7 @@ func validateNodeGroupBase(np NodePool, path string, controlPlaneOnOutposts bool if ng.AMIFamily == NodeImageFamilyWindowsServer20H2CoreContainer || ng.AMIFamily == NodeImageFamilyWindowsServer2004CoreContainer { return fmt.Errorf("AMI Family %s is deprecated. For more information, head to the Amazon documentation on Windows AMIs (https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-windows-ami.html)", ng.AMIFamily) } - return fmt.Errorf("AMI Family %s is not supported - use one of: %s", ng.AMIFamily, strings.Join(supportedAMIFamilies(), ", ")) + return fmt.Errorf("AMI Family %s is not supported - use one of: %s", ng.AMIFamily, strings.Join(SupportedAMIFamilies(), ", ")) } if controlPlaneOnOutposts && ng.AMIFamily != NodeImageFamilyAmazonLinux2 { return fmt.Errorf("only %s is supported on local clusters", NodeImageFamilyAmazonLinux2) @@ -883,6 +883,9 @@ func ValidateNodeGroup(i int, ng *NodeGroup, cfg *ClusterConfig) error { } if ng.ContainerRuntime != nil { + if ng.AMIFamily == NodeImageFamilyAmazonLinux2023 && *ng.ContainerRuntime != ContainerRuntimeContainerD { + return fmt.Errorf("only %s is supported for container runtime on %s nodes", ContainerRuntimeContainerD, NodeImageFamilyAmazonLinux2023) + } if *ng.ContainerRuntime != ContainerRuntimeDockerD && *ng.ContainerRuntime != ContainerRuntimeContainerD && *ng.ContainerRuntime != ContainerRuntimeDockerForWindows { return fmt.Errorf("only %s, %s and %s are supported for container runtime", ContainerRuntimeContainerD, ContainerRuntimeDockerD, ContainerRuntimeDockerForWindows) } @@ -1266,7 +1269,7 @@ func ValidateManagedNodeGroup(index int, ng *ManagedNodeGroup) error { } func normalizeAMIFamily(ng *NodeGroupBase) { - for _, family := range supportedAMIFamilies() { + for _, family := range SupportedAMIFamilies() { if strings.EqualFold(ng.AMIFamily, family) { ng.AMIFamily = family return @@ -1436,7 +1439,7 @@ func validateNodeGroupKubeletExtraConfig(kubeletConfig *InlineDocument) error { } func isSupportedAMIFamily(imageFamily string) bool { - for _, image := range supportedAMIFamilies() { + for _, image := range SupportedAMIFamilies() { if imageFamily == image { return true } diff --git a/pkg/apis/eksctl.io/v1alpha5/validation_test.go b/pkg/apis/eksctl.io/v1alpha5/validation_test.go index 4526e92e3c..a7838b1888 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation_test.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation_test.go @@ -2,6 +2,7 @@ package v1alpha5_test import ( "fmt" + "strings" "github.com/aws/aws-sdk-go-v2/aws" @@ -114,6 +115,20 @@ var _ = Describe("ClusterConfig validation", func() { Expect(err).To(HaveOccurred()) }) + It("should reject docker runtime if AMI Family is AmazonLinux2023", func() { + cfg := api.NewClusterConfig() + cfg.Metadata.Version = api.Version1_23 + ng0 := cfg.NewNodeGroup() + ng0.Name = "node-group" + ng0.AMIFamily = api.NodeImageFamilyAmazonLinux2023 + ng0.ContainerRuntime = aws.String(api.ContainerRuntimeDockerD) + err := api.ValidateClusterConfig(cfg) + Expect(err).NotTo(HaveOccurred()) + err = api.ValidateNodeGroup(0, ng0, cfg) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("only %s is supported for container runtime on %s nodes", api.ContainerRuntimeContainerD, api.NodeImageFamilyAmazonLinux2023)))) + }) + It("should reject docker runtime if version is 1.24 or greater", func() { cfg := api.NewClusterConfig() cfg.Metadata.Version = api.Version1_24 @@ -2002,7 +2017,7 @@ var _ = Describe("ClusterConfig validation", func() { It("fails when the AMIFamily is not supported", func() { ng.AMIFamily = "SomeTrash" err := api.ValidateNodeGroup(0, ng, cfg) - Expect(err).To(MatchError("AMI Family SomeTrash is not supported - use one of: AmazonLinux2, Ubuntu2204, Ubuntu2004, Ubuntu1804, Bottlerocket, WindowsServer2019CoreContainer, WindowsServer2019FullContainer, WindowsServer2022CoreContainer, WindowsServer2022FullContainer")) + Expect(err).To(MatchError(fmt.Sprintf("AMI Family SomeTrash is not supported - use one of: %s", strings.Join(api.SupportedAMIFamilies(), ", ")))) }) It("fails when the AmiFamily is not supported for managed nodes with custom AMI", func() { From 7dea971de8c875de0b9eba361bd90c7983fea01a Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:32:12 +0200 Subject: [PATCH 03/11] add GPU related validations + small nits --- .../eksctl.io/v1alpha5/gpu_validation_test.go | 36 +++++++++++++++++++ pkg/apis/eksctl.io/v1alpha5/validation.go | 21 +++++++---- pkg/cfn/builder/managed_nodegroup.go | 4 +++ pkg/eks/nodegroup_service.go | 7 +++- pkg/nodebootstrap/al2023.go | 2 +- 5 files changed, 61 insertions(+), 9 deletions(-) diff --git a/pkg/apis/eksctl.io/v1alpha5/gpu_validation_test.go b/pkg/apis/eksctl.io/v1alpha5/gpu_validation_test.go index 8523bd9519..f1f5a6f07f 100644 --- a/pkg/apis/eksctl.io/v1alpha5/gpu_validation_test.go +++ b/pkg/apis/eksctl.io/v1alpha5/gpu_validation_test.go @@ -39,6 +39,24 @@ var _ = Describe("GPU instance support", func() { mng.InstanceSelector = &api.InstanceSelector{} assertValidationError(e, api.ValidateManagedNodeGroup(0, mng)) }, + Entry("AL2023 INF", gpuInstanceEntry{ + amiFamily: api.NodeImageFamilyAmazonLinux2023, + gpuInstanceType: "inf1.xlarge", + expectUnsupportedErr: true, + instanceTypeName: "Inferentia", + }), + Entry("AL2023 TRN", gpuInstanceEntry{ + amiFamily: api.NodeImageFamilyAmazonLinux2023, + gpuInstanceType: "trn1.2xlarge", + expectUnsupportedErr: true, + instanceTypeName: "Trainium", + }), + Entry("AL2023 NVIDIA", gpuInstanceEntry{ + amiFamily: api.NodeImageFamilyAmazonLinux2023, + gpuInstanceType: "g4dn.xlarge", + expectUnsupportedErr: true, + instanceTypeName: "GPU", + }), Entry("AL2", gpuInstanceEntry{ gpuInstanceType: "asdf", amiFamily: api.NodeImageFamilyAmazonLinux2, @@ -72,6 +90,24 @@ var _ = Describe("GPU instance support", func() { assertValidationError(e, api.ValidateNodeGroup(0, ng, api.NewClusterConfig())) }, + Entry("AL2023 INF", gpuInstanceEntry{ + amiFamily: api.NodeImageFamilyAmazonLinux2023, + gpuInstanceType: "inf1.xlarge", + expectUnsupportedErr: true, + instanceTypeName: "Inferentia", + }), + Entry("AL2023 TRN", gpuInstanceEntry{ + amiFamily: api.NodeImageFamilyAmazonLinux2023, + gpuInstanceType: "trn1.2xlarge", + expectUnsupportedErr: true, + instanceTypeName: "Trainium", + }), + Entry("AL2023 NVIDIA", gpuInstanceEntry{ + amiFamily: api.NodeImageFamilyAmazonLinux2023, + gpuInstanceType: "g4dn.xlarge", + expectUnsupportedErr: true, + instanceTypeName: "GPU", + }), Entry("AL2", gpuInstanceEntry{ gpuInstanceType: "g4dn.xlarge", amiFamily: api.NodeImageFamilyAmazonLinux2, diff --git a/pkg/apis/eksctl.io/v1alpha5/validation.go b/pkg/apis/eksctl.io/v1alpha5/validation.go index caf62acfbc..f8ba37c2c7 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation.go @@ -53,6 +53,10 @@ var ( return fmt.Errorf("the %q addon must be installed to create pod identity associations; %s", PodIdentityAgentAddon, suggestion) } + ErrUnsupportedInstanceTypes = func(instanceType, amiFamily, suggestion string) error { + return fmt.Errorf("%s instance types are not supported for %s; %s", instanceType, amiFamily, suggestion) + } + GPUDriversWarning = func(amiFamily string) string { return fmt.Sprintf("%s does not ship with NVIDIA GPU drivers installed, hence won't support running GPU-accelerated workloads out of the box", amiFamily) } @@ -641,8 +645,15 @@ func validateNodeGroupBase(np NodePool, path string, controlPlaneOnOutposts bool } } + instanceType := SelectInstanceType(np) + + if ng.AMIFamily == NodeImageFamilyAmazonLinux2023 && instanceutils.IsNvidiaInstanceType(instanceType) { + return ErrUnsupportedInstanceTypes("GPU", NodeImageFamilyAmazonLinux2023, + fmt.Sprintf("EKS accelerated AMIs based on %s will be available at a later date", NodeImageFamilyAmazonLinux2023)) + } + if ng.AMIFamily != NodeImageFamilyAmazonLinux2 && ng.AMIFamily != NodeImageFamilyBottlerocket && ng.AMIFamily != "" { - if instanceutils.IsNvidiaInstanceType(SelectInstanceType(np)) { + if instanceutils.IsNvidiaInstanceType(instanceType) { logger.Warning(GPUDriversWarning(ng.AMIFamily)) } if ng.InstanceSelector != nil && !ng.InstanceSelector.IsZero() && @@ -652,17 +663,13 @@ func validateNodeGroupBase(np NodePool, path string, controlPlaneOnOutposts bool } if ng.AMIFamily != NodeImageFamilyAmazonLinux2 && ng.AMIFamily != "" { - instanceType := SelectInstanceType(np) - unsupportedErr := func(instanceTypeName string) error { - return fmt.Errorf("%s instance types are not supported for %s", instanceTypeName, ng.AMIFamily) - } // Only AL2 supports Inferentia hosts. if instanceutils.IsInferentiaInstanceType(instanceType) { - return unsupportedErr("Inferentia") + return ErrUnsupportedInstanceTypes("Inferentia", ng.AMIFamily, fmt.Sprintf("please use %s instead", NodeImageFamilyAmazonLinux2)) } // Only AL2 supports Trainium hosts. if instanceutils.IsTrainiumInstanceType(instanceType) { - return unsupportedErr("Trainium") + return ErrUnsupportedInstanceTypes("Trainium", ng.AMIFamily, fmt.Sprintf("please use %s instead", NodeImageFamilyAmazonLinux2)) } } diff --git a/pkg/cfn/builder/managed_nodegroup.go b/pkg/cfn/builder/managed_nodegroup.go index 90bda7c9f5..b90d07eb44 100644 --- a/pkg/cfn/builder/managed_nodegroup.go +++ b/pkg/cfn/builder/managed_nodegroup.go @@ -268,6 +268,10 @@ func getAMIType(ng *api.ManagedNodeGroup, instanceType string) ekstypes.AMITypes ARM ekstypes.AMITypes ARMGPU ekstypes.AMITypes }{ + api.NodeImageFamilyAmazonLinux2023: { + X86x64: ekstypes.AMITypesAl2023X8664Standard, + ARM: ekstypes.AMITypesAl2023Arm64Standard, + }, api.NodeImageFamilyAmazonLinux2: { X86x64: ekstypes.AMITypesAl2X8664, X86GPU: ekstypes.AMITypesAl2X8664Gpu, diff --git a/pkg/eks/nodegroup_service.go b/pkg/eks/nodegroup_service.go index f4cb170f5c..bfd4034ccd 100644 --- a/pkg/eks/nodegroup_service.go +++ b/pkg/eks/nodegroup_service.go @@ -60,7 +60,12 @@ func (n *NodeGroupService) Normalize(ctx context.Context, nodePools []api.NodePo if ng.LaunchTemplate == nil && ng.InstanceType == "" && len(ng.InstanceTypes) == 0 && ng.InstanceSelector.IsZero() { ng.InstanceType = api.DefaultNodeType } - hasNativeAMIFamilySupport := ng.AMIFamily == api.NodeImageFamilyAmazonLinux2 || ng.AMIFamily == api.NodeImageFamilyBottlerocket || api.IsWindowsImage(ng.AMIFamily) + hasNativeAMIFamilySupport := + ng.AMIFamily == api.NodeImageFamilyAmazonLinux2023 || + ng.AMIFamily == api.NodeImageFamilyAmazonLinux2 || + ng.AMIFamily == api.NodeImageFamilyBottlerocket || + api.IsWindowsImage(ng.AMIFamily) + if !hasNativeAMIFamilySupport && !api.IsAMI(ng.AMI) { if err := ResolveAMI(ctx, n.provider, clusterConfig.Metadata.Version, np); err != nil { return err diff --git a/pkg/nodebootstrap/al2023.go b/pkg/nodebootstrap/al2023.go index 347a14b86b..2737678529 100644 --- a/pkg/nodebootstrap/al2023.go +++ b/pkg/nodebootstrap/al2023.go @@ -69,7 +69,7 @@ func (m *AL2023) makeNodeConfig() *NodeConfig { } if len(m.taints) > 0 { - nodeConfig.Spec.Kubelet.Flags = append(nodeConfig.Spec.Kubelet.Flags, utils.FormatTaints(m.taints)) + nodeConfig.Spec.Kubelet.Flags = append(nodeConfig.Spec.Kubelet.Flags, "--register-with-taints="+utils.FormatTaints(m.taints)) } return nodeConfig From 677daf0a2a322e8cbe626743da5b9116dd3b7622 Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:13:56 +0200 Subject: [PATCH 04/11] add support for upgrades --- pkg/ami/ssm_resolver.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/ami/ssm_resolver.go b/pkg/ami/ssm_resolver.go index 84e9d67257..55b3e8f2e5 100644 --- a/pkg/ami/ssm_resolver.go +++ b/pkg/ami/ssm_resolver.go @@ -86,6 +86,10 @@ func MakeSSMParameterName(version, instanceType, imageFamily string) (string, er // MakeManagedSSMParameterName creates an SSM parameter name for a managed nodegroup func MakeManagedSSMParameterName(version string, amiType ekstypes.AMITypes) (string, error) { switch amiType { + case ekstypes.AMITypesAl2023X8664Standard: + return fmt.Sprintf("/aws/service/eks/optimized-ami/%s/%s/x86_64/standard/recommended/release_version", version, utils.ToKebabCase(api.NodeImageFamilyAmazonLinux2023)), nil + case ekstypes.AMITypesAl2023Arm64Standard: + return fmt.Sprintf("/aws/service/eks/optimized-ami/%s/%s/arm64/standard/recommended/release_version", version, utils.ToKebabCase(api.NodeImageFamilyAmazonLinux2023)), nil case ekstypes.AMITypesAl2X8664: imageType := utils.ToKebabCase(api.NodeImageFamilyAmazonLinux2) return fmt.Sprintf("/aws/service/eks/optimized-ami/%s/%s/recommended/release_version", version, imageType), nil From c21c4a605bd13c09ad07d71b02f15a5fe9921e75 Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:13:43 +0200 Subject: [PATCH 05/11] add support for EFA --- pkg/nodebootstrap/al2023.go | 64 +++--- pkg/nodebootstrap/al2023_test.go | 184 ++++++++++++++++++ pkg/nodebootstrap/assets/assets.go | 10 + .../assets/scripts/efa.al2023.sh | 12 ++ .../scripts/efa.managed.al2023.boothook | 9 + pkg/nodebootstrap/userdata.go | 2 +- 6 files changed, 254 insertions(+), 27 deletions(-) create mode 100644 pkg/nodebootstrap/al2023_test.go create mode 100644 pkg/nodebootstrap/assets/scripts/efa.al2023.sh create mode 100644 pkg/nodebootstrap/assets/scripts/efa.managed.al2023.boothook diff --git a/pkg/nodebootstrap/al2023.go b/pkg/nodebootstrap/al2023.go index 2737678529..3477ccb506 100644 --- a/pkg/nodebootstrap/al2023.go +++ b/pkg/nodebootstrap/al2023.go @@ -9,16 +9,40 @@ import ( "github.com/weaveworks/eksctl/pkg/nodebootstrap/utils" ) -// AL2023 is a bootstrapper for both EKS-managed and self-managed AmazonLinux2023 nodegroups type AL2023 struct { - cfg *api.ClusterConfig - ng *api.NodeGroupBase - taints []api.NodeGroupTaint - clusterDNS string + cfg *api.ClusterConfig + ng *api.NodeGroupBase + taints []api.NodeGroupTaint + clusterDNS string + + scripts []string + cloudboot []string + nodeConfig *NodeConfig + UserDataMimeBoundary string } -func NewAL2023Bootstrapper(cfg *api.ClusterConfig, np api.NodePool, clusterDNS string) *AL2023 { +func NewManagedAL2023Bootstrapper(cfg *api.ClusterConfig, mng *api.ManagedNodeGroup, clusterDNS string) *AL2023 { + al2023 := newAL2023Bootstrapper(cfg, mng, clusterDNS) + if api.IsEnabled(mng.EFAEnabled) { + al2023.cloudboot = append(al2023.cloudboot, assets.EfaManagedAL2023Boothook) + } + if api.IsAMI(mng.AMI) { + al2023.setNodeConfig() + } + return al2023 +} + +func NewAL2023Bootstrapper(cfg *api.ClusterConfig, ng *api.NodeGroup, clusterDNS string) *AL2023 { + al2023 := newAL2023Bootstrapper(cfg, ng, clusterDNS) + if api.IsEnabled(ng.EFAEnabled) { + al2023.scripts = append(al2023.scripts, assets.EfaAl2023Sh) + } + al2023.setNodeConfig() + return al2023 +} + +func newAL2023Bootstrapper(cfg *api.ClusterConfig, np api.NodePool, clusterDNS string) *AL2023 { return &AL2023{ cfg: cfg, ng: np.BaseNodeGroup(), @@ -28,24 +52,21 @@ func NewAL2023Bootstrapper(cfg *api.ClusterConfig, np api.NodePool, clusterDNS s } func (m *AL2023) UserData() (string, error) { - var ( - buf bytes.Buffer - cloudboot []string - ) + var buf bytes.Buffer - if api.IsEnabled(m.ng.EFAEnabled) { - cloudboot = append(cloudboot, assets.EfaManagedBoothook) + if len(m.scripts) == 0 && len(m.cloudboot) == 0 && m.nodeConfig == nil { + return "", nil } - if err := createMimeMessage(&buf, []string{}, cloudboot, m.makeNodeConfig(), m.UserDataMimeBoundary); err != nil { + if err := createMimeMessage(&buf, m.scripts, m.cloudboot, m.nodeConfig, m.UserDataMimeBoundary); err != nil { return "", err } return base64.StdEncoding.EncodeToString(buf.Bytes()), nil } -func (m *AL2023) makeNodeConfig() *NodeConfig { - nodeConfig := &NodeConfig{ +func (m *AL2023) setNodeConfig() { + m.nodeConfig = &NodeConfig{ ApiVersion: "node.eks.aws/v1alpha1", Kind: "NodeConfig", Spec: NodeSpec{ @@ -63,32 +84,25 @@ func (m *AL2023) makeNodeConfig() *NodeConfig { }, }, } - if m.ng.MaxPodsPerNode > 0 { - nodeConfig.Spec.Kubelet.Config.MaxPods = &m.ng.MaxPodsPerNode + m.nodeConfig.Spec.Kubelet.Config.MaxPods = &m.ng.MaxPodsPerNode } - if len(m.taints) > 0 { - nodeConfig.Spec.Kubelet.Flags = append(nodeConfig.Spec.Kubelet.Flags, "--register-with-taints="+utils.FormatTaints(m.taints)) + m.nodeConfig.Spec.Kubelet.Flags = append(m.nodeConfig.Spec.Kubelet.Flags, "--register-with-taints="+utils.FormatTaints(m.taints)) } - - return nodeConfig } -// NodeConfig represents the core EKS node configuration type NodeConfig struct { ApiVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` Spec NodeSpec `yaml:"spec"` } -// NodeSpec encapsulates the 'spec' section of the YAML type NodeSpec struct { Cluster ClusterSpec `yaml:"cluster"` Kubelet KubeletSpec `yaml:"kubelet"` } -// ClusterSpec holds cluster-related parameters type ClusterSpec struct { ApiServerEndpoint string `yaml:"apiServerEndpoint"` CertificateAuthority string `yaml:"certificateAuthority"` @@ -96,13 +110,11 @@ type ClusterSpec struct { Name string `yaml:"name"` } -// KubeletSpec captures Kubelet parameters and flags type KubeletSpec struct { Config KubeletConfig `yaml:"config"` Flags []string `yaml:"flags"` } -// KubeletConfig holds the 'config' section type KubeletConfig struct { MaxPods *int `yaml:"maxPods,omitempty"` ClusterDNS []string `yaml:"clusterDNS"` diff --git a/pkg/nodebootstrap/al2023_test.go b/pkg/nodebootstrap/al2023_test.go new file mode 100644 index 0000000000..bfe1016403 --- /dev/null +++ b/pkg/nodebootstrap/al2023_test.go @@ -0,0 +1,184 @@ +package nodebootstrap_test + +import ( + "encoding/base64" + "fmt" + "strings" + + "github.com/aws/aws-sdk-go-v2/aws" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5" + "github.com/weaveworks/eksctl/pkg/nodebootstrap" + "github.com/weaveworks/eksctl/pkg/nodebootstrap/assets" +) + +type al2023Entry struct { + overrideNodegroupSettings func(api.NodePool) + expectedUserData string +} + +var _ = DescribeTable("Unmanaged AL2023", func(e al2023Entry) { + cfg, dns := makeDefaultClusterSettings() + ng := api.NewNodeGroup() + makeDefaultNPSettings(ng) + + if e.overrideNodegroupSettings != nil { + e.overrideNodegroupSettings(ng) + } + + al2023BS := nodebootstrap.NewAL2023Bootstrapper(cfg, ng, dns) + al2023BS.UserDataMimeBoundary = "//" + + userData, err := al2023BS.UserData() + Expect(err).NotTo(HaveOccurred()) + decoded, err := base64.StdEncoding.DecodeString(userData) + Expect(err).NotTo(HaveOccurred()) + actual := strings.ReplaceAll(string(decoded), "\r\n", "\n") + Expect(actual).To(Equal(e.expectedUserData)) +}, + Entry("default", al2023Entry{ + expectedUserData: wrapMIMEParts(nodeConfig), + }), + Entry("efa enabled", al2023Entry{ + overrideNodegroupSettings: func(np api.NodePool) { + np.BaseNodeGroup().EFAEnabled = aws.Bool(true) + }, + expectedUserData: wrapMIMEParts(efaScript + nodeConfig), + }), +) + +var _ = DescribeTable("Managed AL2023", func(e al2023Entry) { + cfg, dns := makeDefaultClusterSettings() + mng := api.NewManagedNodeGroup() + makeDefaultNPSettings(mng) + mng.Taints = append(mng.Taints, api.NodeGroupTaint{ + Key: "special", + Value: "true", + Effect: "NoSchedule", + }) + + if e.overrideNodegroupSettings != nil { + e.overrideNodegroupSettings(mng) + } + + al2023ManagedBS := nodebootstrap.NewManagedAL2023Bootstrapper(cfg, mng, dns) + al2023ManagedBS.UserDataMimeBoundary = "//" + + userData, err := al2023ManagedBS.UserData() + Expect(err).NotTo(HaveOccurred()) + decoded, err := base64.StdEncoding.DecodeString(userData) + Expect(err).NotTo(HaveOccurred()) + actual := strings.ReplaceAll(string(decoded), "\r\n", "\n") + Expect(actual).To(Equal(e.expectedUserData)) +}, + Entry("native AMI", al2023Entry{ + expectedUserData: "", + }), + Entry("native AMI && EFA enabled", al2023Entry{ + overrideNodegroupSettings: func(np api.NodePool) { + np.BaseNodeGroup().EFAEnabled = aws.Bool(true) + }, + expectedUserData: wrapMIMEParts(efaCloudhook), + }), + Entry("custom AMI", al2023Entry{ + overrideNodegroupSettings: func(np api.NodePool) { + np.BaseNodeGroup().AMI = "ami-xxxx" + }, + expectedUserData: wrapMIMEParts(managedNodeConfig), + }), + Entry("custom AMI && EFA enabled", al2023Entry{ + overrideNodegroupSettings: func(np api.NodePool) { + np.BaseNodeGroup().AMI = "ami-xxxx" + np.BaseNodeGroup().EFAEnabled = aws.Bool(true) + }, + expectedUserData: wrapMIMEParts(efaCloudhook + managedNodeConfig), + }), +) + +var ( + makeDefaultClusterSettings = func() (*api.ClusterConfig, string) { + clusterConfig := api.NewClusterConfig() + clusterConfig.Metadata.Name = "al2023-test" + clusterConfig.Status = &api.ClusterStatus{ + Endpoint: "https://test.xxx.us-west-2.eks.amazonaws.com", + CertificateAuthorityData: []byte("test CA"), + KubernetesNetworkConfig: &api.KubernetesNetworkConfig{ + ServiceIPv4CIDR: "10.100.0.0/16", + }, + } + return clusterConfig, "10.100.0.10" + } + + makeDefaultNPSettings = func(np api.NodePool) { + baseNg := np.BaseNodeGroup() + baseNg.MaxPodsPerNode = 4 + baseNg.Labels = map[string]string{ + "alpha.eksctl.io/nodegroup-name": "al2023-mng-test", + } + } + + wrapMIMEParts = func(parts string) string { + return `MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary=// + +` + parts + `--//-- +` + } + + efaCloudhook = fmt.Sprintf(`--// +Content-Type: text/cloud-boothook +Content-Type: charset="us-ascii" + +%s +`, assets.EfaManagedAL2023Boothook) + + efaScript = fmt.Sprintf(`--// +Content-Type: text/x-shellscript +Content-Type: charset="us-ascii" + +%s +`, assets.EfaAl2023Sh) + + nodeConfig = `--// +Content-Type: application/node.eks.aws + +apiVersion: node.eks.aws/v1alpha1 +kind: NodeConfig +spec: + cluster: + apiServerEndpoint: https://test.xxx.us-west-2.eks.amazonaws.com + certificateAuthority: dGVzdCBDQQ== + cidr: 10.100.0.0/16 + name: al2023-test + kubelet: + config: + maxPods: 4 + clusterDNS: + - 10.100.0.10 + flags: + - --node-labels=alpha.eksctl.io/nodegroup-name=al2023-mng-test + +` + managedNodeConfig = `--// +Content-Type: application/node.eks.aws + +apiVersion: node.eks.aws/v1alpha1 +kind: NodeConfig +spec: + cluster: + apiServerEndpoint: https://test.xxx.us-west-2.eks.amazonaws.com + certificateAuthority: dGVzdCBDQQ== + cidr: 10.100.0.0/16 + name: al2023-test + kubelet: + config: + maxPods: 4 + clusterDNS: + - 10.100.0.10 + flags: + - --node-labels=alpha.eksctl.io/nodegroup-name=al2023-mng-test + - --register-with-taints=special=true:NoSchedule + +` +) diff --git a/pkg/nodebootstrap/assets/assets.go b/pkg/nodebootstrap/assets/assets.go index 4c6965618e..c77c5bc450 100644 --- a/pkg/nodebootstrap/assets/assets.go +++ b/pkg/nodebootstrap/assets/assets.go @@ -25,11 +25,21 @@ var BootstrapUbuntuSh string //go:embed scripts/efa.al2.sh var EfaAl2Sh string +// EfaAl2023Sh holds the efa.al2023.sh contents +// +//go:embed scripts/efa.al2023.sh +var EfaAl2023Sh string + // EfaManagedBoothook holds the efa.managed.boothook contents // //go:embed scripts/efa.managed.boothook var EfaManagedBoothook string +// EfaManagedAL2023Boothook holds the efa.managed.al2023.boothook contents +// +//go:embed scripts/efa.managed.al2023.boothook +var EfaManagedAL2023Boothook string + // InstallSsmAl2Sh holds the install-ssm.al2.sh contents // //go:embed scripts/install-ssm.al2.sh diff --git a/pkg/nodebootstrap/assets/scripts/efa.al2023.sh b/pkg/nodebootstrap/assets/scripts/efa.al2023.sh new file mode 100644 index 0000000000..3aef0ce36f --- /dev/null +++ b/pkg/nodebootstrap/assets/scripts/efa.al2023.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + +dnf install -y wget +wget -q --timeout=20 https://s3-us-west-2.amazonaws.com/aws-efa-installer/aws-efa-installer-latest.tar.gz -O /tmp/aws-efa-installer.tar.gz +tar -xf /tmp/aws-efa-installer.tar.gz -C /tmp +cd /tmp/aws-efa-installer +./efa_installer.sh -y -g +/opt/amazon/efa/bin/fi_info -p efa diff --git a/pkg/nodebootstrap/assets/scripts/efa.managed.al2023.boothook b/pkg/nodebootstrap/assets/scripts/efa.managed.al2023.boothook new file mode 100644 index 0000000000..5d2a081688 --- /dev/null +++ b/pkg/nodebootstrap/assets/scripts/efa.managed.al2023.boothook @@ -0,0 +1,9 @@ +cloud-init-per once dnf_wget dnf install -y wget +cloud-init-per once wget_efa wget -q --timeout=20 https://s3-us-west-2.amazonaws.com/aws-efa-installer/aws-efa-installer-latest.tar.gz -O /tmp/aws-efa-installer-latest.tar.gz + +cloud-init-per once tar_efa tar -xf /tmp/aws-efa-installer-latest.tar.gz -C /tmp +pushd /tmp/aws-efa-installer +cloud-init-per once install_efa ./efa_installer.sh -y -g +pop /tmp/aws-efa-installer + +cloud-init-per once efa_info /opt/amazon/efa/bin/fi_info -p efa diff --git a/pkg/nodebootstrap/userdata.go b/pkg/nodebootstrap/userdata.go index 15adc20c33..e0cabc7f4a 100644 --- a/pkg/nodebootstrap/userdata.go +++ b/pkg/nodebootstrap/userdata.go @@ -75,7 +75,7 @@ func NewManagedBootstrapper(clusterConfig *api.ClusterConfig, ng *api.ManagedNod } switch ng.AMIFamily { case api.NodeImageFamilyAmazonLinux2023: - return NewAL2023Bootstrapper(clusterConfig, ng, clusterDNS), nil + return NewManagedAL2023Bootstrapper(clusterConfig, ng, clusterDNS), nil case api.NodeImageFamilyAmazonLinux2: return NewManagedAL2Bootstrapper(ng), nil case api.NodeImageFamilyBottlerocket: From 36fa4dbbb68849703df1cf6af23d143ac1909608 Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:14:03 +0200 Subject: [PATCH 06/11] improve validations --- pkg/apis/eksctl.io/v1alpha5/validation.go | 48 +++++++++++++++++-- .../eksctl.io/v1alpha5/validation_test.go | 30 ++++++++++++ pkg/ctl/cmdutils/nodegroup_flags.go | 2 +- 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/pkg/apis/eksctl.io/v1alpha5/validation.go b/pkg/apis/eksctl.io/v1alpha5/validation.go index f8ba37c2c7..a296abcb10 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation.go @@ -824,7 +824,10 @@ func ValidateNodeGroup(i int, ng *NodeGroup, cfg *ClusterConfig) error { ng.AMIFamily, path) } - if ng.AMI != "" && ng.OverrideBootstrapCommand == nil && ng.AMIFamily != NodeImageFamilyBottlerocket && !IsWindowsImage(ng.AMIFamily) { + if ng.AMI != "" && ng.OverrideBootstrapCommand == nil && + ng.AMIFamily != NodeImageFamilyAmazonLinux2023 && + ng.AMIFamily != NodeImageFamilyBottlerocket && + !IsWindowsImage(ng.AMIFamily) { return errors.Errorf("%[1]s.overrideBootstrapCommand is required when using a custom AMI based on %s (%[1]s.ami)", path, ng.AMIFamily) } @@ -854,6 +857,16 @@ func ValidateNodeGroup(i int, ng *NodeGroup, cfg *ClusterConfig) error { if ng.KubeletExtraConfig != nil { return fieldNotSupported("kubeletExtraConfig") } + } else if ng.AMIFamily == NodeImageFamilyAmazonLinux2023 { + if ng.KubeletExtraConfig != nil { + return fieldNotSupported("kubeletExtraConfig") + } + if ng.PreBootstrapCommands != nil { + return fieldNotSupported("preBootstrapCommands") + } + if ng.OverrideBootstrapCommand != nil { + return fieldNotSupported("overrideBootstrapCommand") + } } else if ng.AMIFamily == NodeImageFamilyBottlerocket { if ng.KubeletExtraConfig != nil { return fieldNotSupported("kubeletExtraConfig") @@ -1249,12 +1262,16 @@ func ValidateManagedNodeGroup(index int, ng *ManagedNodeGroup) error { if ng.AMIFamily == "" { return errors.Errorf("when using a custom AMI, amiFamily needs to be explicitly set via config file or via --node-ami-family flag") } - if ng.AMIFamily != NodeImageFamilyAmazonLinux2 && ng.AMIFamily != NodeImageFamilyUbuntu1804 && ng.AMIFamily != NodeImageFamilyUbuntu2004 && ng.AMIFamily != NodeImageFamilyUbuntu2204 { - return errors.Errorf("cannot set amiFamily to %s when using a custom AMI for managed nodes, only %s, %s, %s and %s are supported", ng.AMIFamily, NodeImageFamilyAmazonLinux2, NodeImageFamilyUbuntu1804, NodeImageFamilyUbuntu2004, NodeImageFamilyUbuntu2204) + if !IsAmazonLinuxImage(ng.AMIFamily) && !IsUbuntuImage(ng.AMIFamily) { + return errors.Errorf("cannot set amiFamily to %s when using a custom AMI for managed nodes, only %s, %s, %s, %s and %s are supported", ng.AMIFamily, + NodeImageFamilyAmazonLinux2023, NodeImageFamilyAmazonLinux2, NodeImageFamilyUbuntu1804, NodeImageFamilyUbuntu2004, NodeImageFamilyUbuntu2204) } - if ng.OverrideBootstrapCommand == nil { + if ng.OverrideBootstrapCommand == nil && ng.AMIFamily != NodeImageFamilyAmazonLinux2023 { return errors.Errorf("%[1]s.overrideBootstrapCommand is required when using a custom AMI based on %s (%[1]s.ami)", path, ng.AMIFamily) } + if ng.OverrideBootstrapCommand != nil && ng.AMIFamily == NodeImageFamilyAmazonLinux2023 { + return errors.Errorf("%[1]s.overrideBootstrapCommand is not supported when using a custom AMI based on %s (%[1]s.ami)", path, ng.AMIFamily) + } notSupportedWithCustomAMIErr := func(field string) error { return errors.Errorf("%s.%s is not supported when using a custom AMI (%s.ami)", path, field, path) } @@ -1454,6 +1471,29 @@ func isSupportedAMIFamily(imageFamily string) bool { return false } +func IsAmazonLinuxImage(imageFamily string) bool { + switch imageFamily { + case NodeImageFamilyAmazonLinux2023, + NodeImageFamilyAmazonLinux2: + return true + + default: + return false + } +} + +func IsUbuntuImage(imageFamily string) bool { + switch imageFamily { + case NodeImageFamilyUbuntu2204, + NodeImageFamilyUbuntu2004, + NodeImageFamilyUbuntu1804: + return true + + default: + return false + } +} + // IsWindowsImage reports whether the AMI family is for Windows func IsWindowsImage(imageFamily string) bool { switch imageFamily { diff --git a/pkg/apis/eksctl.io/v1alpha5/validation_test.go b/pkg/apis/eksctl.io/v1alpha5/validation_test.go index a7838b1888..8e1bada38d 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation_test.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation_test.go @@ -171,6 +171,14 @@ var _ = Describe("ClusterConfig validation", func() { errMsg := fmt.Sprintf("overrideBootstrapCommand is required when using a custom AMI based on %s", ng0.AMIFamily) Expect(api.ValidateNodeGroup(0, ng0, cfg)).To(MatchError(ContainSubstring(errMsg))) }) + It("should not require overrideBootstrapCommand if ami is set and type is AmazonLinux2023", func() { + cfg := api.NewClusterConfig() + ng0 := cfg.NewNodeGroup() + ng0.Name = "node-group" + ng0.AMI = "ami-1234" + ng0.AMIFamily = api.NodeImageFamilyAmazonLinux2023 + Expect(api.ValidateNodeGroup(0, ng0, cfg)).To(Succeed()) + }) It("should not require overrideBootstrapCommand if ami is set and type is Bottlerocket", func() { cfg := api.NewClusterConfig() ng0 := cfg.NewNodeGroup() @@ -196,6 +204,24 @@ var _ = Describe("ClusterConfig validation", func() { ng0.OverrideBootstrapCommand = aws.String("echo 'yo'") Expect(api.ValidateNodeGroup(0, ng0, cfg)).To(Succeed()) }) + It("should throw an error if overrideBootstrapCommand is set and type is AmazonLinux2023", func() { + cfg := api.NewClusterConfig() + ng0 := cfg.NewNodeGroup() + ng0.Name = "node-group" + ng0.AMI = "ami-1234" + ng0.AMIFamily = api.NodeImageFamilyAmazonLinux2023 + ng0.OverrideBootstrapCommand = aws.String("echo 'yo'") + Expect(api.ValidateNodeGroup(0, ng0, cfg)).To(MatchError(ContainSubstring(fmt.Sprintf("overrideBootstrapCommand is not supported for %s nodegroups", api.NodeImageFamilyAmazonLinux2023)))) + }) + It("should throw an error if overrideBootstrapCommand is set and type is Bottlerocket", func() { + cfg := api.NewClusterConfig() + ng0 := cfg.NewNodeGroup() + ng0.Name = "node-group" + ng0.AMI = "ami-1234" + ng0.AMIFamily = api.NodeImageFamilyBottlerocket + ng0.OverrideBootstrapCommand = aws.String("echo 'yo'") + Expect(api.ValidateNodeGroup(0, ng0, cfg)).To(MatchError(ContainSubstring(fmt.Sprintf("overrideBootstrapCommand is not supported for %s nodegroups", api.NodeImageFamilyBottlerocket)))) + }) It("should accept ami with a overrideBootstrapCommand set", func() { cfg := api.NewClusterConfig() ng0 := cfg.NewNodeGroup() @@ -2029,6 +2055,10 @@ var _ = Describe("ClusterConfig validation", func() { err := api.ValidateManagedNodeGroup(0, mng) Expect(err).NotTo(HaveOccurred()) + mng.AMIFamily = api.NodeImageFamilyAmazonLinux2 + err = api.ValidateManagedNodeGroup(0, mng) + Expect(err).NotTo(HaveOccurred()) + mng.AMIFamily = api.NodeImageFamilyUbuntu1804 err = api.ValidateManagedNodeGroup(0, mng) Expect(err).NotTo(HaveOccurred()) diff --git a/pkg/ctl/cmdutils/nodegroup_flags.go b/pkg/ctl/cmdutils/nodegroup_flags.go index 4d93152834..fdb25e63d0 100644 --- a/pkg/ctl/cmdutils/nodegroup_flags.go +++ b/pkg/ctl/cmdutils/nodegroup_flags.go @@ -40,7 +40,7 @@ func AddCommonCreateNodeGroupFlags(fs *pflag.FlagSet, cmd *Cmd, ng *api.NodeGrou ng.SSH.EnableSSM = fs.Bool("enable-ssm", false, "Enable AWS Systems Manager (SSM)") fs.StringVar(&ng.AMI, "node-ami", "", "'auto-ssm', 'auto' or an AMI ID (advanced use)") - fs.StringVar(&ng.AMIFamily, "node-ami-family", api.DefaultNodeImageFamily, "'AmazonLinux2' for the Amazon EKS optimized AMI, or use 'Ubuntu2204', 'Ubuntu2004' or 'Ubuntu1804' for the official Canonical EKS AMIs") + fs.StringVar(&ng.AMIFamily, "node-ami-family", api.DefaultNodeImageFamily, fmt.Sprintf("supported AMI families: %s", strings.Join(api.SupportedNodeVolumeTypes(), ", "))) fs.BoolVarP(&ng.PrivateNetworking, "node-private-networking", "P", false, "whether to make nodegroup networking private") From 5e754c26cb32d6bf0671c5f386887a9e70d68772 Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Thu, 28 Mar 2024 18:54:09 +0200 Subject: [PATCH 07/11] fix lint and unit tests --- pkg/apis/eksctl.io/v1alpha5/validation_test.go | 11 ++++++----- pkg/cfn/builder/managed_nodegroup_test.go | 1 + pkg/ctl/cmdutils/nodegroup_flags.go | 2 +- pkg/nodebootstrap/al2023.go | 8 ++++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pkg/apis/eksctl.io/v1alpha5/validation_test.go b/pkg/apis/eksctl.io/v1alpha5/validation_test.go index 8e1bada38d..45eea4fa7f 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation_test.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation_test.go @@ -2055,10 +2055,6 @@ var _ = Describe("ClusterConfig validation", func() { err := api.ValidateManagedNodeGroup(0, mng) Expect(err).NotTo(HaveOccurred()) - mng.AMIFamily = api.NodeImageFamilyAmazonLinux2 - err = api.ValidateManagedNodeGroup(0, mng) - Expect(err).NotTo(HaveOccurred()) - mng.AMIFamily = api.NodeImageFamilyUbuntu1804 err = api.ValidateManagedNodeGroup(0, mng) Expect(err).NotTo(HaveOccurred()) @@ -2074,8 +2070,13 @@ var _ = Describe("ClusterConfig validation", func() { mng.AMIFamily = api.NodeImageFamilyBottlerocket mng.OverrideBootstrapCommand = nil err = api.ValidateManagedNodeGroup(0, mng) - errorMsg := fmt.Sprintf("cannot set amiFamily to %s when using a custom AMI for managed nodes, only %s, %s, %s and %s are supported", mng.AMIFamily, api.NodeImageFamilyAmazonLinux2, api.NodeImageFamilyUbuntu1804, api.NodeImageFamilyUbuntu2004, api.NodeImageFamilyUbuntu2204) + errorMsg := fmt.Sprintf("cannot set amiFamily to %s when using a custom AMI for managed nodes, only %s, %s, %s, %s and %s are supported", + mng.AMIFamily, api.NodeImageFamilyAmazonLinux2023, api.NodeImageFamilyAmazonLinux2, api.NodeImageFamilyUbuntu1804, api.NodeImageFamilyUbuntu2004, api.NodeImageFamilyUbuntu2204) Expect(err).To(MatchError(errorMsg)) + + mng.AMIFamily = api.NodeImageFamilyAmazonLinux2023 + err = api.ValidateManagedNodeGroup(0, mng) + Expect(err).NotTo(HaveOccurred()) }) It("fails when the AMIFamily is WindowsServer2004CoreContainer", func() { diff --git a/pkg/cfn/builder/managed_nodegroup_test.go b/pkg/cfn/builder/managed_nodegroup_test.go index dc0a8ab453..b0eefe8340 100644 --- a/pkg/cfn/builder/managed_nodegroup_test.go +++ b/pkg/cfn/builder/managed_nodegroup_test.go @@ -204,6 +204,7 @@ func TestManagedNodeRole(t *testing.T) { t.Run(fmt.Sprintf("%d: %s", i, tt.description), func(t *testing.T) { require := require.New(t) clusterConfig := api.NewClusterConfig() + clusterConfig.Status = &api.ClusterStatus{} api.SetManagedNodeGroupDefaults(tt.nodeGroup, clusterConfig.Metadata, false) p := mockprovider.NewMockProvider() fakeVPCImporter := new(vpcfakes.FakeImporter) diff --git a/pkg/ctl/cmdutils/nodegroup_flags.go b/pkg/ctl/cmdutils/nodegroup_flags.go index fdb25e63d0..211c6e3afc 100644 --- a/pkg/ctl/cmdutils/nodegroup_flags.go +++ b/pkg/ctl/cmdutils/nodegroup_flags.go @@ -40,7 +40,7 @@ func AddCommonCreateNodeGroupFlags(fs *pflag.FlagSet, cmd *Cmd, ng *api.NodeGrou ng.SSH.EnableSSM = fs.Bool("enable-ssm", false, "Enable AWS Systems Manager (SSM)") fs.StringVar(&ng.AMI, "node-ami", "", "'auto-ssm', 'auto' or an AMI ID (advanced use)") - fs.StringVar(&ng.AMIFamily, "node-ami-family", api.DefaultNodeImageFamily, fmt.Sprintf("supported AMI families: %s", strings.Join(api.SupportedNodeVolumeTypes(), ", "))) + fs.StringVar(&ng.AMIFamily, "node-ami-family", api.DefaultNodeImageFamily, fmt.Sprintf("supported AMI families: %s", strings.Join(api.SupportedAMIFamilies(), ", "))) fs.BoolVarP(&ng.PrivateNetworking, "node-private-networking", "P", false, "whether to make nodegroup networking private") diff --git a/pkg/nodebootstrap/al2023.go b/pkg/nodebootstrap/al2023.go index 3477ccb506..d5747b25d4 100644 --- a/pkg/nodebootstrap/al2023.go +++ b/pkg/nodebootstrap/al2023.go @@ -67,12 +67,12 @@ func (m *AL2023) UserData() (string, error) { func (m *AL2023) setNodeConfig() { m.nodeConfig = &NodeConfig{ - ApiVersion: "node.eks.aws/v1alpha1", + APIVersion: "node.eks.aws/v1alpha1", Kind: "NodeConfig", Spec: NodeSpec{ Cluster: ClusterSpec{ Name: m.cfg.Metadata.Name, - ApiServerEndpoint: m.cfg.Status.Endpoint, + APIServerEndpoint: m.cfg.Status.Endpoint, CertificateAuthority: base64.StdEncoding.EncodeToString(m.cfg.Status.CertificateAuthorityData), CIDR: m.cfg.Status.KubernetesNetworkConfig.ServiceIPv4CIDR, }, @@ -93,7 +93,7 @@ func (m *AL2023) setNodeConfig() { } type NodeConfig struct { - ApiVersion string `yaml:"apiVersion"` + APIVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` Spec NodeSpec `yaml:"spec"` } @@ -104,7 +104,7 @@ type NodeSpec struct { } type ClusterSpec struct { - ApiServerEndpoint string `yaml:"apiServerEndpoint"` + APIServerEndpoint string `yaml:"apiServerEndpoint"` CertificateAuthority string `yaml:"certificateAuthority"` CIDR string `yaml:"cidr"` Name string `yaml:"name"` From f5932a022c67713757e9458f5c0dd5d5c0e790ea Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Thu, 28 Mar 2024 18:54:20 +0200 Subject: [PATCH 08/11] update docs --- pkg/nodebootstrap/README.md | 33 ++++++++++++++++++++++++ userdocs/src/getting-started.md | 5 +++- userdocs/src/usage/arm-support.md | 5 +--- userdocs/src/usage/container-runtime.md | 4 +-- userdocs/src/usage/custom-ami-support.md | 4 ++- userdocs/theme/home.html | 3 ++- 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/pkg/nodebootstrap/README.md b/pkg/nodebootstrap/README.md index 845d74e6ad..37eef5f547 100644 --- a/pkg/nodebootstrap/README.md +++ b/pkg/nodebootstrap/README.md @@ -42,6 +42,39 @@ and then call `/etc/eks/bootstrap.sh`. For AL2, enabling either SSM or EFA will add `assets/install-ssm.al2.sh` or `assets/efa.al2.sh`. +### AmazonLinux2023 + +While AL2023 implements the `Bootstrapper` interface, the underlying userdata will be entirely different from other AMI families. Specifically, AL2023 introduces a new node initialization process nodeadm that uses a YAML configuration schema, dropping the use of `/etc/eks/bootstrap.sh` script. For self-managed nodes, and for EKS-managed nodes based on custom AMIs, eksctl will populate userdata in the fashion below: + +``` +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary=// + +--// +Content-Type: application/node.eks.aws + +apiVersion: node.eks.aws/v1alpha1 +kind: NodeConfig +spec: + cluster: + apiServerEndpoint: https://XXXX.us-west-2.eks.amazonaws.com + certificateAuthority: XXXX + cidr: 10.100.0.0/16 + name: my-cluster + kubelet: + config: + clusterDNS: + - 10.100.0.10 + flags: + - --node-labels=alpha.eksctl.io/cluster-name=my-cluster,alpha.eksctl.io/nodegroup-name=my-nodegroup + - --register-with-taints=special=true:NoSchedule (only for EKS-managed nodes) + +--//-- + +``` + +For EKS-managed nodes based on native AMIs, the userdata above is fulfilled automatically by the AWS SSM agent. + ## Troubleshooting ### Ubuntu diff --git a/userdocs/src/getting-started.md b/userdocs/src/getting-started.md index 8995f58eb1..4a11e2b5f7 100644 --- a/userdocs/src/getting-started.md +++ b/userdocs/src/getting-started.md @@ -1,6 +1,9 @@ # Getting started -!!! tip "New for 2023" +!!! tip "New for 2024" + `eksctl` now supports AMIs based on AmazonLinux2023 + +!!! tip "eksctl main features in 2023" `eksctl` now supports configuring cluster access management via [AWS EKS Access Entries](/usage/access-entries). `eksctl` now supports configuring fine-grained permissions to EKS running apps via [EKS Pod Identity Associations](/usage/pod-identity-associations) diff --git a/userdocs/src/usage/arm-support.md b/userdocs/src/usage/arm-support.md index d75957609d..e23df6492e 100644 --- a/userdocs/src/usage/arm-support.md +++ b/userdocs/src/usage/arm-support.md @@ -51,10 +51,7 @@ managedNodeGroups: eksctl create cluster -f cluster-arm-2.yaml ``` -The AMI resolvers, `auto` and `auto-ssm`, will infer the correct AMI based on the ARM instance type. - -???+ note - Note that currently there are only AmazonLinux2 EKS optimized AMIs for ARM. +The AMI resolvers, `auto` and `auto-ssm`, will infer the correct AMI based on the ARM instance type. Only AmazonLinux2023, AmazonLinux2 and Bottlerocket families have EKS optimized AMIs for ARM. ???+ note ARM is supported for clusters with version 1.15 and higher. diff --git a/userdocs/src/usage/container-runtime.md b/userdocs/src/usage/container-runtime.md index 4c9fa63ee8..1d07cd4251 100644 --- a/userdocs/src/usage/container-runtime.md +++ b/userdocs/src/usage/container-runtime.md @@ -1,8 +1,8 @@ # Define Container Runtime !!! warning - Starting with Kubernetes version `1.24`, dockershim support has been deprecated. Therefore, if you create a cluster using `eksctl` on version `1.24` or higher, the information below no longer applies, and the only supported container runtime is `containerd`. Trying to set it otherwise will return a validation error. - + Starting with Kubernetes version `1.24`, dockershim support has been deprecated. Therefore, if you create a cluster using `eksctl` on version `1.24` or higher, the information below no longer applies, and the only supported container runtime is `containerd`. Trying to set it otherwise will return a validation error. Additionally, AL2023 AMIs only support `containerd` regadless of K8s version. + At some point, we will completely remove the option to set `containerRuntime` in config file, together with the support for older Kubernetes versions support (i.e. `1.22` or `1.23`). For AL2 ( AmazonLinux2 ) and Windows AMIs, it's possible to set container runtime to `containerd`. diff --git a/userdocs/src/usage/custom-ami-support.md b/userdocs/src/usage/custom-ami-support.md index 5108599caa..d4ed34315b 100644 --- a/userdocs/src/usage/custom-ami-support.md +++ b/userdocs/src/usage/custom-ami-support.md @@ -16,6 +16,7 @@ The flag can take the AMI image id for an image to explicitly use. It also can t ???+ note When setting `--node-ami` to an ID string, `eksctl` will assume that a custom AMI has been requested. For AmazonLinux2 and Ubuntu nodes, both EKS managed and self-managed, this will mean that `overrideBootstrapCommand` is required. + For AmazonLinux2023, since it stops using the `/etc/eks/bootstrap.sh` script for node bootstrapping, in favour of a nodeadm initialization process (for more information, please refer to [node bootstrapping docs](https://github.com/eksctl-io/eksctl/blob/main/pkg/nodebootstrap/README.md)), `overrideBootstrapCommand` is not supported. CLI flag examples: ```sh @@ -55,6 +56,7 @@ The `--node-ami-family` can take following keywords: | Keyword | Description | |--------------------------------|:--------------------------------------------------------------------------------------------------------------:| | AmazonLinux2 | Indicates that the EKS AMI image based on Amazon Linux 2 should be used (default). | +| AmazonLinux2023 | Indicates that the EKS AMI image based on Amazon Linux 2023 should be used. | | Ubuntu2204 | Indicates that the EKS AMI image based on Ubuntu 22.04 LTS (Jammy) should be used (available for EKS >= 1.29). | | Ubuntu2004 | Indicates that the EKS AMI image based on Ubuntu 20.04 LTS (Focal) should be used (supported for EKS <= 1.29). | | Ubuntu1804 | Indicates that the EKS AMI image based on Ubuntu 18.04 LTS (Bionic) should be used. | @@ -84,7 +86,7 @@ managedNodeGroups: The `--node-ami-family` flag can also be used with `eksctl create nodegroup`. `eksctl` requires AMI Family to be explicitly set via config file or via `--node-ami-family` CLI flag, whenever working with a custom AMI. ???+ note - At the moment, EKS managed nodegroups only support the following AMI Families when working with custom AMIs: `AmazonLinux2`, `Ubuntu1804`, `Ubuntu2004` and `Ubuntu2204` + At the moment, EKS managed nodegroups only support the following AMI Families when working with custom AMIs: `AmazonLinux2023`, `AmazonLinux2`, `Ubuntu1804`, `Ubuntu2004` and `Ubuntu2204` ## Windows custom AMI support Only self-managed Windows nodegroups can specify a custom AMI. `amiFamily` should be set to a valid Windows AMI family. diff --git a/userdocs/theme/home.html b/userdocs/theme/home.html index 83b76bcd6f..6bd0a3e3b5 100644 --- a/userdocs/theme/home.html +++ b/userdocs/theme/home.html @@ -532,7 +532,8 @@

eksctl create cluster

Usage Outposts
-

New for {{ build_date_utc.strftime('%Y') }}

+

Check out latest eksctl features

+

Support for AMIs based on AmazonLinux2023

Configuring cluster access management via AWS EKS Access Entries.

Configuring fine-grained permissions to EKS running apps via EKS Pod Identity Associations.

Creating fully private clusters on AWS Outposts.

From de74c5bafe288a30d090d9fc22672a195b0b4b70 Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Thu, 28 Mar 2024 19:38:39 +0200 Subject: [PATCH 09/11] add validation error for maxpods limitation --- pkg/apis/eksctl.io/v1alpha5/validation.go | 4 ++++ pkg/apis/eksctl.io/v1alpha5/validation_test.go | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/pkg/apis/eksctl.io/v1alpha5/validation.go b/pkg/apis/eksctl.io/v1alpha5/validation.go index a296abcb10..b49144ba7a 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation.go @@ -1187,6 +1187,10 @@ func ValidateManagedNodeGroup(index int, ng *ManagedNodeGroup) error { } } + if ng.AMIFamily == NodeImageFamilyAmazonLinux2023 && ng.MaxPodsPerNode > 0 { + return errors.Errorf("eksctl does not support configuring maxPodsPerNode EKS-managed nodes based on %s", NodeImageFamilyAmazonLinux2023) + } + if ng.AMIFamily == NodeImageFamilyBottlerocket { fieldNotSupported := func(field string) error { return &unsupportedFieldError{ diff --git a/pkg/apis/eksctl.io/v1alpha5/validation_test.go b/pkg/apis/eksctl.io/v1alpha5/validation_test.go index 45eea4fa7f..cecec152d1 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation_test.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation_test.go @@ -2092,6 +2092,16 @@ var _ = Describe("ClusterConfig validation", func() { }) }) + Describe("AmazonLinux2023 node groups", func() { + It("returns an error when setting maxPodsPerNode for managed nodegroups", func() { + ng := api.NewManagedNodeGroup() + ng.AMIFamily = api.NodeImageFamilyAmazonLinux2023 + ng.MaxPodsPerNode = 5 + err := api.ValidateManagedNodeGroup(0, ng) + Expect(err).To(MatchError(ContainSubstring("eksctl does not support configuring maxPodsPerNode EKS-managed nodes"))) + }) + }) + Describe("Windows node groups", func() { It("returns an error with unsupported fields", func() { doc := api.InlineDocument{ From 0f3f294082f87cc82c211151ce02a2bd6bf133d0 Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Fri, 29 Mar 2024 14:43:31 +0200 Subject: [PATCH 10/11] add integration tests for al2023 --- .../tests/custom_ami/custom_ami_test.go | 33 +++++++++++++++++-- .../tests/custom_ami/testdata/al2023.yaml | 19 +++++++++++ .../tests/managed/managed_nodegroup_test.go | 27 +++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 integration/tests/custom_ami/testdata/al2023.yaml diff --git a/integration/tests/custom_ami/custom_ami_test.go b/integration/tests/custom_ami/custom_ami_test.go index 05d6125af3..c51e605895 100644 --- a/integration/tests/custom_ami/custom_ami_test.go +++ b/integration/tests/custom_ami/custom_ami_test.go @@ -38,6 +38,7 @@ func TestOverrideBootstrap(t *testing.T) { var ( customAMIAL2 string + customAMIAL2023 string customAMIBottlerocket string ) @@ -47,15 +48,23 @@ var _ = BeforeSuite(func() { // retrieve AL2 AMI input := &awsssm.GetParameterInput{ - Name: aws.String("/aws/service/eks/optimized-ami/1.22/amazon-linux-2/recommended/image_id"), + Name: aws.String(fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2/recommended/image_id", params.Version)), } output, err := ssm.GetParameter(context.Background(), input) Expect(err).NotTo(HaveOccurred()) customAMIAL2 = *output.Parameter.Value + // retrieve AL2023 AMI + input = &awsssm.GetParameterInput{ + Name: aws.String(fmt.Sprintf("/aws/service/eks/optimized-ami/%s/amazon-linux-2023/x86_64/standard/recommended/image_id", params.Version)), + } + output, err = ssm.GetParameter(context.Background(), input) + Expect(err).NotTo(HaveOccurred()) + customAMIAL2023 = *output.Parameter.Value + // retrieve Bottlerocket AMI input = &awsssm.GetParameterInput{ - Name: aws.String("/aws/service/bottlerocket/aws-k8s-1.25/x86_64/latest/image_id"), + Name: aws.String(fmt.Sprintf("/aws/service/bottlerocket/aws-k8s-%s/x86_64/latest/image_id", params.Version)), } output, err = ssm.GetParameter(context.Background(), input) Expect(err).NotTo(HaveOccurred()) @@ -75,6 +84,26 @@ var _ = BeforeSuite(func() { var _ = Describe("(Integration) [Test Custom AMI]", func() { params.LogStacksEventsOnFailure() + Context("al2023 managed and un-managed nodegroups", func() { + It("can create working nodegroups which can join the cluster", func() { + By(fmt.Sprintf("using the following EKS optimised AMI: %s", customAMIAL2023)) + content, err := os.ReadFile(filepath.Join("testdata/al2023.yaml")) + Expect(err).NotTo(HaveOccurred()) + content = bytes.ReplaceAll(content, []byte(""), []byte(params.ClusterName)) + content = bytes.ReplaceAll(content, []byte(""), []byte(params.Region)) + content = bytes.ReplaceAll(content, []byte(""), []byte(customAMIAL2023)) + cmd := params.EksctlCreateCmd. + WithArgs( + "nodegroup", + "--config-file", "-", + "--verbose", "4", + ). + WithoutArg("--region", params.Region). + WithStdin(bytes.NewReader(content)) + Expect(cmd).To(RunSuccessfully()) + }) + }) + Context("override bootstrap command for managed and un-managed nodegroups", func() { It("can create a working nodegroup which can join the cluster", func() { diff --git a/integration/tests/custom_ami/testdata/al2023.yaml b/integration/tests/custom_ami/testdata/al2023.yaml new file mode 100644 index 0000000000..57f6738e63 --- /dev/null +++ b/integration/tests/custom_ami/testdata/al2023.yaml @@ -0,0 +1,19 @@ +apiVersion: eksctl.io/v1alpha5 +kind: ClusterConfig + +# name is generated +metadata: + name: + region: + +nodeGroups: + - name: unm-al2023 + ami: + amiFamily: AmazonLinux2023 + desiredCapacity: 1 + +managedNodeGroups: + - name: mng-al2023 + ami: + amiFamily: AmazonLinux2023 + desiredCapacity: 1 diff --git a/integration/tests/managed/managed_nodegroup_test.go b/integration/tests/managed/managed_nodegroup_test.go index e4588875ad..a65a628194 100644 --- a/integration/tests/managed/managed_nodegroup_test.go +++ b/integration/tests/managed/managed_nodegroup_test.go @@ -70,6 +70,7 @@ var _ = Describe("(Integration) Create Managed Nodegroups", func() { const ( updateConfigNodegroup = "ng-update-config" + al2023Nodegroup = "ng-al2023" bottlerocketNodegroup = "ng-bottlerocket" bottlerocketGPUNodegroup = "ng-bottlerocket-gpu" ubuntuNodegroup = "ng-ubuntu" @@ -302,6 +303,32 @@ var _ = Describe("(Integration) Create Managed Nodegroups", func() { checkNg(bottlerocketGPUNodegroup) }) + It("supports AmazonLinux2023 nodegroups", func() { + clusterConfig := makeClusterConfig() + clusterConfig.ManagedNodeGroups = []*api.ManagedNodeGroup{ + { + NodeGroupBase: &api.NodeGroupBase{ + Name: al2023Nodegroup, + AMIFamily: "AmazonLinux2023", + }, + }, + } + + By("creating it") + Expect(params.EksctlCreateCmd. + WithArgs( + "nodegroup", + "--config-file", "-", + "--verbose", "4", + ). + WithoutArg("--region", params.Region). + WithStdin(clusterutils.Reader(clusterConfig))). + To(RunSuccessfully()) + + By("ensuring it is healthy") + checkNg(al2023Nodegroup) + }) + It("supports bottlerocket and ubuntu nodegroups with additional volumes", func() { clusterConfig := makeClusterConfig() clusterConfig.ManagedNodeGroups = []*api.ManagedNodeGroup{ From 8965847e273bcac4ac2044707e784fb20ad57007 Mon Sep 17 00:00:00 2001 From: Tibi <110664232+TiberiuGC@users.noreply.github.com> Date: Tue, 2 Apr 2024 08:06:50 +0300 Subject: [PATCH 11/11] improve validation message --- pkg/apis/eksctl.io/v1alpha5/validation.go | 19 +++++++++++++++++-- .../eksctl.io/v1alpha5/validation_test.go | 4 ++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/pkg/apis/eksctl.io/v1alpha5/validation.go b/pkg/apis/eksctl.io/v1alpha5/validation.go index b49144ba7a..0b3b0000f3 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation.go @@ -62,6 +62,11 @@ var ( } ) +var ( + SupportedAmazonLinuxImages = supportedAMIFamiliesForOS(IsAmazonLinuxImage) + SupportedUbuntuImages = supportedAMIFamiliesForOS(IsUbuntuImage) +) + // NOTE: we don't use k8s.io/apimachinery/pkg/util/sets here to keep API package free of dependencies type nameSet map[string]struct{} @@ -1267,8 +1272,8 @@ func ValidateManagedNodeGroup(index int, ng *ManagedNodeGroup) error { return errors.Errorf("when using a custom AMI, amiFamily needs to be explicitly set via config file or via --node-ami-family flag") } if !IsAmazonLinuxImage(ng.AMIFamily) && !IsUbuntuImage(ng.AMIFamily) { - return errors.Errorf("cannot set amiFamily to %s when using a custom AMI for managed nodes, only %s, %s, %s, %s and %s are supported", ng.AMIFamily, - NodeImageFamilyAmazonLinux2023, NodeImageFamilyAmazonLinux2, NodeImageFamilyUbuntu1804, NodeImageFamilyUbuntu2004, NodeImageFamilyUbuntu2204) + return errors.Errorf("cannot set amiFamily to %s when using a custom AMI for managed nodes, only %s are supported", ng.AMIFamily, + strings.Join(append(SupportedAmazonLinuxImages, SupportedUbuntuImages...), ", ")) } if ng.OverrideBootstrapCommand == nil && ng.AMIFamily != NodeImageFamilyAmazonLinux2023 { return errors.Errorf("%[1]s.overrideBootstrapCommand is required when using a custom AMI based on %s (%[1]s.ami)", path, ng.AMIFamily) @@ -1475,6 +1480,16 @@ func isSupportedAMIFamily(imageFamily string) bool { return false } +func supportedAMIFamiliesForOS(isOSImage func(string) bool) []string { + amiFamilies := []string{} + for _, image := range SupportedAMIFamilies() { + if isOSImage(image) { + amiFamilies = append(amiFamilies, image) + } + } + return amiFamilies +} + func IsAmazonLinuxImage(imageFamily string) bool { switch imageFamily { case NodeImageFamilyAmazonLinux2023, diff --git a/pkg/apis/eksctl.io/v1alpha5/validation_test.go b/pkg/apis/eksctl.io/v1alpha5/validation_test.go index cecec152d1..6f76855032 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation_test.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation_test.go @@ -2070,8 +2070,8 @@ var _ = Describe("ClusterConfig validation", func() { mng.AMIFamily = api.NodeImageFamilyBottlerocket mng.OverrideBootstrapCommand = nil err = api.ValidateManagedNodeGroup(0, mng) - errorMsg := fmt.Sprintf("cannot set amiFamily to %s when using a custom AMI for managed nodes, only %s, %s, %s, %s and %s are supported", - mng.AMIFamily, api.NodeImageFamilyAmazonLinux2023, api.NodeImageFamilyAmazonLinux2, api.NodeImageFamilyUbuntu1804, api.NodeImageFamilyUbuntu2004, api.NodeImageFamilyUbuntu2204) + errorMsg := fmt.Sprintf("cannot set amiFamily to %s when using a custom AMI for managed nodes, only %s are supported", mng.AMIFamily, + strings.Join(append(api.SupportedAmazonLinuxImages, api.SupportedUbuntuImages...), ", ")) Expect(err).To(MatchError(errorMsg)) mng.AMIFamily = api.NodeImageFamilyAmazonLinux2023