Introduction
Amazon EKS handles container orchestration in cloud-native environments and is core infrastructure for application deployment. As clusters grow and workloads increase, however, various resource shortages can arise.
"What if a service that was working yesterday suddenly cannot create Pods?"
This post walks through the three resource shortages most frequently encountered in production environments and how to resolve them.
- Pod IP exhaustion: "No more IP addresses can be assigned"
- Insufficient CPU/memory: "No spare resources on the node"
- Insufficient Spot Instance capacity: "The requested instance type is unavailable"
Each section covers the root cause, specific solutions, and preventive measures, in that order.
1. Resolving Pod IP Exhaustion
Situation and Symptoms
Actual error message:
Warning FailedCreatePodSandBox pod/my-app-xxx Failed to create pod sandbox:
rpc error: code = Unknown desc = failed to set up sandbox container:
networkPlugin cni failed to set up pod "my-app-xxx_default" network:
add cmd: failed to assign an IP address to container
How to diagnose the problem:
# 1. 노드별 IP 사용량 확인
kubectl get nodes -o custom-columns=NAME:.metadata.name,PODS:.status.capacity.pods,ALLOCATABLE:.status.allocatable.pods
# 2. VPC CNI 로그 확인
kubectl logs -n kube-system -l k8s-app=aws-node --tail=100
# 3. ENI 사용 현황 확인
aws ec2 describe-network-interfaces --filters "Name=description,Values=aws-K8S-*"
As the number of Pods in an EKS cluster increases, IP addresses run out because the default VPC CNI configuration limits the number of IPs each node can receive.
Solutions
1.1 Adjust WARM_IP_TARGET
First, let's look at what the key VPC CNI settings mean.
Key settings:
warm_ip_target: Number of spare IPs to keep ready on each node (default: 1)minimum_ip_target: Minimum guaranteed IP count per node (default: number of IPs per ENI)max_eni: Maximum number of ENIs (Elastic Network Interfaces) per nodewarm_eni_target: Number of spare ENIs to keep ready (default: 1)
apiVersion: v1
kind: ConfigMap
metadata:
name: amazon-vpc-cni
namespace: kube-system
data:
# Pod 생성 시 IP 부족으로 인한 지연을 방지하기 위해 미리 확보할 IP 개수
warm_ip_target: "10" # 기본값: 1
# 노드당 최소한 보장할 IP 개수 (갑작스런 트래픽 증가 대비)
minimum_ip_target: "5"
# 노드당 최대 ENI 개수 제한 (인스턴스 타입별로 상한 존재)
max_eni: "4"
# 새로운 ENI 할당 시 미리 준비해둘 개수
warm_eni_target: "1"
Effects of the configuration:
- Setting
warm_ip_targetto 10 significantly reduces IP allocation delays when creating Pods - Setting it too high, however, wastes IP addresses (increasing costs)
- Choose a value appropriate for the cluster size and Pod creation patterns
1.2 Enable Prefix Delegation
What is Prefix Delegation?
The traditional approach assigns individual IP addresses to each ENI. Prefix Delegation assigns a /28 subnet block (16 IPs) at once, using IPs more efficiently.
Comparison:
- Traditional approach: m5.large node → up to 29 Pods
- Prefix Delegation: m5.large node → up to 110 Pods (approximately 3.8 times as many)
apiVersion: v1
kind: ConfigMap
metadata:
name: amazon-vpc-cni
namespace: kube-system
data:
# Prefix Delegation 기능 활성화
enable_prefix_delegation: "true"
# 미리 할당받을 prefix 개수 (각 prefix = 16개 IP)
warm_prefix_target: "1"
# prefix 내에서 미리 확보할 IP 개수
warm_ip_target: "5"
# prefix가 고갈되기 전 새 prefix를 요청할 임계점
minimum_ip_target: "3"
Precautions:
- Does not apply to existing nodes, so a new node group is needed
- Check in advance that the subnets have enough IP space
1.3 Configure Custom Networking
Use this when security requirements or IP range separation call for it.
apiVersion: v1
kind: ConfigMap
metadata:
name: amazon-vpc-cni
namespace: kube-system
data:
aws_vpc_k8s_cni_custom_network_cfg: "true"
apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata:
name: pod-subnet-config
spec:
subnet: subnet-xxxxxxxxx
securityGroups:
- sg-xxxxxxxxx
1.4 Initial Design Considerations
- Subnet sizing: Plan for at least the expected Pod count × 1.5
- Distribution across Availability Zones: Distribute subnets across at least 3 AZs
- Reserve CIDR blocks: Leave room for future expansion
2. Insufficient Resource Limits/Requests
Situation and Symptoms
Actual error message:
Warning FailedScheduling pod/my-app-xxx 0/3 nodes are available:
3 Insufficient cpu, 2 Insufficient memory.
How to diagnose the problem:
# 1. 클러스터 전체 리소스 현황
kubectl top nodes
# 2. 특정 노드의 상세 리소스 정보
kubectl describe node <node-name>
# 3. Pending Pod 상세 정보 확인
kubectl describe pod <pod-name>
# 4. 리소스 사용률이 높은 Pod 찾기
kubectl top pods --all-namespaces --sort-by=cpu
kubectl top pods --all-namespaces --sort-by=memory
When growing Pod counts exhaust node CPU/memory resources, Pods remain Pending.
Kubernetes 1.33 In-Place Pod Resize (Beta)
Starting with Kubernetes 1.33, resources can be adjusted without restarting the Pod.
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: app
image: nginx
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
# 1.33에서 추가된 필드
resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired
- resourceName: memory
restartPolicy: NotRequired
Resource adjustment command:
kubectl patch pod example-pod --patch='
{
"spec": {
"containers": [{
"name": "app",
"resources": {
"requests": {"cpu": "200m", "memory": "256Mi"},
"limits": {"cpu": "400m", "memory": "512Mi"}
}
}]
}
}'
Solutions for Versions Before 1.33
2.1 Right-sizing with Kyverno
What is Kyverno?
Kyverno is a Kubernetes-native policy engine that manages cluster resources through policies defined in YAML. You can write policies using familiar Kubernetes syntax instead of a complex scripting language.
Key Kyverno features:
- Validation: Checks rule compliance when resources are created/updated
- Mutation: Automatically adds/updates fields when resources are created
- Generation: Automatically creates related resources when a specific resource is created
- Cleanup: Automatically removes resources matching conditions
- Policy Exceptions: Handles policy exceptions in specific situations
Why right-sizing is needed:
Many development teams set resource requests too high or omit them entirely, wasting cluster resources or causing performance issues. Kyverno lets you manage this through policies.
Resource requirement validation policy:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-pod-resources
spec:
validationFailureAction: enforce
background: true
rules:
- name: validate-resources
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Resource requests and limits are required"
pattern:
spec:
containers:
- name: "*"
resources:
requests:
memory: "?*"
cpu: "?*"
limits:
memory: "?*"
cpu: "?*"
Automatic resource allocation policy:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-resources
spec:
background: true
rules:
- name: add-default-resources
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
containers:
- (name): "*"
resources:
requests:
+(memory): "128Mi"
+(cpu): "100m"
limits:
+(memory): "256Mi"
+(cpu): "200m"
Limiting excessive resource requests:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: limit-resources
spec:
validationFailureAction: enforce
rules:
- name: check-cpu-limit
match:
any:
- resources:
kinds:
- Pod
validate:
message: "CPU limit cannot exceed 2 cores"
pattern:
spec:
containers:
- name: "*"
resources:
limits:
cpu: "<=2000m"
2.2 Using Vertical Pod Autoscaler (VPA)
Install and configure VPA:
# VPA 설치
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler/
./hack/vpa-install.sh
Configure a VPA policy:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto" # Auto: 자동 적용, Off: 권장값만 제공, Initial: 최초 생성시만
resourcePolicy:
containerPolicies:
- containerName: app
maxAllowed:
cpu: 1
memory: 500Mi
minAllowed:
cpu: 100m
memory: 50Mi
# 리소스 조정 시 적용할 배율
controlledResources: ["cpu", "memory"]
Scenario comparing before and after VPA:
Before:
# 개발자가 임의로 설정한 리소스 (과도하게 할당)
resources:
requests:
cpu: "1000m" # 실제 사용량: 200m
memory: "2Gi" # 실제 사용량: 512Mi
limits:
cpu: "2000m"
memory: "4Gi"
Check the VPA analysis:
kubectl describe vpa my-app-vpa
Example VPA recommendations:
Recommendation:
Container Recommendations:
Container Name: app
Lower Bound:
Cpu: 150m
Memory: 256Mi
Target:
Cpu: 250m # 실제 사용량 기반 권장값
Memory: 512Mi # 실제 사용량 기반 권장값
Upper Bound:
Cpu: 400m
Memory: 1Gi
After:
# VPA가 자동 조정한 리소스 (효율적 할당)
resources:
requests:
cpu: "250m" # 75% 절약
memory: "512Mi" # 75% 절약
limits:
cpu: "400m" # 80% 절약
memory: "1Gi" # 75% 절약
Effects:
- Resource efficiency: Savings of 75% CPU and 75% memory allow more Pods to be scheduled
- Cost savings: Run 4 times as many workloads on the same node
- Stable performance: Maintain appropriate headroom based on actual usage patterns
3. Insufficient Spot Instance Capacity
Situation and Symptoms
Actual error message:
# CloudWatch 이벤트 또는 EC2 콘솔에서 확인 가능
EC2 Auto Scaling group activity:
Status: Failed
StatusReason: We currently do not have sufficient m5.large capacity
in the Availability Zone you requested (us-west-2a).
Our system will be working on provisioning additional capacity.
# Karpenter 사용 시
Warning FailedScheduling pod/my-app-xxx
no nodes available to schedule pod,
insufficient capacity for instance type m5.large in zone us-west-2a
How to diagnose the problem:
# 1. Auto Scaling Group 활동 히스토리 확인
aws autoscaling describe-scaling-activities --auto-scaling-group-name <asg-name>
# 2. Spot Instance 가격 및 가용성 확인
aws ec2 describe-spot-price-history --instance-types m5.large --availability-zone us-west-2a
# 3. Karpenter 노드 프로비저닝 로그 확인 (Karpenter 사용 시)
kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter
# 4. 클러스터 오토스케일러 로그 확인 (CA 사용 시)
kubectl logs -n kube-system -l app=cluster-autoscaler
Main problem scenarios:
- InsufficientInstanceCapacity: Insufficient Spot capacity for the requested instance type in a particular AZ
- SpotFleetRequestConfigurationInvalid: Spot Fleet configuration error
- SchedulingDisabled: Scaling is temporarily disabled for the node group
- UnfulfillCapacity: The requested capacity cannot be fulfilled
Solutions
3.1 Use Diverse Instance Types
Strategy:
To improve Spot Instance availability, combine different instance families and sizes. Relying on a single type affects the entire system when that type's capacity is insufficient.
Karpenter configuration (recommended):
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: diversified-spot-nodepool
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
# 다양한 인스턴스 패밀리 포함
- key: node.kubernetes.io/instance-type
operator: In
values:
# 범용 인스턴스 (M 시리즈)
- "m5.large"
- "m5.xlarge"
- "m5a.large"
- "m5a.xlarge"
- "m5n.large"
- "m5n.xlarge"
# 컴퓨팅 최적화 (C 시리즈)
- "c5.large"
- "c5.xlarge"
- "c5a.large"
- "c5a.xlarge"
- "c5n.large"
- "c5n.xlarge"
# 메모리 최적화 (R 시리즈)
- "r5.large"
- "r5.xlarge"
# 다중 AZ 지원
- key: topology.kubernetes.io/zone
operator: In
values:
- "us-west-2a"
- "us-west-2b"
- "us-west-2c"
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
name: default
# 중단 정책 설정
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 30s
expireAfter: 2160h # 90일
Auto Scaling Group configuration:
# ASG 사용 시 Launch Template 설정
apiVersion: v1
kind: ConfigMap
metadata:
name: cluster-autoscaler-status
namespace: kube-system
data:
nodes.max: "100"
# 다양한 인스턴스 타입 지원을 위한 MixedInstancesPolicy 활용
3.2 Strategies for Spot Instance Interruptions
Handle Spot Instance interruption signals:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: spot-interrupt-handler
namespace: kube-system
spec:
selector:
matchLabels:
app: spot-interrupt-handler
template:
metadata:
labels:
app: spot-interrupt-handler
spec:
serviceAccount: spot-interrupt-handler
hostNetwork: true
containers:
- name: spot-interrupt-handler
image: amazon/aws-node-termination-handler:v1.19.0
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: SPOT_INTERRUPT_THRESHOLD
value: "120" # 2분 전 미리 알림
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
volumes:
- name: proc
hostPath:
path: /proc
tolerations:
- operator: Exists
Configure a PodDisruptionBudget:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
minAvailable: 2 # 최소 2개 Pod는 항상 유지
selector:
matchLabels:
app: my-app
3.3 Mixed Instance Policy (When Using an ASG)
{
"InstancesDistribution": {
"OnDemandAllocationStrategy": "prioritized",
"OnDemandBaseCapacity": 2,
"OnDemandPercentageAboveBaseCapacity": 20,
"SpotAllocationStrategy": "diversified",
"SpotInstancePools": 4,
"SpotMaxPrice": "0.50"
},
"LaunchTemplate": {
"LaunchTemplateSpecification": {
"LaunchTemplateId": "lt-xxxxxxxxx",
"Version": "$Latest"
},
"Overrides": [
{
"InstanceType": "m5.large",
"WeightedCapacity": 1
},
{
"InstanceType": "m5.xlarge",
"WeightedCapacity": 2
},
{
"InstanceType": "c5.large",
"WeightedCapacity": 1
}
]
}
}
3.4 Real-time Monitoring and Alerts
CloudWatch-based Spot Instance monitoring:
# CloudWatch 알람 설정 (AWS CLI)
aws cloudwatch put-metric-alarm \
--alarm-name "SpotInstanceTermination" \
--alarm-description "Spot instance termination detected" \
--metric-name "SpotInstanceTerminating" \
--namespace "AWS/EC2Spot" \
--statistic "Sum" \
--period 300 \
--threshold 1 \
--comparison-operator "GreaterThanOrEqualToThreshold" \
--evaluation-periods 1 \
--alarm-actions "arn:aws:sns:us-west-2:123456789012:spot-alerts"
Prometheus + Grafana monitoring:
apiVersion: v1
kind: ConfigMap
metadata:
name: spot-monitoring-rules
namespace: monitoring
data:
spot-rules.yml: |
groups:
- name: spot-instances
rules:
# Spot Instance 중단 예측
- alert: SpotInstanceInterruptionWarning
expr: aws_spot_instance_interruption_warning > 0
for: 30s
labels:
severity: warning
annotations:
summary: "Spot instance interruption warning"
description: "Spot instance {{ $labels.instance_id }} will be terminated in 2 minutes"
# 노드 용량 부족
- alert: InsufficientSpotCapacity
expr: increase(kube_node_status_condition{condition="Ready",status="false"}[10m]) > 2
for: 2m
labels:
severity: critical
annotations:
summary: "Multiple spot instances unavailable"
description: "{{ $value }} nodes became unavailable in the last 10 minutes"
# 클러스터 오토스케일러 실패
- alert: ClusterAutoscalerFailure
expr: increase(cluster_autoscaler_failed_scale_ups_total[15m]) > 0
for: 1m
labels:
severity: warning
annotations:
summary: "Cluster autoscaler failed to scale up"
description: "Autoscaler failed to provision nodes: {{ $labels.reason }}"
Slack alert integration:
apiVersion: v1
kind: ConfigMap
metadata:
name: alertmanager-config
data:
alertmanager.yml: |
global:
slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
route:
group_by: ['alertname']
group_wait: 10s
group_interval: 10s
repeat_interval: 1h
receiver: 'spot-alerts'
receivers:
- name: 'spot-alerts'
slack_configs:
- channel: '#eks-alerts'
title: 'EKS Spot Instance Alert'
text: |
{{ range .Alerts }}
*Alert:* {{ .Annotations.summary }}
*Description:* {{ .Annotations.description }}
*Severity:* {{ .Labels.severity }}
{{ end }}
Production Operations Guide and Best Practices
1. Proactive Monitoring
Monitor key metrics:
# 일일 점검 스크립트 예시
#!/bin/bash
echo "=== EKS 클러스터 리소스 현황 체크 ==="
# 1. 노드별 Pod 밀도 확인
echo "1. 노드별 Pod 사용률:"
kubectl get nodes -o custom-columns=NAME:.metadata.name,PODS:.status.capacity.pods,ALLOCATABLE:.status.allocatable.pods | awk 'NR==1 || $2/$3 > 0.8'
# 2. IP 주소 사용률 확인
echo "2. VPC CNI IP 사용 현황:"
kubectl get pods -n kube-system -l k8s-app=aws-node -o jsonpath='{range .items[*]}{.spec.nodeName}{"\t"}{.status.containerStatuses[0].restartCount}{"\n"}{end}'
# 3. 리소스 사용률 Top 10
echo "3. CPU 사용률 상위 Pod:"
kubectl top pods --all-namespaces --sort-by=cpu | head -10
echo "4. 메모리 사용률 상위 Pod:"
kubectl top pods --all-namespaces --sort-by=memory | head -10
# 5. Pending Pod 확인
echo "5. Pending 상태 Pod:"
kubectl get pods --all-namespaces --field-selector=status.phase=Pending
Predictive capacity planning:
- Analyze daily usage patterns: Identify resource demand during peak hours
- Track monthly growth: Monitor workload growth trends
- Account for seasonality: Reflect periodic fluctuations specific to the business
2. Cost Optimization Strategies
Optimize the Spot/On-Demand ratio:
# 권장 비율: Spot 70%, On-Demand 30%
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: cost-optimized-nodepool
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
# Spot Instance 우선, 부족 시 On-Demand로 대체
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
name: default
# 비용 최적화를 위한 빠른 스케일 다운
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 10s
Automate resource right-sizing:
# VPA 권장값 적용 스크립트
#!/bin/bash
for deployment in $(kubectl get deployments --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}'); do
namespace=$(echo $deployment | cut -d' ' -f1)
name=$(echo $deployment | cut -d' ' -f2)
# VPA 권장값 확인
recommendation=$(kubectl get vpa ${name}-vpa -n $namespace -o jsonpath='{.status.recommendation.containerRecommendations[0].target}' 2>/dev/null)
if [ ! -z "$recommendation" ]; then
echo "Deployment $namespace/$name 리소스 조정 권장: $recommendation"
fi
done
3. High Availability and Failure Recovery
Multi-AZ deployment strategy:
# Pod Anti-Affinity를 활용한 분산 배치
apiVersion: apps/v1
kind: Deployment
metadata:
name: high-availability-app
spec:
replicas: 6
template:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- high-availability-app
topologyKey: topology.kubernetes.io/zone
- weight: 50
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- high-availability-app
topologyKey: kubernetes.io/hostname
Automatic failure recovery mechanism:
# Health Check 및 자동 재시작
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: app
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
Closing Notes
Resource shortages are hard to avoid when operating EKS clusters. As this post shows, though, understanding each problem's root cause and applying an appropriate solution makes them manageable.
Key points:
- Resolve Pod IP exhaustion by optimizing VPC CNI settings and using Prefix Delegation
- Address resource shortages with Kubernetes 1.33 features and management through Kyverno/VPA
- Improve reliability for Spot Instance issues through diversification and active monitoring
Operational principles:
- Prevention matters more than reacting after the fact
- Optimization without monitoring is just guesswork
- Automated policies prevent human error
In particular, Kubernetes 1.33's in-place Pod resize feature will substantially change operations. Adjusting resources without restarting Pods brings operations closer to zero downtime.
Checklist: A Roadmap for Resolving Resource Shortages
Actions You Can Take Now
Step 1: Diagnose the current state (30 minutes)
- [ ] Check cluster resource utilization
- [ ] Analyze why Pods are Pending
- [ ] Review current VPC CNI settings
- [ ] Check Spot Instance interruption history
Step 2: Emergency response (1 hour)
- [ ] Add nodes manually in critical situations
- [ ] Identify and address Pods consuming excessive resources
- [ ] Temporarily suspend unnecessary workloads
Step 3: Medium- to long-term improvements (1 week)
- [ ] Enable VPC CNI Prefix Delegation
- [ ] Introduce Kyverno policy-based resource management
- [ ] Install VPA and analyze its recommendations
- [ ] Diversify node groups with different instance types
Step 4: Establish operational processes (1 month)
- [ ] Build monitoring dashboards
- [ ] Configure the alerting system
- [ ] Deploy automation scripts
- [ ] Establish regular review processes
Success Metrics
Technical metrics:
- Pod creation time < 30 seconds
- Maintain node utilization at 70-80%
- Recovery time after Spot Instance interruption < 5 minutes
Business metrics:
- Reduce infrastructure costs by 20%
- Service availability of at least 99.9%
- Reduce incident response time by 50%
I hope this guide helps you operate reliable, cost-efficient EKS clusters through continuous monitoring and capacity planning.
References: