Amazon EKS — Node Issues

3 minute read

Node problems prevent pods from scheduling and running. Common issues include NotReady status, join failures, IAM misconfigurations, and disk pressure.


NotReady Nodes

Diagnosis

# List node status
kubectl get nodes

# Detailed conditions
kubectl describe node <node-name> | grep -A15 Conditions
Condition Status Meaning
Ready False kubelet is unhealthy
MemoryPressure True Node running out of memory
DiskPressure True Node running out of disk
PIDPressure True Too many processes
NetworkUnavailable True Network not configured (CNI issue)

Common Causes

Cause Diagnosis Fix
kubelet crashed SSM into node, check systemctl status kubelet Restart kubelet or replace node
VPC CNI failure Check aws-node pod on that node Restart aws-node DaemonSet pod
Resource exhaustion kubectl top node <name> Drain and replace node
EC2 instance issue AWS Console → EC2 → Status Checks Terminate instance (ASG replaces it)

Fix — SSM Into Node

# Find instance ID
INSTANCE_ID=$(kubectl get node <node-name> -o jsonpath='{.spec.providerID}' | cut -d'/' -f5)

# Start SSM session
aws ssm start-session --target $INSTANCE_ID

# On the node:
systemctl status kubelet
journalctl -u kubelet --since "10 minutes ago" --no-pager | tail -50

Node Join Failures

Nodes are in EC2 but don’t appear in kubectl get nodes.

Diagnosis

# Check node group health
aws eks describe-nodegroup --cluster-name my-cluster --nodegroup-name my-nodes \
  --query 'nodegroup.health'

# Check EC2 instances
aws ec2 describe-instances \
  --filters "Name=tag:eks:cluster-name,Values=my-cluster" \
  --query 'Reservations[*].Instances[*].{ID:InstanceId,State:State.Name}'

Common Causes

Cause Symptom Fix
Wrong IAM role Node can’t call EKS API Check node group IAM role has AmazonEKSWorkerNodePolicy, AmazonEKS_CNI_Policy, AmazonEC2ContainerRegistryReadOnly
Security group blocking Can’t reach API server Ensure node SG allows egress to API server SG on 443
No internet/NAT Can’t pull container images Add NAT Gateway or VPC endpoints
Wrong subnet Not in EKS VPC Verify subnet is in the cluster VPC
Bootstrap script failure Self-managed AMI issue Check /var/log/cloud-init-output.log via SSM

Fix — Node IAM Role

# Verify required policies
aws iam list-attached-role-policies --role-name EKSNodeRole

# Must have:
# - AmazonEKSWorkerNodePolicy
# - AmazonEKS_CNI_Policy (or IRSA for VPC CNI)
# - AmazonEC2ContainerRegistryReadOnly

Disk Pressure

Diagnosis

# Check disk usage via SSM
aws ssm start-session --target <instance-id>

# On node:
df -h
du -sh /var/log/*
du -sh /var/lib/containerd/*
crictl images | wc -l

Common Causes

Cause Fix
Container images piling up crictl rmi --prune
Logs filling disk Configure log rotation, Fluent Bit
Large emptyDir volumes Set sizeLimit on emptyDir
Small root volume Increase launch template volume size

Fix — Increase Root Volume

resource "aws_eks_node_group" "main" {
  # ...
  launch_template {
    id      = aws_launch_template.node.id
    version = aws_launch_template.node.latest_version
  }
}

resource "aws_launch_template" "node" {
  name_prefix = "eks-node-"

  block_device_mappings {
    device_name = "/dev/xvda"
    ebs {
      volume_size = 100    # Increase from default 20GB
      volume_type = "gp3"
      encrypted   = true
    }
  }
}

kubelet Log Analysis

# Via SSM on the node
journalctl -u kubelet --since "30 minutes ago" --no-pager | tail -100

# Common error patterns:
# "failed to get sandbox image" → Can't pull pause container → ECR/internet connectivity
# "Failed to create pod sandbox" → VPC CNI issue → Check aws-node
# "node has DiskPressure" → Disk full → Clean up or resize
# "Unable to register node" → IAM/API server issue → Check IAM role and SG

EC2 Instance Status Checks

aws ec2 describe-instance-status --instance-ids <id> \
  --query 'InstanceStatuses[*].{System:SystemStatus.Status,Instance:InstanceStatus.Status}'
Status Meaning Action
ok / ok Healthy Issue is Kubernetes-level
impaired / ok Hardware issue Terminate (ASG replaces)
ok / impaired OS issue Reboot or terminate

Force-Replace a Stuck Node

# Cordon (no new pods)
kubectl cordon <node-name>

# Drain (evict pods)
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data --force --timeout=120s

# Terminate instance (ASG/Karpenter will replace)
INSTANCE_ID=$(kubectl get node <node-name> -o jsonpath='{.spec.providerID}' | cut -d'/' -f5)
aws ec2 terminate-instances --instance-ids $INSTANCE_ID

Updated: