Published on

EKS 노드 자동확장 실패 원인 7가지와 해결

Authors

EKS에서 워크로드가 늘었는데도 노드가 늘지 않으면, 대부분은 "노드를 늘릴 필요가 없다"고 판단했거나(스케줄러 관점), "늘리고 싶지만 못 늘리는" 상태입니다(클라우드/권한/용량 관점). 이 글은 두 관점을 분리해 실패 원인 7가지를 빠르게 좁히는 방식으로 구성했습니다.

특히 다음 두 가지 질문에 답하면 원인 범위를 크게 줄일 수 있습니다.

  • 파드 이벤트에 FailedScheduling 이 찍히는가
  • Cluster Autoscaler 로그에 scale up 시도가 보이는가

문제 진단 중 파드가 반복 재시작 중이라면, 노드 확장 이전에 파드 상태가 왜 불안정한지부터 확인해야 합니다. 관련 체크리스트는 K8s CrashLoopBackOff 원인별 진단 체크리스트도 함께 참고하세요.


먼저 확인할 3가지(가장 빨리 갈라지는 분기)

1) 정말로 "노드가 필요"한 상태인가

노드 자동확장은 보통 다음 조건이 있어야 트리거됩니다.

  • 파드가 Pending
  • 스케줄러가 "리소스 부족" 등으로 배치하지 못해 Unschedulable
  • Cluster Autoscaler가 그 파드를 보고 "노드를 늘리면 배치 가능"하다고 판단

다음으로 빠르게 확인합니다.

kubectl get pods -A --field-selector=status.phase=Pending
kubectl describe pod -n <namespace> <pod-name>

kubectl describe 결과의 Events 에서 FailedScheduling 이유가 핵심입니다.


2) Cluster Autoscaler가 실제로 동작 중인가

EKS에서 Cluster Autoscaler는 보통 kube-system 네임스페이스에 디플로이됩니다.

kubectl -n kube-system get deploy | grep -i autoscaler
kubectl -n kube-system logs deploy/cluster-autoscaler -f --tail=200

로그에서 다음 키워드를 찾습니다.

  • No unschedulable pods (확장할 이유가 없다고 판단)
  • scale up 또는 Scale-up (확장 시도)
  • failed to / AccessDenied / throttling / insufficient capacity (확장 실패)

3) 노드 그룹(ASG/MNG)이 확장 가능한 상태인가

Managed Node Group(MNG)든 Self-managed ASG든 결국 스케일은 ASG의 Desired 증가로 이어집니다. 현재 최대치에 막혀 있지 않은지 확인합니다.

aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names <asg-name> \
  --query 'AutoScalingGroups[0].{Min:MinSize,Max:MaxSize,Desired:DesiredCapacity}'

Desired == Max 라면, Autoscaler가 늘리고 싶어도 못 늘립니다.


실패 원인 1) Pending인데도 Unschedulable이 아니라서 확장 트리거가 안 됨

증상

  • 파드는 Pending 인데 EventsFailedScheduling 이 거의 없거나
  • FailedScheduling 이지만 이유가 리소스 부족이 아님

대표적으로 이런 케이스가 있습니다.

  • nodeSelector / nodeAffinity 가 너무 빡빡해서 "어떤 노드 그룹을 늘려도" 매칭이 안 됨
  • topologySpreadConstraints 로 인해 특정 존에만 배치되어야 하는데 해당 존 노드 그룹이 없음
  • taints/tolerations 불일치

진단

kubectl describe pod -n <namespace> <pod-name>

Events 에서 다음을 확인합니다.

  • node(s) didn't match Pod's node affinity/selector
  • node(s) had taint {key: value}, that the pod didn't tolerate
  • pod has unbound immediate PersistentVolumeClaims (이건 원인 6에서 다룹니다)

해결

  • 노드 그룹 라벨/테인트 전략을 문서화하고, 워크로드가 요구하는 조건이 "실제로 존재하는" 노드 그룹과 매칭되는지 점검
  • 임시로는 문제 파드에서 nodeSelector 를 완화하거나, 필요한 tolerations 를 추가

예시(테인트 허용):

apiVersion: v1
kind: Pod
metadata:
  name: example
spec:
  tolerations:
    - key: "dedicated"
      operator: "Equal"
      value: "batch"
      effect: "NoSchedule"

실패 원인 2) Cluster Autoscaler가 노드 그룹을 "발견"하지 못함(태그/디스커버리 문제)

증상

  • 파드는 Unschedulable 인데 Autoscaler 로그에 확장 대상 노드 그룹이 안 보임
  • 로그에 No node group config 류 메시지

EKS에서 Autoscaler는 보통 ASG 태그를 통해 확장 대상을 자동 발견합니다. 태그가 빠지면 "확장할 그룹이 없다"고 결론냅니다.

진단

Autoscaler 실행 옵션에서 --node-group-auto-discovery 설정을 확인합니다.

kubectl -n kube-system get deploy cluster-autoscaler -o yaml | sed -n '1,200p'

ASG 태그도 확인합니다.

aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names <asg-name> \
  --query 'AutoScalingGroups[0].Tags'

일반적으로 다음 형태가 필요합니다(환경에 따라 키는 다를 수 있으나, EKS 관행은 아래 조합이 많습니다).

  • k8s.io/cluster-autoscaler/enabled = true
  • k8s.io/cluster-autoscaler/<cluster-name> = owned 또는 shared

주의: 본문에 <cluster-name> 같은 부등호가 그대로 노출되면 MDX에서 깨질 수 있으니, 항상 인라인 코드로 표기합니다.

해결

ASG에 태그를 추가합니다.

aws autoscaling create-or-update-tags --tags \
  ResourceId=<asg-name>,ResourceType=auto-scaling-group,Key=k8s.io/cluster-autoscaler/enabled,Value=true,PropagateAtLaunch=true \
  ResourceId=<asg-name>,ResourceType=auto-scaling-group,Key=k8s.io/cluster-autoscaler/<cluster-name>,Value=owned,PropagateAtLaunch=true

또는 고정 구성(명시적 노드 그룹)으로 운영한다면 Autoscaler에 --nodes=min:max:asg-name 형태로 직접 지정하는 방법도 있습니다.


실패 원인 3) IAM 권한 부족 또는 IRSA 설정 오류로 AWS API 호출이 실패

증상

  • Autoscaler 로그에 AccessDenied
  • sts:AssumeRoleWithWebIdentity 실패
  • ASG Desired 변경이 전혀 발생하지 않음

EKS에서 권장되는 방식은 IRSA(IAM Roles for Service Accounts)로 Autoscaler에 권한을 부여하는 것입니다. 이 연결이 끊기면 Autoscaler는 "보고"는 있어도 "조치"를 못 합니다.

진단

서비스어카운트에 어노테이션이 붙어 있는지 확인합니다.

kubectl -n kube-system get sa cluster-autoscaler -o yaml

다음 형태를 확인합니다.

  • eks.amazonaws.com/role-arn: arn:aws:iam::...:role/...

그리고 로그에서 실제 에러를 확인합니다.

kubectl -n kube-system logs deploy/cluster-autoscaler --tail=300 | grep -E 'AccessDenied|AssumeRole|Unauthorized|denied'

해결

  • OIDC provider가 클러스터에 연결되어 있는지 확인
  • Autoscaler용 IAM 정책에 최소 필요 권한을 부여(ASG describe/update, EC2 describe 등)
  • cluster-autoscaler 서비스어카운트가 올바른 role을 참조하도록 수정

운영에서 권한 문제는 Argo CD로 배포한 매니페스트/차트가 drift 나면서 재발하기도 합니다. 배포 도구를 쓴다면 Argo CD Sync 실패? RBAC·CRD·Drift 진단법처럼 "원하는 상태"가 유지되는지 점검하는 습관이 도움이 됩니다.


실패 원인 4) 노드 그룹 MaxSize, 쿼터, 혹은 인스턴스 타입/용량 부족

증상

  • Autoscaler는 scale up 을 시도하지만, ASG 액티비티에 실패가 남음
  • 에러에 InsufficientInstanceCapacity, MaxLimitExceeded, VcpuLimitExceeded 등이 보임

진단

ASG 스케일링 액티비티를 확인합니다.

aws autoscaling describe-scaling-activities \
  --auto-scaling-group-name <asg-name> \
  --max-items 20 \
  --query 'Activities[*].{StatusCode:StatusCode,Description:Description,StatusMessage:StatusMessage,StartTime:StartTime}'

EC2 쿼터(Service Quotas)도 함께 확인합니다.

  • 리전별 vCPU 한도
  • Spot 사용 시 Spot 한도

해결

  • MaxSize 상향
  • 인스턴스 타입을 단일로 고정하지 말고, 여러 타입을 허용(특히 Spot/온디맨드 혼합)
  • 가용 영역을 다중으로 열어두고, 특정 AZ에만 묶이지 않게 구성
  • 쿼터 증설 요청

실무 팁: "특정 인스턴스 타입만" 쓰는 구성은 평소엔 단순하지만, 트래픽 급증 시 용량 부족에 취약합니다. 최소 2~3개 타입을 후보로 두는 편이 장애 확률을 크게 낮춥니다.


실패 원인 5) 리소스 요청(requests) 과대/오설정으로 어떤 노드에도 안 들어감

증상

  • FailedScheduling 이유가 Insufficient cpu 또는 Insufficient memory
  • 노드를 늘려도 계속 Pending
  • 또는 Autoscaler 로그에 "노드를 늘려도 배치 불가" 류의 판단이 보임

이 케이스는 단순히 노드가 부족한 게 아니라, 파드가 요구하는 requests 가 노드 인스턴스 스펙을 초과하거나, DaemonSet 오버헤드 때문에 실효 용량이 부족한 상황입니다.

진단

파드 요청량을 확인합니다.

kubectl get pod -n <namespace> <pod-name> -o jsonpath='{.spec.containers[*].resources.requests}'

노드의 allocatable도 확인합니다.

kubectl get node <node-name> -o jsonpath='{.status.allocatable}'

DaemonSet이 많은 클러스터는 "노드 한 대가 생겨도" 실제로 워크로드에 쓸 수 있는 CPU/메모리가 생각보다 작습니다.

해결

  • requests 를 현실적인 값으로 조정(측정 기반)
  • 큰 파드를 위한 전용 노드 그룹(더 큰 인스턴스 타입) 추가
  • DaemonSet 리소스 요청 최적화

예시(요청량 조정):

resources:
  requests:
    cpu: "250m"
    memory: "512Mi"
  limits:
    cpu: "1000m"
    memory: "1Gi"

실패 원인 6) PVC 바인딩/스토리지 토폴로지 문제로 스케줄링이 막힘

증상

  • FailedSchedulingpod has unbound immediate PersistentVolumeClaims
  • EBS CSI 사용 시 특정 AZ에 PV가 묶여서 다른 AZ 노드로는 배치 불가
  • Autoscaler는 노드를 늘리지만 "올바른 AZ" 가 아니면 여전히 배치 실패

진단

PVC/PV 상태와 StorageClass의 volumeBindingMode 를 확인합니다.

kubectl get pvc -n <namespace>
kubectl describe pvc -n <namespace> <pvc-name>

kubectl get sc
kubectl describe sc <storage-class-name>

volumeBindingModeImmediate 인 경우, PV가 먼저 특정 토폴로지로 잡혀 스케줄링을 제약할 수 있습니다.

해결

  • 가능한 경우 StorageClass를 WaitForFirstConsumer 로 설정해 "파드가 스케줄될 노드의 AZ" 에 맞춰 볼륨이 생성되게 함
  • 워크로드가 특정 AZ에만 있어야 한다면, 해당 AZ에 노드 그룹이 존재하고 확장 가능해야 함

실패 원인 7) Cluster Autoscaler 설정값(스킵/딜레이/스케일다운)으로 의도치 않게 확장이 지연 또는 무시

증상

  • 확장이 "가끔" 안 되거나, 한참 뒤에야 됨
  • 특정 파드/노드 그룹만 확장 대상에서 제외됨

Autoscaler에는 안전장치가 많습니다. 대표적으로 다음 옵션/조건 때문에 확장이 늦어지거나 무시될 수 있습니다.

  • --scale-up-from-zero 비활성: 0대에서 시작하는 노드 그룹을 못 올림
  • --max-node-provision-time 이 너무 짧아 노드가 준비되기 전에 실패 처리
  • 노드에 확장 제외 어노테이션/라벨(운영 정책)
  • 우선순위가 낮은 파드가 preemption 대상이지만 실제로는 해결이 안 되는 케이스

진단

Autoscaler 디플로이 옵션을 확인합니다.

kubectl -n kube-system get deploy cluster-autoscaler -o yaml | sed -n '1,220p'

로그에서 스킵 사유를 찾습니다.

kubectl -n kube-system logs deploy/cluster-autoscaler --tail=400 | grep -iE 'skip|ignored|not triggered|from zero|max node provision'

해결

  • 0대에서 확장해야 하는 노드 그룹이 있다면 --scale-up-from-zero=true 검토
  • 노드 프로비저닝이 느린 환경(커스텀 AMI, 부팅 스크립트 과다, 큰 이미지 풀 등)이면 --max-node-provision-time 완화
  • 확장 제외 정책(라벨/태그/어노테이션)이 있다면 문서화하고, 워크로드 팀과 합의된 규칙으로 관리

실전 트러블슈팅 순서(10분 안에 원인 좁히기)

아래 순서대로 보면 "어디에서 막혔는지"가 선명해집니다.

  1. kubectl get pods -A 로 Pending 확인
  2. kubectl describe podFailedScheduling 이유 확인
  3. Autoscaler 로그에서 No unschedulable pods 인지, scale up 시도인지 확인
  4. ASG MaxSize/Desired 상태 확인
  5. ASG scaling activity에서 AWS 측 실패 메시지 확인
  6. IAM(특히 IRSA) 관련 AccessDenied 여부 확인
  7. PVC/StorageClass 토폴로지 제약 확인

부록: 자주 쓰는 커맨드 모음

Pending 파드와 이벤트 한 번에 보기

kubectl get pods -A --field-selector=status.phase=Pending
kubectl get events -A --sort-by=.lastTimestamp | tail -n 50

Autoscaler 상태 빠르게 확인

kubectl -n kube-system get deploy cluster-autoscaler
kubectl -n kube-system logs deploy/cluster-autoscaler --tail=200

노드 리소스/할당 상태 확인

kubectl top nodes
kubectl describe node <node-name> | sed -n '1,200p'

마무리

EKS 노드 자동확장 실패는 "Autoscaler가 고장"이라기보다, 대부분 스케줄링 제약(라벨/테인트/스토리지/요청량)과 클라우드 제약(태그 디스커버리, IAM, 쿼터/용량) 중 하나입니다.

가장 중요한 습관은 두 가지입니다.

  • 파드 Events 와 Autoscaler 로그를 함께 보고, "트리거가 없었는지" vs "트리거는 있었는데 실패했는지"를 먼저 나누기
  • ASG scaling activity에서 AWS가 말하는 실패 이유를 끝까지 확인하기

이 두 가지만 지켜도, 원인 7가지 중 어디에 해당하는지 대부분 빠르게 수렴합니다.