Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive ยท Kubernetes

A Managed Cluster on Amazon EKS

Create a production-shaped EKS cluster with eksctl: managed node groups, IAM roles for service accounts, add-ons, access control, logging, cost and upgrades.

4 min read

  • Amazon EKS 1.21
  • eksctl 0.8x
  • Region ap-south-1
  • Managed node groups
  • IRSA

Running the Kubernetes control plane yourself means managing etcd, certificates, upgrades and availability. Amazon EKS runs the control plane for you across multiple Availability Zones; you provide worker nodes (EC2, Fargate) and workloads. eksctl, the official CLI, creates the cluster, VPC, IAM roles and node groups from one YAML file.

State of EKS on this date. EKS supports Kubernetes 1.21 as its newest version (1.22 is expected within weeks) with older versions on a roughly 14-month support cycle. The control plane costs about $0.10 per hour per cluster, plus the nodes and supporting resources (NAT gateways, load balancers, EBS volumes). Pin the Kubernetes version explicitly.

What eksctl builds

flowchart TB
  YAML[cluster.yaml] --> EKSCTL[eksctl]
  EKSCTL -->|CloudFormation stacks| AWS
  subgraph AWS[Region ap-south-1]
    CP[EKS control plane<br/>managed, multi-AZ, API endpoint]
    subgraph VPC[VPC 192.168.0.0/16]
      PUB[Public subnets x3<br/>NAT gateway load balancers]
      PRV[Private subnets x3]
      NG[Managed node group<br/>m5.large x3 in private subnets]
    end
    OIDC[IAM OIDC provider<br/>for IRSA]
  end
  CP <-->|ENIs in your subnets| NG
  PRV --> NG
  NG --> PODS[Your pods]
  PODS -->|IAM role for service account| AWSAPI[S3 DynamoDB Secrets Manager]
  KC[kubectl] -->|IAM authentication| CP

cluster.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: prod
  region: ap-south-1
  version: "1.21"
  tags: { owner: platform, env: prod }
availabilityZones: [ap-south-1a, ap-south-1b, ap-south-1c]
iam:
  withOIDC: true                        # needed for IAM roles for service accounts
vpc:
  nat: { gateway: HighlyAvailable }     # one NAT per AZ; use Single for cheaper labs
managedNodeGroups:
  - name: general
    instanceType: m5.large
    minSize: 3
    maxSize: 9
    desiredCapacity: 3
    volumeSize: 50
    volumeType: gp3
    privateNetworking: true
    labels: { role: general }
    iam:
      withAddonPolicies: { ebs: true, albIngress: false, cloudWatch: true }
    ssh: { allow: false }               # use SSM Session Manager instead
cloudWatch:
  clusterLogging:
    enableTypes: ["api", "audit", "authenticator", "controllerManager", "scheduler"]
    logRetentionInDays: 30
addons:
  - { name: vpc-cni, version: latest }
  - { name: coredns, version: latest }
  - { name: kube-proxy, version: latest }
eksctl create cluster -f cluster.yaml          # about 15 to 20 minutes
aws eks update-kubeconfig --name prod --region ap-south-1
kubectl get nodes -o wide
kubectl -n kube-system get pods
eksctl get cluster --region ap-south-1

Why these choices: nodes in private subnets (no public IPs), a NAT gateway per AZ for resilience, managed node groups (AWS handles AMI updates and graceful node replacement), gp3 volumes, control-plane audit logs for security investigation, and OIDC for per-pod permissions.

Access control

EKS authenticates with AWS IAM and authorises with Kubernetes RBAC. The creator of the cluster becomes system:masters, and that fact is not visible in any ConfigMap, so write it down. Add other people and roles through the aws-auth ConfigMap:

eksctl create iamidentitymapping --cluster prod --region ap-south-1 \
  --arn arn:aws:iam::111111111111:role/PlatformAdmins --group system:masters --username admin:{{SessionName}}
eksctl create iamidentitymapping --cluster prod --region ap-south-1 \
  --arn arn:aws:iam::111111111111:role/Developers --group shop-developers --username dev:{{SessionName}}
kubectl -n kube-system get configmap aws-auth -o yaml

Bind shop-developers to Roles in specific namespaces (see the RBAC post). A broken aws-auth can lock everyone out, so edit it carefully and keep an emergency admin role. Restrict the public API endpoint (vpc.clusterEndpoints: publicAccess with publicAccessCIDRs, or private access only).

Per-pod AWS permissions (IRSA)

eksctl create iamserviceaccount --cluster prod --region ap-south-1 --namespace shop --name web \
  --attach-policy-arn arn:aws:iam::111111111111:policy/shop-web-s3 --approve
kubectl -n shop describe sa web | grep -i role-arn

The pod gets temporary AWS credentials for just that role, with no node-wide instance permissions. Do not give node roles broad application permissions.

Storage, ingress and autoscaling

  • Storage: install the Amazon EBS CSI driver for persistent volumes (needed on newer Kubernetes versions; create the IAM role through IRSA).
  • Ingress: the AWS Load Balancer Controller creates ALBs from Ingress objects and NLBs from Services.
  • Autoscaling pods: metrics-server plus HPAs. Nodes: Cluster Autoscaler (tags on the node group) or Karpenter.
  • Install the monitoring stack and cert-manager as described in the earlier posts.

Cost controls

  • NAT gateways and idle nodes dominate costs of small clusters; use Spot node groups for fault-tolerant workloads and right-size requests.
  • Set budgets and tag resources (owner, env).
  • Delete lab clusters: eksctl delete cluster -f cluster.yaml (removes load balancers created by Services first, else deletion hangs on dependent ENIs).

Upgrades

EKS upgrades are in-place and one minor version at a time:

eksctl upgrade cluster -f cluster.yaml --approve               # control plane (after updating `version`)
eksctl upgrade nodegroup --name general --cluster prod --kubernetes-version 1.22   # or roll a new node group
eksctl utils update-kube-proxy --cluster prod --approve

Check for removed APIs first (see the 1.22 removals post), update add-ons, then nodes. The control plane is typically upgraded before the nodes, and nodes may lag by up to two minor versions, but do not leave them behind for long.

Checklist before production

  • Private nodes, restricted API endpoint, audit logs on
  • Secrets encryption with a KMS key (secretsEncryption in the config)
  • IRSA for workloads, no broad node permissions
  • Network policy engine (Calico or Cilium) and Pod Security labels
  • Backups (Velero or GitOps for manifests, snapshots for volumes)
  • Alerts, dashboards and an upgrade calendar