Skip to main content

EKS Cluster

The multi-region shopping mall platform deploys an Amazon EKS cluster in each region. It uses Kubernetes 1.29 and automatically provisions workload-optimized nodes through Karpenter.

Cluster Configuration

Cluster Specifications

Itemus-east-1us-west-2
Cluster Namemulti-region-mallmulti-region-mall
Kubernetes Version1.291.29
API EndpointPrivate + PublicPrivate + Public
Service CIDR172.20.0.0/16172.20.0.0/16
Pod NetworkingVPC CNIVPC CNI
Log Typesapi, audit, authenticator, controllerManager, scheduler

Terraform Configuration

resource "aws_eks_cluster" "main" {
name = var.cluster_name
version = var.cluster_version # "1.29"
role_arn = aws_iam_role.eks_cluster.arn

kubernetes_network_config {
service_ipv4_cidr = "172.20.0.0/16"
}

vpc_config {
subnet_ids = var.private_subnet_ids
endpoint_private_access = true
endpoint_public_access = true
}

enabled_cluster_log_types = [
"api",
"audit",
"authenticator",
"controllerManager",
"scheduler"
]

encryption_config {
provider {
key_arn = aws_kms_key.eks_secrets.arn
}
resources = ["secrets"]
}
}

Karpenter NodePools

Karpenter automatically provisions optimal EC2 instances based on workload requirements.

NodePool Configuration

General NodePool

The default NodePool for general-purpose workloads.

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: general
spec:
template:
metadata:
labels:
node-pool: general
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
requirements:
- key: karpenter.sh/capacity-type
operator: In
values:
- spot
- on-demand
- key: kubernetes.io/arch
operator: In
values:
- amd64
- arm64
- key: karpenter.k8s.aws/instance-family
operator: In
values:
- m6i
- m6g
- m7i
- m7g
- c6i
- c6g
- c7i
- c7g
- r6i
- r6g
- r7i
- r7g
- key: karpenter.k8s.aws/instance-size
operator: In
values:
- large
- xlarge
- 2xlarge
weight: 70
limits:
cpu: "200"
memory: 400Gi
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 30s

NodePool Summary

NodePoolInstance FamilyCapacity TypeLimitsPurpose
generalm6i, m7i, c6i, c7i, r6i, r7i (+ ARM)Spot + On-Demand200 vCPU, 400GiGeneral workloads
memoryr6i, r7i, r6g, r7gOn-Demand100 vCPU, 400GiMemory-intensive (Redis, Search)
apic6i, c7i, m6i, m7iOn-Demand80 vCPU, 160GiAPI server workloads
batchVarious typesSpot Only50 vCPU, 100GiBatch jobs, Analytics

EC2NodeClass

EC2 settings shared by all NodePools.

apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
amiSelectorTerms:
- alias: al2023@latest
role: "multi-region-mall-node-group-${REGION}"
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: multi-region-mall
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: multi-region-mall
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 100Gi
volumeType: gp3
iops: 3000
throughput: 125
encrypted: true

Managed Add-ons

The following managed add-ons are installed on the EKS cluster:

Add-onDescriptionIRSA
vpc-cniAWS VPC CNI PluginYes
corednsKubernetes DNS ServiceNo
kube-proxyNetwork ProxyNo
aws-ebs-csi-driverEBS Volume ProvisioningYes
aws-efs-csi-driverEFS Volume MountingYes
resource "aws_eks_addon" "vpc_cni" {
cluster_name = aws_eks_cluster.main.name
addon_name = "vpc-cni"
service_account_role_arn = aws_iam_role.vpc_cni.arn
}

resource "aws_eks_addon" "coredns" {
cluster_name = aws_eks_cluster.main.name
addon_name = "coredns"
depends_on = [aws_eks_addon.vpc_cni]
}

resource "aws_eks_addon" "kube_proxy" {
cluster_name = aws_eks_cluster.main.name
addon_name = "kube-proxy"
}

resource "aws_eks_addon" "aws_ebs_csi_driver" {
cluster_name = aws_eks_cluster.main.name
addon_name = "aws-ebs-csi-driver"
}

resource "aws_eks_addon" "aws_efs_csi_driver" {
cluster_name = aws_eks_cluster.main.name
addon_name = "aws-efs-csi-driver"
}

IRSA (IAM Roles for Service Accounts)

Each microservice securely accesses AWS resources through IRSA.

Service-to-IRSA Mapping

IRSA Configuration Example

locals {
services = {
"product-catalog" = { namespace = "core-services", policies = ["AmazonDocDBFullAccess"] }
"order" = { namespace = "core-services", policies = ["AmazonRDSDataFullAccess"] }
"payment" = { namespace = "core-services", policies = ["AmazonRDSDataFullAccess"] }
"user-profile" = { namespace = "user-services", policies = ["AmazonDocDBFullAccess"] }
"analytics" = { namespace = "platform", policies = ["AmazonS3FullAccess"] }
}
}

resource "aws_iam_role" "service_irsa" {
for_each = local.services

name = "${var.cluster_name}-${each.key}-${var.region}"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRoleWithWebIdentity"
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.eks.arn
}
Condition = {
StringEquals = {
"${local.oidc_provider_url}:aud" = "sts.amazonaws.com"
"${local.oidc_provider_url}:sub" = "system:serviceaccount:${each.value.namespace}:${each.key}"
}
}
}]
})
}

Cluster Security

Encryption

  • Secrets Encryption: Kubernetes Secrets encryption using KMS keys
  • etcd Encryption: AWS managed encryption
encryption_config {
provider {
key_arn = aws_kms_key.eks_secrets.arn
}
resources = ["secrets"]
}

Network Security

  • Private Subnets: Worker nodes are placed in private subnets
  • Security Groups: Separate cluster and node security groups
  • API Endpoint: Private + Public access (VPN/Bastion recommended)

Connecting to the Cluster

# Update kubeconfig
aws eks update-kubeconfig \
--name multi-region-mall \
--region us-east-1

# Verify cluster
kubectl cluster-info
kubectl get nodes

Next Steps