Published on

EKS Karpenter NodeClaim NotReady 10분 진단

Authors

서버리스처럼 노드를 늘리려고 Karpenter를 붙였는데, 정작 NodeClaimNotReady에서 멈추면 체감 난이도가 급상승합니다. 이유는 단순히 “노드가 안 붙음”이 아니라, (1) Karpenter CRD·컨트롤러 계층, (2) EC2 인스턴스 부팅(AMI/유저데이터) 계층, (3) IAM(노드 역할/IRSA) 계층, (4) CNI(aws-node/IP 할당) 계층 중 어디서 끊겼는지에 따라 증상이 비슷하게 보이기 때문입니다.

이 글은 NodeClaim NotReady10분 안에 “어느 계층 문제인지” 먼저 갈라내고, 재현 가능한 명령으로 원인을 좁히는 실전 진단 루틴을 제공합니다.

> 참고로 노드가 조인했는데 kubelet NotReady로 떨어지는 케이스는 CNI 초기화 문제인 경우가 많습니다. 아래 글도 함께 보시면 진단 속도가 빨라집니다: EKS kubelet NotReady - CNI plugin not initialized 해결


0) 10분 타이머: 먼저 “어디까지 진행됐는지” 확인

NodeClaim NotReady는 크게 두 부류로 나뉩니다.

  1. EC2 자체가 안 뜸: Karpenter가 인스턴스를 만들지 못했거나(권한/리소스/서브넷/SG), 만들었는데 부팅 실패.
  2. EC2는 떴는데 클러스터 조인 실패: 부트스트랩/인증(aws-auth)/CNI/IP 부족/보안그룹/엔드포인트 문제.

가장 먼저 Karpenter 이벤트를 봅니다.

kubectl get nodeclaim -A
kubectl describe nodeclaim <nodeclaim-name>

# Karpenter 컨트롤러 로그
kubectl -n karpenter logs deploy/karpenter -c controller --tail=200

describe의 Events에 아래 단서가 자주 나옵니다.

  • Failed to launch / UnauthorizedOperation → IAM/권한
  • no subnets found / no security groups found → 서브넷/SG 셀렉터 태그
  • UnhealthyDependents → 인스턴스 프로파일/인스턴스 역할 연결 문제
  • Launched instance는 있는데 노드가 안 생김 → 부트스트랩/조인/CNI

1) CRD·컨트롤러 레벨: “Karpenter가 정상 동작 중인가?”

1-1. CRD 설치 여부와 버전 불일치

업그레이드/재설치 이후 CRD가 누락되거나 버전이 꼬이면, 컨트롤러는 떠 있는데 리소스 reconcile이 제대로 안 되는 경우가 있습니다.

kubectl get crd | grep -E 'karpenter|nodeclaim|nodepool'

# Karpenter가 사용하는 주요 리소스 확인
kubectl api-resources | grep -i karpenter

증상 힌트

  • no matches for kind "NodeClaim" 같은 에러 → CRD 미설치
  • 컨트롤러 로그에 failed to list / conversion webhook 에러 → CRD/웹훅/버전 문제

1-2. 컨트롤러가 AWS API 호출을 못하는지(IRSA)

Karpenter 컨트롤러는 EC2/SSM/Pricing/EKS 등 AWS API를 호출합니다. IRSA가 틀리면 AssumeRoleWithWebIdentity 단계에서 막힙니다.

kubectl -n karpenter get sa karpenter -o yaml | sed -n '1,120p'
kubectl -n karpenter describe pod -l app.kubernetes.io/name=karpenter | sed -n '1,200p'

컨트롤러 로그에서 다음이 보이면 IRSA/IAM 문제입니다.

  • AccessDenied
  • InvalidIdentityToken
  • NoCredentialProviders

2) IAM 레벨: NodeRole/InstanceProfile/권한 3종 세트

NodeClaim이 NotReady일 때 가장 흔한 축은 노드 인스턴스 역할(NodeRole) 이거나, 인스턴스 프로파일 연결이거나, aws-auth 매핑입니다.

2-1. EC2가 떴는지부터 확인(없으면 IAM/셀렉터/쿼터)

# NodeClaim에 찍힌 instance-id를 찾거나
kubectl get nodeclaim <name> -o jsonpath='{.status.providerID}{"\n"}'

# 또는 Karpenter 로그에서 instance-id 확인 후
aws ec2 describe-instances --instance-ids i-xxxxxxxxxxxxxxxxx \
  --query 'Reservations[0].Instances[0].{State:State.Name,Subnet:SubnetId,SG:SecurityGroups[*].GroupId,Profile:IamInstanceProfile.Arn,AZ:Placement.AvailabilityZone}'
  • 인스턴스가 아예 없다 → Karpenter 컨트롤러 IAM 또는 서브넷/SG 선택자/쿼터
  • 인스턴스는 running → 다음 단계(조인 실패)로 이동

2-2. NodeRole에 꼭 필요한 권한(최소 체크)

EKS 노드가 조인하려면 일반적으로 다음 AWS managed policy 조합이 필요합니다(조직 정책에 따라 커스텀 가능).

  • AmazonEKSWorkerNodePolicy
  • AmazonEKS_CNI_Policy (또는 CNI가 IRSA면 노드 역할에서 제거 가능)
  • AmazonEC2ContainerRegistryReadOnly

추가로 Karpenter 환경에서는 SSM, EBS CSI, CloudWatch Agent 등을 쓰면 더 필요합니다.

# 인스턴스 프로파일에 연결된 role 이름 확인
aws iam get-instance-profile --instance-profile-name <profile-name>

# role에 붙은 정책 확인
aws iam list-attached-role-policies --role-name <node-role-name>

2-3. aws-auth 매핑 누락(노드 인증 실패)

노드가 부팅은 됐는데 클러스터에 Node 오브젝트가 생기지 않거나, 생겼다가 NotReady로 남는다면 aws-auth 누락을 의심합니다.

kubectl -n kube-system get cm aws-auth -o yaml

mapRoles에 NodeRole이 매핑되어야 합니다(예시).

mapRoles: |
  - rolearn: arn:aws:iam::<ACCOUNT_ID>:role/<NodeRoleName>
    username: system:node:{{EC2PrivateDNSName}}
    groups:
      - system:bootstrappers
      - system:nodes

> 운영 환경에서는 GitOps/관리형 애드온/terraform으로 aws-auth를 일관되게 관리하세요. 수동 수정은 드리프트를 만들기 쉽습니다.


3) AMI·부트스트랩 레벨: “노드가 EKS에 조인하는가?”

EC2는 떴는데 Node가 안 보이면, 대부분 부트스트랩(userData) 또는 AMI/EKS 버전 불일치에서 터집니다.

3-1. 인스턴스 시스템 로그로 부트스트랩 실패 확인

aws ec2 get-console-output --instance-id i-xxxxxxxxxxxxxxxxx --latest \
  --query 'Output' --output text | tail -n 80

다음 키워드를 찾습니다.

  • bootstrap.sh 실패
  • kubelet 인증서/토큰 관련 에러
  • cannot reach / timeout (EKS API 엔드포인트 접근 문제)

3-2. 프라이빗 클러스터에서 API 엔드포인트 라우팅

프라이빗 엔드포인트만 열어둔 EKS에서, Karpenter가 띄운 노드가 컨트롤 플레인에 접근할 네트워크 경로가 없으면 조인에 실패합니다.

  • 노드 서브넷의 라우트 테이블
  • VPC 엔드포인트(EKS, STS 등) 또는 NAT
  • 보안그룹 egress

특히 STS 접근이 막히면 토큰/인증에서 다양한 형태로 실패합니다. 네트워크/엔드포인트 진단 감각은 아래 글이 도움이 됩니다: EKS Pod→S3 504 타임아웃 - VPC 엔드포인트·NAT·DNS 진단


4) CNI 레벨: Node는 생겼는데 NotReady(또는 Pod가 안 뜸)

kubectl get nodes에 노드가 보이는데 NotReady라면, 우선 노드 컨디션과 kubelet 메시지를 봅니다.

kubectl get nodes
kubectl describe node <node-name> | sed -n '1,220p'

여기서 대표적으로 많이 보는 문구가:

  • network plugin is not ready: cni plugin not initialized
  • Container runtime network not ready

이면 CNI 초기화 실패입니다. 아래 순서로 확인합니다.

4-1. aws-node(DaemonSet) 상태

kubectl -n kube-system get ds aws-node
kubectl -n kube-system get pods -l k8s-app=aws-node -o wide
kubectl -n kube-system logs -l k8s-app=aws-node --tail=200

aws-node가 새 노드에 스케줄되지 않거나 CrashLoop이면, 노드는 Ready로 못 올라옵니다.

4-2. IP 부족(서브넷 IP 고갈 / prefix delegation)

Karpenter로 스케일 아웃하면 서브넷 IP가 먼저 바닥나는 경우가 흔합니다. 이때 노드는 떠도 Pod IP 할당이 막혀 Ready가 늦어지거나, 워크로드가 Pending에 걸립니다.

aws ec2 describe-subnets --subnet-ids subnet-xxxx subnet-yyyy \
  --query 'Subnets[*].{SubnetId:SubnetId,AZ:AvailabilityZone,AvailableIp:AvailableIpAddressCount,CIDR:CidrBlock}'
  • AvailableIpAddressCount가 낮으면: 더 큰 CIDR, 서브넷 추가, 또는 CNI prefix delegation 검토

4-3. CNI 권한(aws-node IRSA 사용 시)

최근 구성에서는 aws-node가 IRSA로 ENI/IP를 관리하기도 합니다. 이때 IRSA가 깨지면 노드 네트워킹이 올라오지 않습니다.

  • aws-node 서비스어카운트 annotation
  • OIDC provider/Trust policy
  • 필요한 EC2 권한

관련 진단 흐름은 ContainerCreating에서 멈출 때 체크리스트와도 동일한 축이 많습니다: EKS Pod가 ContainerCreating에 멈출 때 10분 진단


5) 실전 “10분 체크리스트” (복붙용)

아래를 위에서 아래로 실행하면, 보통 10분 내에 범위를 확 줄일 수 있습니다.

# 1) Karpenter/NodeClaim 이벤트
kubectl get nodeclaim
kubectl describe nodeclaim <nodeclaim>

# 2) Karpenter 컨트롤러 로그
kubectl -n karpenter logs deploy/karpenter -c controller --tail=200

# 3) EC2 생성 여부
INSTANCE_ID=$(kubectl get nodeclaim <nodeclaim> -o jsonpath='{.status.providerID}' | sed 's|.*/||')
echo "$INSTANCE_ID"
aws ec2 describe-instances --instance-ids "$INSTANCE_ID" \
  --query 'Reservations[0].Instances[0].{State:State.Name,Subnet:SubnetId,Profile:IamInstanceProfile.Arn,SG:SecurityGroups[*].GroupId,AZ:Placement.AvailabilityZone}'

# 4) aws-auth 매핑
kubectl -n kube-system get cm aws-auth -o yaml

# 5) 노드가 보이면 CNI 확인
kubectl get nodes -o wide
kubectl -n kube-system get pods -l k8s-app=aws-node -o wide
kubectl -n kube-system logs -l k8s-app=aws-node --tail=200

# 6) 서브넷 IP 여유
aws ec2 describe-subnets --subnet-ids <subnet-1> <subnet-2> \
  --query 'Subnets[*].{SubnetId:SubnetId,AvailableIp:AvailableIpAddressCount,AZ:AvailabilityZone}'

6) 케이스별 빠른 결론(원인 → 조치)

케이스 A: UnauthorizedOperation / AccessDenied로 인스턴스 생성 실패

  • 원인: Karpenter 컨트롤러 IRSA 정책 부족(EC2 RunInstances, iam:PassRole 등)
  • 조치: Karpenter 공식 권한 세트 재점검, 특히 iam:PassRole 대상이 NodeRole로 제한되어 있는지 확인

케이스 B: 인스턴스는 running인데 Node가 클러스터에 안 나타남

  • 원인: aws-auth에 NodeRole 미매핑, 부트스트랩 실패, API 엔드포인트 경로 부재
  • 조치: aws-auth 수정(또는 IaC로 반영), console output에서 bootstrap/kubelet 에러 확인, 프라이빗 클러스터면 NAT/VPC 엔드포인트/SG egress 점검

케이스 C: Node는 생겼는데 NotReady + cni plugin not initialized

  • 원인: aws-node 미기동/CrashLoop, CNI 권한(IRSA) 문제, 서브넷 IP 고갈
  • 조치: aws-node 로그 확인, IRSA/정책 수정, 서브넷 확장 또는 CNI 튜닝(prefix delegation)

7) 재발 방지: 관측 포인트 3가지

  1. Karpenter 컨트롤러 경보: AccessDenied, Failed to launch, no subnets found를 로그 기반 메트릭으로 알람화
  2. 서브넷 IP 수명 관리: 스케일 아웃 이벤트 대비 AvailableIpAddressCount 모니터링
  3. CNI/노드 부트스트랩 표준화: EKS 버전별 AMI/부트스트랩을 IaC로 고정하고, 변경 시 롤링 전략 마련

마무리

NodeClaim NotReady는 “Karpenter가 문제”라기보다, CRD/컨트롤러 → EC2 생성 → 노드 인증(IAM/aws-auth) → CNI 네트워크로 이어지는 체인 중 어디가 끊겼는지를 빨리 가르는 게임입니다. 위 체크리스트대로 보면 대부분은 1) 권한(특히 PassRole·aws-auth), 2) 프라이빗 네트워크에서의 API/STS 경로, 3) CNI/IP 고갈 중 하나로 수렴합니다.

다음 단계로, 노드가 Ready인데도 워크로드가 Pending에 걸린다면 스케줄링 조건/리소스/taint까지 확장해서 보셔야 합니다: EKS Pod Pending 0/XX nodes available 원인별 해결