- Published on
Karpenter 도입 후 EKS 노드가 안 늘 때 해결법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스처럼 자동으로 노드를 늘려줄 것 같아 Karpenter를 도입했는데, 막상 부하가 올라가도 EKS 노드가 전혀 늘지 않고 Pod가 Pending에 머무르는 경우가 꽤 흔합니다. 이 문제는 대부분 “Karpenter가 스케일 아웃 결정을 못 내림” 또는 “결정은 했지만 AWS에서 인스턴스 생성이 실패함” 두 갈래로 나뉩니다.
이 글에서는 증상을 이벤트로 확정 → Karpenter 로그로 원인 분류 → IAM/네트워크/리소스/스펙 설정을 체크리스트로 해결하는 흐름으로 정리합니다.
관련해서 Pod Pending 자체의 일반 원인도 함께 확인하면 진단 속도가 빨라집니다: EKS Pod Pending 0/XX nodes available 원인별 해결
1) 먼저 “정말 Karpenter가 스케일해야 하는 상황”인지 확정
1-1. Pending Pod 이벤트 확인
Karpenter가 노드를 늘리려면 기본적으로 **스케줄러가 배치하지 못한 Pod(Pending)**가 있어야 합니다.
kubectl get pod -A --field-selector=status.phase=Pending
kubectl describe pod -n <ns> <pod>
Events에서 아래 같은 메시지가 핵심입니다.
0/XX nodes are available: ... Insufficient cpu/memory→ 스케일 아웃 후보node(s) had taint {…}, that the pod didn't tolerate→ 노드 늘려도 못 올라감(스펙/톨러레이션 문제)pod has unbound immediate PersistentVolumeClaims→ 스토리지 바인딩 문제(노드 스케일과 무관)
1-2. 리소스 요청(requests)이 없는 Pod는 “스케일 신호”가 약해질 수 있음
Karpenter는 스케줄링 불가 Pod의 요구사항을 기반으로 인스턴스를 선택합니다. resources.requests가 비어 있거나 너무 작으면, 실제로는 부족한데도 스케일 트리거가 애매해질 수 있습니다(특히 binpack/daemonset 오버헤드 포함 시).
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
2) Karpenter 컨트롤러가 “스케일 결정을 내렸는지” 확인
2-1. Karpenter 로그에서 가장 먼저 볼 것
kubectl -n karpenter logs deploy/karpenter -c controller --tail=200
자주 나오는 패턴:
no subnets found/no security groups found→ 태그/셀렉터 문제AccessDenied/UnauthorizedOperation→ IRSA/IAM 권한 문제launch template/CreateFleet실패 → EC2 용량/쿼터/AMI/인스턴스 타입 문제cannot satisfy requirements→ NodePool/NodeClass 요구조건이 너무 빡셈
2-2. NodePool/NodeClass 상태 확인
(Karpenter v1 계열 기준) 리소스 상태와 이벤트를 함께 봅니다.
kubectl get nodepool -A
kubectl describe nodepool <name>
kubectl get ec2nodeclass -A
kubectl describe ec2nodeclass <name>
Events에 이유가 거의 다 적힙니다. 예: Failed resolving subnets, Failed to discover AMI, Insufficient permissions.
3) 원인 1순위: IRSA/IAM 권한 문제로 인스턴스 생성이 막힘
Karpenter는 EC2, IAM, EKS, SSM(AMI 조회 방식에 따라), Pricing(옵션) 등 여러 API를 호출합니다. 권한이 하나라도 빠지면 스케일 결정을 해도 실제 노드 생성이 실패합니다.
3-1. 흔한 증상
- 로그에
AccessDenied/sts:AssumeRoleWithWebIdentity실패 CreateFleet/RunInstances/CreateLaunchTemplate권한 부족
IRSA 자체가 꼬였을 가능성이 크면 아래 글의 체크리스트가 그대로 도움이 됩니다.
3-2. 서비스어카운트에 올바른 role-arn이 붙었는지
kubectl -n karpenter get sa karpenter -o yaml | yq '.metadata.annotations'
예상:
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/KarpenterControllerRole-<CLUSTER>
3-3. ControllerRole에 필요한 권한이 실제로 있는지(핵심)
환경마다 정책은 달라질 수 있지만, 최소한 다음 계열이 막히면 노드가 안 늘어납니다.
ec2:CreateFleet,ec2:RunInstances,ec2:CreateLaunchTemplate*,ec2:Describe*,ec2:CreateTagsiam:PassRole(노드 인스턴스 프로파일/역할 전달)ssm:GetParameter(AL2/AL2023 AMI를 SSM로 찾는 구성일 때)
권한이 맞는데도 실패한다면, 조건(Condition)으로 리소스 태그 제한을 걸어둔 조직 정책(SCP/Permission Boundary)도 같이 확인해야 합니다.
4) 원인 2순위: 서브넷/보안그룹 디스커버리(태그) 실패
Karpenter는 EC2NodeClass에서 서브넷/보안그룹을 태그 셀렉터로 찾는 구성이 일반적입니다. 여기서 태그가 안 맞으면 로그에 no subnets found가 뜨고, 노드 생성이 진행되지 않습니다.
4-1. EC2NodeClass 예시(태그 기반)
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
amiFamily: AL2
role: KarpenterNodeRole-my-eks
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: my-eks
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: my-eks
tags:
karpenter.sh/discovery: my-eks
4-2. 서브넷/SG에 discovery 태그가 실제로 있는지
aws ec2 describe-subnets \
--filters "Name=tag:karpenter.sh/discovery,Values=my-eks" \
--query 'Subnets[].SubnetId' --output text
aws ec2 describe-security-groups \
--filters "Name=tag:karpenter.sh/discovery,Values=my-eks" \
--query 'SecurityGroups[].GroupId' --output text
결과가 비어 있으면 100% 이슈입니다.
4-3. 퍼블릭/프라이빗 서브넷 라우팅도 함께 확인
- 프라이빗 서브넷이면 NAT/프록시 없이 ECR/SSM 접근이 막혀 부팅 후 조인 실패
- VPC 엔드포인트(EC2, ECR, SSM, STS 등) 구성이 없으면 제한망에서 실패 가능
이 경우 “노드가 생성은 됐는데 Ready가 안 됨” 형태로도 나타납니다.
5) 원인 3순위: NodePool 요구조건이 너무 엄격해서 “만족 가능한 인스턴스가 없음”
Karpenter는 NodePool의 requirements를 만족하는 인스턴스 타입/가용영역/아키텍처를 찾아야 합니다. 조건이 조금만 과해도 cannot satisfy requirements로 끝납니다.
5-1. 과한 requirements의 전형
- 특정 AZ만 고정했는데 그 AZ에 용량이 없음
- 인스턴스 패밀리/사이즈를 너무 제한함
capacity-type=spot만 허용했는데 스팟이 없음- 아키텍처를
arm64로 고정했는데 이미지가amd64
5-2. 현실적인 NodePool 예시(온디맨드 + 범용)
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: general
spec:
template:
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
- key: node.kubernetes.io/instance-type
operator: In
values: ["m6i.large", "m6i.xlarge", "c6i.large", "c6i.xlarge"]
limits:
cpu: "200"
disruption:
consolidationPolicy: WhenEmpty
디버깅 단계에서는 인스턴스 타입 목록을 넓히고, AZ 고정을 풀어 “일단 뜨게” 만든 뒤 점진적으로 조이는 게 빠릅니다.
6) 원인 4순위: limits/quotas 때문에 더 이상 못 늘어남
6-1. NodePool limits에 걸림
spec.limits.cpu 같은 제한을 걸어두면, 그 이상은 스케일 아웃이 멈춥니다.
kubectl describe nodepool general | sed -n '/Limits/,$p'
6-2. AWS 계정/리전 EC2 쿼터
특히 신규 계정/리전은 vCPU 한도가 낮아 CreateFleet이 실패합니다.
확인 포인트:
- On-Demand vCPU limit
- Spot vCPU limit
- 특정 인스턴스 패밀리 제한
aws service-quotas list-service-quotas --service-code ec2 \
--query 'Quotas[?contains(QuotaName, `vCPU`) == `true`].[QuotaName,Value]' \
--output table
7) 원인 5순위: 노드는 만들어졌는데 클러스터에 조인(Ready)하지 못함
이 케이스는 “노드 수가 안 늘어”로 보이지만, 실제로는 EC2가 생성되었다가 부팅 후 조인 실패로 사라지거나 NotReady로 남습니다.
7-1. 빠른 확인
kubectl get nodes -owide
kubectl get events -A --sort-by=.lastTimestamp | tail -n 50
7-2. aws-auth / Access Entry 문제
Karpenter 노드 역할이 EKS에 인증되지 않으면 노드가 조인하지 못합니다.
aws-authConfigMap(구형 방식) 또는- EKS Access Entry(신형 방식)
을 사용 중인 클러스터 정책에 맞게 노드 역할을 등록해야 합니다.
7-3. CNI/kube-proxy/CoreDNS 등 애드온 장애
노드가 떠도 네트워킹 애드온이 깨져 있으면 Ready 전환이 늦거나 파드가 계속 Pending/CrashLoopBackOff로 이어질 수 있습니다. 특히 kube-proxy iptables 이슈는 노드/파드 통신을 망가뜨립니다.
8) 실전 트러블슈팅: “10분 안에 결론내는” 커맨드 루틴
아래 순서대로 보면 대부분 1~2회전 내에 원인이 좁혀집니다.
# 1) Pending 파드와 이벤트
kubectl get pod -A --field-selector=status.phase=Pending
kubectl describe pod -n <ns> <pod>
# 2) Karpenter 로그
kubectl -n karpenter logs deploy/karpenter -c controller --tail=300
# 3) NodePool/NodeClass 이벤트
kubectl describe nodepool <nodepool>
kubectl describe ec2nodeclass <nodeclass>
# 4) 디스커버리 태그로 서브넷/SG가 잡히는지
aws ec2 describe-subnets --filters "Name=tag:karpenter.sh/discovery,Values=<cluster>" --query 'Subnets[].SubnetId' --output text
aws ec2 describe-security-groups --filters "Name=tag:karpenter.sh/discovery,Values=<cluster>" --query 'SecurityGroups[].GroupId' --output text
# 5) IRSA/권한(AccessDenied 여부)
kubectl -n karpenter get sa karpenter -o yaml | yq '.metadata.annotations'
AccessDenied가 보이면: IRSA/정책/PassRole부터 해결no subnets/security groups found면: 태그/셀렉터 수정cannot satisfy requirements면: NodePool requirements 완화vCPU limit/InsufficientInstanceCapacity면: 쿼터/인스턴스 타입/AZ 분산
9) 재발 방지 체크리스트(운영 관점)
9-1. 관측성
- Karpenter controller 로그를 CloudWatch로 수집
- Karpenter 이벤트(Provisioning 실패)를 알람으로 연결
- EC2 Fleet/Spot 실패 지표 모니터링
9-2. 구성 안정성
- NodePool requirements는 “최소 제약”부터 시작해 점진 강화
- 서브넷/SG 태그는 IaC(Terraform/CDK)로 강제
- 온디맨드 fallback(스팟만 쓰지 않기)
9-3. 장애 시 빠른 우회
- 임시로 NodePool에 온디맨드 허용
- 인스턴스 타입 풀 확장
- limits 상향(또는 일시 해제)
마무리
Karpenter 도입 후 노드가 안 늘어나는 문제는 대부분 다음 5개 중 하나로 귀결됩니다.
- Pending Pod가 스케일 대상으로 성립하지 않음(톨러레이션/PVC 등)
- IRSA/IAM 권한 부족(특히 PassRole, CreateFleet)
- 서브넷/보안그룹 디스커버리 태그 실패
- NodePool requirements 과제약 또는 EC2 쿼터/용량 이슈
- 노드는 생성됐지만 조인 실패(aws-auth/AccessEntry, 네트워크)
위의 커맨드 루틴으로 이벤트와 로그를 먼저 수집하면, 감으로 설정을 바꾸는 시간을 크게 줄일 수 있습니다.