- Published on
EKS Pod Pending? 노드 셀렉터·테인트 5분 진단
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
운영 중인 EKS에서 배포는 성공했는데 Pod가 계속 Pending 상태로 남아 있으면, 대부분은 스케줄러가 “어느 노드에도 이 Pod를 올릴 수 없다”는 결론을 내린 상황입니다. 이때 로그를 길게 파기 전에, 스케줄링 제약부터 빠르게 확인하면 시간을 크게 줄일 수 있습니다.
특히 EKS에서는 워크로드 분리(온디맨드/스팟, GPU, 전용 노드풀), 보안 격리(전용 테인트), 비용 최적화(노드 그룹 분리) 때문에 nodeSelector/affinity/taints를 적극적으로 쓰는 팀이 많습니다. 그만큼 라벨 불일치나 톨러레이션 누락으로 Pending이 자주 발생합니다.
이 글은 “5분 진단”을 목표로, nodeSelector와 테인트를 중심으로 가장 빠른 확인 순서와 즉시 수정 패턴을 제공합니다.
관련 이슈로 이미지 풀 단계에서 막히는 경우는 Pending이 아니라 ImagePullBackOff로 보이는 경우가 많습니다. 그 케이스는 별도 글인 K8s ImagePullBackOff - ErrImagePull·401 빠른 해결도 함께 참고하세요.
1) 5분 진단 로드맵
아래 순서대로 보면 대부분의 Pending 원인을 짧게 좁힐 수 있습니다.
- Pod 이벤트에서 스케줄 실패 이유 확인
- Pod의 스케줄링 제약 확인:
nodeSelector,nodeAffinity,topologySpreadConstraints - 노드 라벨과 매칭 여부 확인
- 노드 테인트와 Pod 톨러레이션 매칭 여부 확인
- 노드 리소스/용량 및 노드 상태 확인(부가 체크)
이 글은 2, 3, 4에 집중합니다. 이유는 EKS에서 Pending의 “가장 흔한” 원인이기 때문입니다.
2) 먼저 이벤트부터: 스케줄러가 이미 답을 준다
Pending Pod는 대개 이벤트에 힌트를 남깁니다.
kubectl describe pod -n <NAMESPACE> <POD_NAME>
위 출력의 Events 섹션을 봅니다. 자주 보이는 문구는 다음과 같습니다.
0/XX nodes are available: XX node(s) didn't match Pod's node affinity/selector0/XX nodes are available: XX node(s) had taint {key: value}, that the pod didn't tolerate0/XX nodes are available: insufficient cpu또는insufficient memorynode(s) were unschedulable
여기서 이미 “셀렉터 불일치” 또는 “테인트 미톨러레이션”이 찍히면, 바로 다음 섹션으로 넘어가면 됩니다.
3) Pod의 nodeSelector와 affinity를 확인하는 법
Pod가 어떤 노드에 올라가고 싶은지(혹은 반드시 올라가야 하는지)는 스펙에 들어 있습니다.
kubectl get pod -n <NAMESPACE> <POD_NAME> -o yaml
아래 항목을 집중적으로 봅니다.
spec.nodeSelectorspec.affinity.nodeAffinityspec.schedulerName(커스텀 스케줄러 사용 여부)spec.topologySpreadConstraints(특정 존에 균등 분산 강제 시)
3-1) nodeSelector의 전형적인 함정
예를 들어 다음과 같은 Pod 스펙이 있다고 가정합니다.
apiVersion: v1
kind: Pod
metadata:
name: api
spec:
nodeSelector:
nodegroup: blue
containers:
- name: api
image: nginx:1.25
이 Pod는 라벨이 nodegroup=blue인 노드에서만 스케줄링됩니다.
문제는 “내가 생각한 라벨 키/값”과 “실제 노드 라벨”이 다를 때입니다. 예를 들어 EKS Managed Node Group을 쓴다면, 기본적으로는 eks.amazonaws.com/nodegroup 같은 라벨이 붙어 있는 경우가 많습니다. 팀에서 커스텀 라벨을 붙였다면 그 라벨이 정말 존재하는지부터 확인해야 합니다.
3-2) 노드 라벨을 빠르게 확인하기
클러스터 전체 노드 라벨을 한 번에 훑는 명령입니다.
kubectl get nodes --show-labels
특정 라벨 키가 어디에 어떻게 붙어 있는지 보고 싶다면 다음이 편합니다.
kubectl get nodes -L nodegroup -L eks.amazonaws.com/nodegroup
이 출력에서 nodegroup 컬럼이 비어 있다면, Pod의 nodeSelector는 어떤 노드와도 매칭되지 않으므로 Pending이 됩니다.
3-3) nodeAffinity는 “필수”와 “선호”를 구분해야 한다
nodeAffinity는 requiredDuringSchedulingIgnoredDuringExecution과 preferredDuringSchedulingIgnoredDuringExecution의 의미가 다릅니다.
required...는 만족 못 하면 스케줄링 불가preferred...는 만족 못 해도 스케줄링은 가능(다만 우선순위 낮음)
예시:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: workload
operator: In
values:
- batch
이 경우 workload=batch 라벨이 없는 노드는 전부 후보에서 제외됩니다. nodeSelector보다 더 복잡한 조건을 만들 수 있는 대신, 운영 중 라벨이 살짝만 어긋나도 Pending이 발생하기 쉽습니다.
4) EKS에서 자주 터지는 테인트·톨러레이션 미스매치
테인트는 “이 노드는 아무나 못 올라와”이고, 톨러레이션은 “나는 그 제한을 견딜게”입니다.
스케줄러 이벤트에 다음이 보이면 거의 확정입니다.
had taint {dedicated: gpu}, that the pod didn't tolerate
4-1) 노드 테인트 확인
kubectl describe node <NODE_NAME>
출력 중 Taints: 라인을 찾습니다. 예:
dedicated=gpu:NoSchedulespot=true:NoScheduleteam=platform:NoExecute
EKS에서 GPU 노드그룹이나 전용 워크로드 노드풀을 운영할 때 NoSchedule 테인트를 걸어두는 경우가 많습니다.
4-2) Pod에 톨러레이션이 있는지 확인
Pod 스펙에서 spec.tolerations를 봅니다.
tolerations:
- key: dedicated
operator: Equal
value: gpu
effect: NoSchedule
위 톨러레이션이 없으면 dedicated=gpu:NoSchedule 테인트가 있는 노드에는 스케줄링되지 않습니다.
4-3) 흔한 실수 3가지
key또는value오타
- 노드는
dedicated=gpu인데 Pod는dedicate=gpu처럼 키가 다름
effect불일치
- 노드는
NoSchedule인데 Pod는NoExecute로 적음
operator의미 혼동
operator: Exists는value를 보지 않습니다operator: Equal은value까지 정확히 매칭해야 합니다
예를 들어 아래는 “키만 있으면 허용”입니다.
tolerations:
- key: dedicated
operator: Exists
effect: NoSchedule
이 패턴은 편하지만, dedicated=team-a와 dedicated=gpu를 구분하지 못해 의도치 않은 노드에 올라갈 수 있습니다. 전용 노드 격리가 목적이라면 Equal을 권장합니다.
5) nodeSelector와 테인트가 함께 걸린 경우: 교집합이 비어버린다
실무에서 가장 골치 아픈 케이스는 “각각은 맞는 것 같은데, 둘을 동시에 만족하는 노드가 없다”입니다.
예:
- Pod는
nodeSelector: nodegroup=blue - blue 노드그룹의 노드에는
dedicated=platform:NoSchedule테인트가 있음 - Pod에는 해당 톨러레이션이 없음
이 경우 셀렉터는 맞지만 테인트에서 막혀서 Pending이 됩니다.
진단은 다음처럼 “후보 노드가 실제로 몇 개인지”를 눈으로 확인하면 빠릅니다.
kubectl get nodes -L nodegroup | grep blue
kubectl describe node <BLUE_NODE_NAME> | grep -n "Taints" -A2
그리고 Pod 스펙에서 tolerations를 추가할지, 아니면 노드그룹 테인트 정책을 바꿀지 결정하면 됩니다.
6) 즉시 적용 가능한 수정 패턴
운영 중에는 “원인 규명”만큼이나 “어떤 수정이 안전한가”가 중요합니다. 아래는 자주 쓰는 처방전입니다.
6-1) Deployment에 톨러레이션 추가
kubectl patch deploy -n <NAMESPACE> <DEPLOYMENT_NAME> --type='json' -p='[
{
"op": "add",
"path": "/spec/template/spec/tolerations",
"value": [
{
"key": "dedicated",
"operator": "Equal",
"value": "platform",
"effect": "NoSchedule"
}
]
}
]'
주의: 이미 tolerations가 있다면 add가 아니라 replace 또는 배열에 add를 해야 합니다. 운영에서는 YAML을 직접 수정해 적용하는 편이 실수를 줄입니다.
6-2) nodeSelector를 실제 라벨에 맞게 정정
노드에 실제로 붙은 라벨이 eks.amazonaws.com/nodegroup=blue라면, Pod 스펙을 그에 맞게 바꿉니다.
spec:
nodeSelector:
eks.amazonaws.com/nodegroup: blue
팀 공통 규칙으로는 “커스텀 라벨 키를 하나로 표준화”하는 것이 Pending 재발을 크게 줄입니다.
6-3) 임시 우회: 스케줄링 제약을 완화해 서비스 복구 먼저
장애 상황에서 우선 서비스 복구가 필요하면 required를 preferred로 낮추는 방법이 있습니다.
예:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: workload
operator: In
values:
- batch
다만 이 방식은 “원래 의도한 격리”가 깨질 수 있으므로, 복구 후 반드시 원복 계획을 세워야 합니다.
7) 그래도 Pending이면: 마지막으로 보는 보조 체크
nodeSelector와 테인트가 정상이면, 다음을 확인합니다.
7-1) 노드가 스케줄 불가 상태인지
kubectl get nodes
SchedulingDisabled가 보이면 해당 노드에는 스케줄링되지 않습니다(예: 드레인 중).
7-2) 리소스 부족
이벤트에 insufficient cpu 등이 있으면 실제로 노드 여유가 없는 것입니다.
kubectl describe node <NODE_NAME>
Allocated resources 섹션에서 requests 합계를 확인합니다. EKS에서는 DaemonSet이 많거나, 시스템 파드가 큰 경우 “생각보다 여유가 없는” 일이 흔합니다.
7-3) 컨트롤 플레인 이슈가 아니라 워커 노드 쪽 문제인지
노드가 클러스터에 제대로 조인되지 않았거나 CNI 문제로 노드 상태가 NotReady일 수 있습니다. 이 경우도 이벤트에 힌트가 남습니다.
8) 운영 팁: Pending을 줄이는 라벨·테인트 설계 규칙
마지막으로 재발 방지를 위한 규칙을 정리합니다.
- 라벨 키 표준화:
workload,team,capacity-type처럼 의미 중심으로 통일 - 테인트는 “정말 격리가 필요한 노드”에만 최소화해서 사용
- 톨러레이션은
Exists남발을 피하고, 가능한Equal로 명시 - 노드그룹 변경 시(라벨/테인트 추가) 영향받는 Deployment 목록을 사전에 점검
EKS 운영에서는 장애가 한 번 나면 원인보다 “비슷한 장애가 계속 반복”되는 게 더 큰 비용입니다. 라벨과 테인트 정책을 문서화하고, 신규 서비스 템플릿에 기본 규칙을 녹여두면 Pending은 눈에 띄게 줄어듭니다.
추가로, 클러스터 인증이나 서비스 어카운트 연동 이슈로 런타임에서 실패하는 케이스도 종종 섞여 들어옵니다. IRSA가 전부 깨졌을 때의 복구 시나리오는 EKS OIDC Provider 삭제로 IRSA 전부 실패했을 때 복구에 정리해두었습니다.
빠른 체크리스트(요약)
kubectl describe pod이벤트에selector또는taint문구가 있는가- Pod의
nodeSelector키/값이 실제 노드 라벨과 일치하는가 - 노드에
NoSchedule테인트가 있는데 Pod에 톨러레이션이 없는가 - 셀렉터와 테인트를 동시에 만족하는 노드가 실제로 존재하는가
이 4가지만 제대로 보면, EKS에서 Pod Pending의 절반 이상은 5분 안에 결론이 납니다.