{"id":55127,"date":"2022-07-05T22:30:01","date_gmt":"2022-07-05T17:00:01","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=55127"},"modified":"2022-07-08T17:03:00","modified_gmt":"2022-07-08T11:33:00","slug":"implementation-of-aws-eks-node-group-using-terraform","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/implementation-of-aws-eks-node-group-using-terraform\/","title":{"rendered":"Implementation of AWS EKS Node Group Using Terraform"},"content":{"rendered":"<p><b>Implementation of AWS EKS Node Group Using Terraform<\/b><\/p>\n<p><span style=\"font-weight: 400\">Manages an EKS Node Group, which can provision and optionally update an Auto Scaling Group of Kubernetes worker nodes compatible with EKS.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Amazon EKS managed node groups automate the provisioning and lifecycle management of nodes (Amazon EC2 instances) for Amazon EKS Kubernetes clusters.<\/span><\/p>\n<p><span style=\"font-weight: 400\">With Amazon EKS managed node groups, you don\u2019t need to separately provision or register the Amazon EC2 instances that provide compute capacity to run your Kubernetes applications. You can create, automatically update, or terminate nodes for your cluster with a single operation. Node updates and terminations automatically drain nodes to ensure that your applications stay available.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Every managed node is provisioned as part of an Amazon EC2 Auto Scaling group that&#8217;s managed for you by Amazon EKS. Every resource including the instances and Auto Scaling groups runs within your AWS account. Each node group runs across multiple Availability Zones that you define.<\/span><\/p>\n<p><b>Managed node group capacity types<\/b><\/p>\n<h4><b>On-Demand<\/b><\/h4>\n<p><span style=\"font-weight: 400\">With On-Demand Instances, you pay for compute capacity by the second, with no long-term commitments.<\/span><\/p>\n<h4><b>Spot<\/b><\/h4>\n<p><span style=\"font-weight: 400\">Amazon EC2 Spot Instances are spare Amazon EC2 capacity that offers steep discounts off of On-Demand prices. Amazon EC2 Spot Instances can be interrupted with a two-minute interruption notice when EC2 needs the capacity back. For more information, see Spot Instances in the Amazon EC2 User Guide for Linux Instances. You can configure a managed node group with Amazon EC2 Spot Instances to optimize costs for the compute nodes running in your Amazon EKS cluster.<\/span><\/p>\n<h4><b>Example Usage<\/b><\/h4>\n<pre><span style=\"font-weight: 400\">resource \"aws_eks_node_group\" \"example\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0cluster_name\u00a0 \u00a0 = aws_eks_cluster.example.name<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0node_group_name = \"example\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0node_role_arn \u00a0 = aws_iam_role.example.arn<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0subnet_ids\u00a0 \u00a0 \u00a0 = aws_subnet.example[*].id<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0scaling_config {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0desired_size = 1<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0max_size \u00a0 \u00a0 = 1<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0min_size \u00a0 \u00a0 = 1<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0update_config {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0max_unavailable = 2<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0# Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling.<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0# Otherwise, EKS will not be able to properly delete EC2 Instances and Elastic Network Interfaces.<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0depends_on = [<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0aws_iam_role_policy_attachment.example-AmazonEKSWorkerNodePolicy,<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0aws_iam_role_policy_attachment.example-AmazonEKS_CNI_Policy,<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0aws_iam_role_policy_attachment.example-AmazonEC2ContainerRegistryReadOnly,<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0]<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span><\/pre>\n<h4><b>Code used in an existing project.<\/b><\/h4>\n<pre><span style=\"font-weight: 400\">resource \"aws_eks_node_group\" \"aws_eks_node_group\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0cluster_name\u00a0 \u00a0 =\"${var.environment}_${var.cluster_name}\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0node_group_name = \"${var.environment}-worker\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0node_role_arn \u00a0 = aws_iam_role.eks_worker_role.arn<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0subnet_ids\u00a0 \u00a0 \u00a0 = \"${concat(var.private_subnets)}\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0scaling_config {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0desired_size = 2<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0max_size \u00a0 \u00a0 = 3<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0min_size \u00a0 \u00a0 = 1<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0update_config {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0max_unavailable = 2<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0tags = {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"Name\" \u00a0 = \"example abc\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"SecurityClass\" = \"example abc\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"Customers\" = \"example abc\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"ServiceLevel\" =\"example abc\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"Billable\" = \"example abc\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"RemedyGroup\" = \"example abc\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"Function\" = \"example abc\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"ProductCode\" = \"example abc\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0# Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling.<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0# Otherwise, EKS will not be able to properly delete EC2 Instances and Elastic Network Interfaces.<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0depends_on = [<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0aws_iam_role_policy_attachment.AmazonEKSWorkerNodePolicy,<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0aws_iam_role_policy_attachment.AmazonEKS_CNI_Policy,<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0aws_iam_role_policy_attachment.AmazonEC2ContainerRegistryReadOnly,<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0]<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span><\/pre>\n<h3><b>Ignoring Changes to Desired Size<\/b><\/h3>\n<p><span style=\"font-weight: 400\">You can utilize the generic Terraform resource <\/span><a href=\"https:\/\/www.terraform.io\/docs\/configuration\/meta-arguments\/lifecycle.html\"><span style=\"font-weight: 400\">lifecycle configuration block<\/span><\/a><span style=\"font-weight: 400\"> with <\/span><span style=\"font-weight: 400\">ignore_changes<\/span><span style=\"font-weight: 400\"> to create an EKS Node Group with an initial size of running instances, then ignore any changes to that count caused externally (e.g., Application Autoscaling).<\/span><\/p>\n<pre><span style=\"font-weight: 400\">resource \"aws_eks_node_group\" \"example\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0# ... other configurations ...<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0scaling_config {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0# Example: Create EKS Node Group with 2 instances to start<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0desired_size = 2<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0# ... other configurations ...<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0# Optional: Allow external changes without Terraform plan difference<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0lifecycle {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0ignore_changes = [scaling_config[0].desired_size]<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span><\/pre>\n<h3><b>Example IAM Role for EKS Node Group<\/b><\/h3>\n<pre><span style=\"font-weight: 400\">resource \"aws_iam_role\" \"example\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0name = \"eks-node-group-example\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0assume_role_policy = jsonencode({<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0Statement = [{<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Action = \"sts:AssumeRole\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Effect = \"Allow\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Principal = {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Service = \"ec2.amazonaws.com\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0}]<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0Version = \"2012-10-17\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0})<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span>\r\n\r\n<span style=\"font-weight: 400\">resource \"aws_iam_role_policy_attachment\" \"example-AmazonEKSWorkerNodePolicy\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0policy_arn = \"arn:aws:iam::aws:policy\/AmazonEKSWorkerNodePolicy\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0role \u00a0 \u00a0 \u00a0 = aws_iam_role.example.name<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span>\r\n\r\n<span style=\"font-weight: 400\">resource \"aws_iam_role_policy_attachment\" \"example-AmazonEKS_CNI_Policy\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0policy_arn = \"arn:aws:iam::aws:policy\/AmazonEKS_CNI_Policy\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0role \u00a0 \u00a0 \u00a0 = aws_iam_role.example.name<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span>\r\n\r\n<span style=\"font-weight: 400\">resource \"aws_iam_role_policy_attachment\" \"example-AmazonEC2ContainerRegistryReadOnly\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0policy_arn = \"arn:aws:iam::aws:policy\/AmazonEC2ContainerRegistryReadOnly\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0role \u00a0 \u00a0 \u00a0 = aws_iam_role.example.name<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span><\/pre>\n<h3><b>Example Subnets for EKS Node Group<\/b><\/h3>\n<pre><span style=\"font-weight: 400\">data \"aws_availability_zones\" \"available\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0state = \"available\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span>\r\n\r\n<span style=\"font-weight: 400\">resource \"aws_subnet\" \"example\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0count = 2<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0availability_zone = data.aws_availability_zones.available.names[count.index]<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0cidr_block\u00a0 \u00a0 \u00a0 \u00a0 = cidrsubnet(aws_vpc.example.cidr_block, 8, count.index)<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0vpc_id\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 = aws_vpc.example.id<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0tags = {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"kubernetes.io\/cluster\/${aws_eks_cluster.example.name}\" = \"shared\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span><\/pre>\n<pre><span style=\"font-weight: 400\">data \"aws_availability_zones\" \"available\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0state = \"available\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span>\r\n\r\n<span style=\"font-weight: 400\">resource \"aws_subnet\" \"example\" {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0count = 2<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0availability_zone = data.aws_availability_zones.available.names[count.index]<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0cidr_block\u00a0 \u00a0 \u00a0 \u00a0 = cidrsubnet(aws_vpc.example.cidr_block, 8, count.index)<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0vpc_id\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 = aws_vpc.example.id<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0tags = {<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0\u00a0\u00a0\"kubernetes.io\/cluster\/${aws_eks_cluster.example.name}\" = \"shared\"<\/span>\r\n\r\n<span style=\"font-weight: 400\">\u00a0\u00a0}<\/span>\r\n\r\n<span style=\"font-weight: 400\">}<\/span><\/pre>\n<p>&nbsp;<\/p>\n<h4><b>Import<\/b><\/h4>\n<p><span style=\"font-weight: 400\">EKS Node Groups can be imported using the cluster_name and node_group_name separated by a colon (:), e.g.,<\/span><\/p>\n<p><span style=\"font-weight: 400\">$ terraform import aws_eks_node_group.my_node_group my_cluster:my_node_group<\/span><\/p>\n<div class=\"ap-custom-wrapper\"><\/div><!--ap-custom-wrapper-->","protected":false},"excerpt":{"rendered":"<p>Implementation of AWS EKS Node Group Using Terraform Manages an EKS Node Group, which can provision and optionally update an Auto Scaling Group of Kubernetes worker nodes compatible with EKS. Amazon EKS managed node groups automate the provisioning and lifecycle management of nodes (Amazon EC2 instances) for Amazon EKS Kubernetes clusters. With Amazon EKS managed [&hellip;]<\/p>\n","protected":false},"author":1472,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":84},"categories":[2348],"tags":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/55127"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1472"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=55127"}],"version-history":[{"count":6,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/55127\/revisions"}],"predecessor-version":[{"id":55245,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/55127\/revisions\/55245"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=55127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=55127"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=55127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}