EKS Cluster Setup

Creating an EKS cluster involves setting up the AWS infrastructure (VPC, IAM roles), creating the cluster control plane, configuring worker nodes, and connecting your local kubectl to the cluster. This guide covers the complete setup process from prerequisites to deploying your first application.

Prerequisites

Before creating an EKS cluster, ensure you have:

AWS Account Requirements

  • AWS Account - Active AWS account with appropriate permissions
  • IAM Permissions - Ability to create IAM roles, VPCs, and EKS clusters
  • Service Quotas - Sufficient service quotas for EC2 instances, VPCs, and EKS clusters
  • Region Selection - Choose an AWS region where EKS is available

Local Tools

Install these tools on your local machine:

kubectl:

# macOS
brew install kubectl

# Linux
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

# Verify installation
kubectl version --client

AWS CLI:

# macOS
brew install awscli

# Linux
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Verify installation
aws --version

eksctl (Recommended):

# macOS/Linux
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin

# Verify installation
eksctl version

AWS CLI Authentication:

# Configure AWS credentials
aws configure

# Or use environment variables
export AWS_ACCESS_KEY_ID=your-access-key
export AWS_SECRET_ACCESS_KEY=your-secret-key
export AWS_DEFAULT_REGION=us-west-2

Understanding EKS Components

Before creating a cluster, understand what gets created:

graph TB subgraph aws_resources[AWS Resources Created] EKS[EKS Cluster] --> CP[Control Plane] EKS --> VPC[VPC] EKS --> SG[Security Groups] EKS --> IAM1[Cluster IAM Role] EKS --> IAM2[Node IAM Role] EKS --> OIDC[OIDC Provider] end subgraph node_resources[Node Resources] NG[Node Group] --> ASG[Auto Scaling Group] NG --> LT[Launch Template] NG --> EC2[EC2 Instances] end EKS --> NG style EKS fill:#e1f5ff style CP fill:#fff4e1 style NG fill:#e8f5e9

EKS Cluster:

  • Control plane managed by AWS
  • API endpoint for cluster access
  • Cluster configuration and version

VPC and Networking:

  • VPC for cluster isolation
  • Subnets across availability zones
  • Internet gateway or NAT gateway for internet access
  • Route tables for traffic routing

IAM Roles:

  • Cluster Role - Permissions for EKS service to manage control plane
  • Node Role - Permissions for worker nodes to access AWS services

OIDC Provider:

  • Enables IAM Roles for Service Accounts (IRSA)
  • Allows pods to assume IAM roles

Node Group:

  • Auto Scaling Group for worker nodes
  • Launch Template defining instance configuration
  • EC2 instances running Kubernetes node components

Creating a Cluster

There are three main ways to create an EKS cluster:

eksctl is the official CLI tool for EKS that simplifies cluster creation:

Simple Cluster Creation:

# Create cluster with default settings
eksctl create cluster \
  --name my-cluster \
  --region us-west-2 \
  --nodegroup-name standard-workers \
  --node-type t3.medium \
  --nodes 3 \
  --nodes-min 1 \
  --nodes-max 4

This single command creates:

  • EKS cluster
  • VPC with subnets
  • IAM roles
  • Node group with 3 nodes
  • kubeconfig configuration

Advanced Cluster Configuration:

# cluster-config.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: production-cluster
  region: us-west-2
  version: "1.28"

vpc:
  cidr: 10.0.0.0/16
  nat:
    gateway: HighlyAvailable

iam:
  withOIDC: true
  serviceAccounts:
    - metadata:
        name: aws-load-balancer-controller
        namespace: kube-system
      wellKnownPolicies:
        awsLoadBalancerController: true

managedNodeGroups:
  - name: general-workers
    instanceType: t3.large
    minSize: 2
    maxSize: 10
    desiredCapacity: 3
    volumeSize: 50
    labels:
      role: general
    taints:
      - key: dedicated
        value: general
        effect: NoSchedule

  - name: compute-workers
    instanceType: c5.xlarge
    minSize: 0
    maxSize: 20
    desiredCapacity: 2
    labels:
      role: compute
    spot: true

addons:
  - name: vpc-cni
  - name: coredns
  - name: kube-proxy
  - name: ebs-csi-driver

Create cluster from config:

eksctl create cluster -f cluster-config.yaml

Method 2: AWS Console

Creating via the AWS Console provides a visual interface:

  1. Navigate to EKS:

    • Go to AWS Console → EKS → Clusters → Create cluster
  2. Configure Cluster:

    • Cluster name
    • Kubernetes version
    • Cluster service role (IAM role for EKS service)
    • VPC and subnets
    • Security groups
    • Cluster endpoint access (public, private, or both)
    • Logging configuration
  3. Create Node Group:

    • After cluster creation, create a node group
    • Select instance types
    • Configure scaling
    • Set IAM role for nodes
  4. Configure kubectl:

    • Update kubeconfig: aws eks update-kubeconfig --name my-cluster --region us-west-2

Method 3: Terraform

For infrastructure as code, use Terraform:

# main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-west-2"
}

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 19.0"

  cluster_name    = "my-cluster"
  cluster_version = "1.28"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets

  cluster_endpoint_public_access  = true
  cluster_endpoint_private_access = true

  eks_managed_node_groups = {
    general = {
      min_size     = 1
      max_size     = 10
      desired_size = 3

      instance_types = ["t3.medium"]
      capacity_type  = "ON_DEMAND"
    }

    compute = {
      min_size     = 0
      max_size     = 20
      desired_size = 2

      instance_types = ["c5.xlarge"]
      capacity_type  = "SPOT"
    }
  }
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"

  name = "eks-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["us-west-2a", "us-west-2b", "us-west-2c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  enable_nat_gateway = true
  single_nat_gateway = false

  tags = {
    "kubernetes.io/cluster/my-cluster" = "shared"
  }
}

Apply with:

terraform init
terraform plan
terraform apply

Cluster Configuration Options

Kubernetes Version

Choose a Kubernetes version supported by EKS:

# List available versions
aws eks describe-addon-versions --addon-name vpc-cni --region us-west-2

# Create cluster with specific version
eksctl create cluster --name my-cluster --version 1.28

Version Considerations:

  • Use a recent stable version for new features
  • Check EKS version support lifecycle
  • Consider upgrade path when choosing version
  • Test version compatibility with your applications

VPC Configuration

EKS requires a VPC with specific configuration:

Subnet Requirements:

  • At least 2 subnets in different availability zones
  • Sufficient IP addresses for pods (VPC CNI uses VPC IPs)
  • Private subnets for nodes (recommended)
  • Public subnets for load balancers (if needed)

CIDR Planning:

graph TB VPC[VPC: 10.0.0.0/16] --> S1[Subnet 1: 10.0.1.0/24<br/>AZ-1] VPC --> S2[Subnet 2: 10.0.2.0/24<br/>AZ-2] VPC --> S3[Subnet 3: 10.0.3.0/24<br/>AZ-3] S1 --> N1[Nodes: 10.0.1.0/26] S1 --> P1[Pods: 10.0.1.64/26] style VPC fill:#e1f5ff style S1 fill:#fff4e1 style P1 fill:#e8f5e9

IP Address Planning:

  • Reserve IPs for nodes (1 per node)
  • Reserve IPs for pods (varies by instance type)
  • Reserve IPs for AWS services (load balancers, etc.)
  • Plan for growth and scaling

Cluster Endpoint Configuration

Control how the API server is accessed:

Public Endpoint:

  • Accessible from internet
  • Requires authentication
  • Simpler for development
  • Less secure for production

Private Endpoint:

  • Only accessible from VPC
  • More secure
  • Requires VPN or bastion host
  • Recommended for production

Both (Recommended):

  • Public for kubectl access
  • Private for pod-to-API communication
  • Best of both worlds
# Configure endpoint access
aws eks update-cluster-config \
  --name my-cluster \
  --region us-west-2 \
  --resources-vpc-config endpointPublicAccess=true,endpointPrivateAccess=true

Initial Node Group Setup

After creating the cluster, set up worker nodes:

Managed Node Groups

AWS manages the node lifecycle:

# Create managed node group
eksctl create nodegroup \
  --cluster my-cluster \
  --name general-workers \
  --node-type t3.large \
  --nodes 3 \
  --nodes-min 1 \
  --nodes-max 10 \
  --ssh-access \
  --ssh-public-key my-key

Benefits:

  • Automatic node updates
  • Automatic node replacement
  • Integrated with Auto Scaling Groups
  • Less operational overhead

Configuration Options:

  • Instance types and sizes
  • Minimum, maximum, and desired capacity
  • Spot instances for cost savings
  • Labels and taints
  • User data for node initialization

Self-Managed Node Groups

You manage the node lifecycle:

# Create launch template
aws ec2 create-launch-template \
  --launch-template-name eks-node-template \
  --launch-template-data file://node-userdata.sh

# Create Auto Scaling Group
aws autoscaling create-auto-scaling-group \
  --auto-scaling-group-name eks-nodes \
  --launch-template LaunchTemplateName=eks-node-template \
  --min-size 1 \
  --max-size 10 \
  --desired-capacity 3 \
  --vpc-zone-identifier subnet-xxx,subnet-yyy

When to Use:

  • Need custom AMIs
  • Require specific node configurations
  • Want full control over node lifecycle
  • Have existing Auto Scaling Group patterns

Cluster Authentication

Configure kubectl to access your cluster:

Update kubeconfig

# Using AWS CLI
aws eks update-kubeconfig \
  --name my-cluster \
  --region us-west-2

# Using eksctl
eksctl utils write-kubeconfig \
  --cluster my-cluster \
  --region us-west-2

This updates ~/.kube/config with cluster credentials.

Verify Access

# Test cluster access
kubectl get nodes

# Should show your worker nodes
NAME                          STATUS   ROLES    AGE   VERSION
ip-10-0-1-123.ec2.internal    Ready    <none>   5m    v1.28.0-eks-xxxxx
ip-10-0-2-456.ec2.internal    Ready    <none>   5m    v1.28.0-eks-xxxxx
ip-10-0-3-789.ec2.internal    Ready    <none>   5m    v1.28.0-eks-xxxxx

aws-auth ConfigMap

The aws-auth ConfigMap maps AWS IAM users/roles to Kubernetes users:

# View current config
kubectl get configmap aws-auth -n kube-system -o yaml

# Add IAM user/role to cluster
eksctl create iamidentitymapping \
  --cluster my-cluster \
  --arn arn:aws:iam::123456789012:user/john \
  --username john \
  --group system:masters

Common Mappings:

  • IAM users → Kubernetes users
  • IAM roles → Kubernetes users
  • Service accounts → IAM roles (IRSA)

Post-Setup Configuration

After cluster creation, configure essential components:

Install EKS Add-ons

# Install VPC CNI
aws eks create-addon \
  --cluster-name my-cluster \
  --addon-name vpc-cni \
  --addon-version latest

# Install CoreDNS
aws eks create-addon \
  --cluster-name my-cluster \
  --addon-name coredns \
  --addon-version latest

# Install kube-proxy
aws eks create-addon \
  --cluster-name my-cluster \
  --addon-name kube-proxy \
  --addon-version latest

# Install EBS CSI driver
aws eks create-addon \
  --cluster-name my-cluster \
  --addon-name aws-ebs-csi-driver \
  --addon-version latest

Configure OIDC Provider

Enable IAM Roles for Service Accounts:

# Create OIDC provider
eksctl utils associate-iam-oidc-provider \
  --cluster my-cluster \
  --approve

Set Up Monitoring

# Enable CloudWatch Container Insights
aws eks update-cluster-config \
  --name my-cluster \
  --logging '{"enable":[{"types":["api","audit","authenticator","controllerManager","scheduler"]}]}'

Tagging and Resource Management

Proper tagging helps with cost management and organization:

# Tag cluster
aws eks tag-resource \
  --resource-arn arn:aws:eks:us-west-2:123456789012:cluster/my-cluster \
  --tags Environment=Production,Team=Platform,CostCenter=Engineering

# Tag node group
aws eks update-nodegroup-config \
  --cluster-name my-cluster \
  --nodegroup-name general-workers \
  --labels Environment=Production,Team=Platform

Recommended Tags:

  • Environment (dev, staging, prod)
  • Team/Department
  • Cost Center
  • Application
  • Managed By (eksctl, terraform, etc.)

Best Practices

  1. Use eksctl or Terraform - Avoid manual Console creation for reproducibility

  2. Plan VPC CIDR Carefully - Ensure sufficient IP space for pods and growth

  3. Use Private Subnets for Nodes - More secure, use NAT gateway for outbound access

  4. Enable OIDC Provider - Required for IRSA and modern security patterns

  5. Configure Cluster Logging - Enable audit logs for security and troubleshooting

  6. Use Managed Node Groups - Less operational overhead unless you need custom configs

  7. Set Appropriate Scaling Limits - Configure min/max to prevent cost overruns

  8. Tag Everything - Helps with cost allocation and resource management

  9. Test in Non-Production First - Validate configuration before production

  10. Document Your Setup - Keep track of configuration decisions and rationale

Common Issues

Insufficient IP Addresses

Problem: Pods can’t get IP addresses

Solution:

  • Increase subnet CIDR size
  • Use secondary CIDR ranges
  • Consider using custom networking mode

Node Group Creation Fails

Problem: Node group creation times out or fails

Solution:

  • Check IAM role permissions
  • Verify security group rules
  • Check subnet configuration
  • Review CloudWatch logs

kubectl Access Denied

Problem: Can’t access cluster with kubectl

Solution:

  • Verify kubeconfig is updated
  • Check IAM user/role permissions
  • Verify aws-auth ConfigMap includes your user
  • Check cluster endpoint access configuration

Next Steps

After cluster setup:

See Also