[CloudNeta] EKS 워크샵 스터디 (5) - EKS 디버깅 마스터 가이드 (2)
이번 게시글에서는 EKS 워크샵 스터디 제 5주차 내용을 작성합니다.
이 글은 3부로 나누어집니다.
- 5주차 - EKS 디버깅 마스터 가이드 (1)
- 5주차 - EKS 디버깅 마스터 가이드 (2) (현재 보고계신 글)
- 5주차 - EKS 디버깅 마스터 가이드 (3)
V. 네트워킹 디버깅 워크플로우
VPC CNI → DNS → Service → NetworkPolicy 순 계층별 진단 흐름을 살펴봅시다.
flowchart TB
START([네트워크 문제 감지])
D1[VPC CNI 점검]
D2[DNS 해석 확인]
D3[Service 연결]
D4[NetworkPolicy]
DT1[IP 고갈 / ENI 제한Prefix Delegation]
DT2[CoreDNS OOMndots:5 / DNSCache]
DT3[Selector 불일치port/targetPort]
DT4[AND vs OR 혼동Default Deny]
GW[Gateway API]
NS[netshoot 디버깅]
START -->|파드 IP 미할당| D1
START -->|DNS 실패| D2
START -->|연결 불가| D3
START -->|차단 의심| D4
D1 --> DT1
D2 --> DT2
D3 --> DT3
D4 --> DT4
D3 -.->|Ingress/LB| GW
D4 -.->|패킷 분석| NS
style START fill:#ff4444,stroke:#cc3636,color:#fff
style D1 fill:#4286f4,stroke:#2a6acf,color:#fff
style D2 fill:#4286f4,stroke:#2a6acf,color:#fff
style D3 fill:#4286f4,stroke:#2a6acf,color:#fff
style D4 fill:#ff9900,stroke:#cc7a00,color:#fff
style DT1 fill:#fbbc04,stroke:#c99603,color:#000
style DT2 fill:#fbbc04,stroke:#c99603,color:#000
style DT3 fill:#fbbc04,stroke:#c99603,color:#000
style DT4 fill:#ff4444,stroke:#cc3636,color:#fff
style GW fill:#4286f4,stroke:#2a6acf,color:#fff
style NS fill:#10b981,stroke:#0d8a63,color:#fff증상 → 진단 경로 요약
위의 증상과 그에 대해 어떤 부분을 점검하고 그 주요 원인이 무엇인지 살펴봅시다.
| 증상 |
1차 점검 |
주요 원인 |
| 파드 IP 미할당 |
VPC CNI |
IP 고갈 / ENI 제한 → Prefix Delegation |
| DNS 실패 |
DNS 해석 |
CoreDNS OOM, ndots:5 문제, DNSCache 부재 |
| 연결 불가 |
Service 연결 |
Selector 불일치, port / targetPort 설정 오류 |
| 차단 의심 |
NetworkPolicy |
AND vs OR 혼동, Default Deny 누락 |
보조 도구: Ingress/LB 재설계 시 Gateway API, 패킷 레벨 분석은 netshoot 컨테이너로 진단
VPC CNI: IP 고갈 & ENI 제한
| 문제 |
증상 / 내용 |
대응 |
| IP 고갈 |
서브넷의 사용 가능 IP 부족 → 파드가 ContainerCreating에서 멈춤 |
서브넷 확장 / Secondary CIDR |
| ENI 제한 |
인스턴스 타입별 ENI 수 × ENI당 IP 제한 (c5.xlarge: 4 ENI × 15 IP = 최대 58 파드) |
인스턴스 타입 상향 |
| Prefix Delegation |
ENI에 /28 prefix(16 IP) 할당 → IP 용량 16배 확대 (c5.xlarge: 최대 110 파드) |
아래 명령어로 활성화 |
진단 및 활성화
# 서브넷별 사용 가능 IP 확인
aws ec2 describe-subnets --subnet-ids <subnet-id> \
--query 'Subnets[].{ID:SubnetId,AZ:AvailabilityZone,Available:AvailableIpAddressCount}'
# Prefix Delegation 활성화 (IP 용량 16배)
kubectl set env daemonset aws-node -n kube-system ENABLE_PREFIX_DELEGATION=true
# 노드의 현재 ENI / 파드 수 확인
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, allocatable_pods: .status.allocatable.pods}'
Service 연결 실패 진단
flowchart TB
START([Service 연결 실패])
D1[Endpoints 확인]
D2A[Selector 라벨 불일치]
D2B[Pod Not Ready]
D3A[port / targetPort 불일치]
D3B[kube-proxy / SG]
START --> D1
D1 -->|Endpoints 비어있음| D2A
D1 -->|Endpoints 비어있음| D2B
D1 -->|IP 존재| D3A
D1 -->|IP 존재| D3B
style START fill:#ff4444,stroke:#cc3636,color:#fff
style D1 fill:#fbbc04,stroke:#c99603,color:#000
style D2A fill:#10b981,stroke:#0d8a63,color:#fff
style D2B fill:#10b981,stroke:#0d8a63,color:#fff
style D3A fill:#10b981,stroke:#0d8a63,color:#fff
style D3B fill:#10b981,stroke:#0d8a63,color:#fff핵심 진단 명령어
# Endpoints 확인 (비어있으면 Selector 불일치)
kubectl get endpoints <service-name>
# Service selector와 파드 label 비교
kubectl get svc <svc> -o jsonpath='{.spec.selector}'
kubectl get pods --show-labels
# 파드가 실제 리스닝하는 포트 확인
kubectl get pod <pod> -o jsonpath='{.spec.containers[*].ports[*].containerPort}'
DNS 디버깅: CoreDNS & ndots
| 문제 |
증상 |
대응 |
| ⚠️ CoreDNS OOM |
5,000+ Pod 클러스터에서 쿼리 급증 시 OOMKilled → 클러스터 전체 DNS 해석 실패 |
메모리 증가 + NodeLocal DNSCache |
🌐 ndots:5 문제 |
외부 도메인 조회 시 5번 불필요한 쿼리 (api.example.com → 4번 실패 후 성공) |
ndots:2 또는 trailing dot(.) 사용 |
| ⚡ NodeLocal DNSCache |
각 노드에 DNS 캐시 → CoreDNS 부하 감소 (VPC DNS 한도: ENI당 1,024 pkt/s) |
대규모 클러스터 필수 |
진단 명령어
# CoreDNS 상태 확인
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl top pods -n kube-system -l k8s-app=kube-dns
# 파드 내부 resolv.conf 확인 (ndots 값)
kubectl exec <pod> -- cat /etc/resolv.conf
# DNS 해석 테스트
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup kubernetes.default
NetworkPolicy 디버깅
| 함정 |
내용 |
| 🚫 Default Deny |
podSelector: {}로 모든 ingress 차단 - Allow 규칙 선작성 없이 적용하면 전체 트래픽 차단 |
| 🔀 AND vs OR 혼동 |
indent 한 레벨 차이로 보안 정책이 완전히 달라짐. 같은 from 항목 = AND(교집합), 별도 from 항목 = OR(합집합) |
| 🏷️ namespaceSelector 라벨 |
namespaceSelector는 네임스페이스에 라벨이 있어야 동작. kubectl label ns prod user=alice 필수 |
AND vs OR - indent 차이에 주의
# AND: "alice NS의 client 파드"만 허용
ingress:
- from:
- namespaceSelector: # ← 같은 from 항목
matchLabels: { user: alice }
podSelector: # ← AND 결합
matchLabels: { role: client }
# OR: "alice NS 전체" 또는 "모든 NS의 client Pod"
ingress:
- from:
- namespaceSelector: # ← 첫 번째 from
matchLabels: { user: alice }
- podSelector: # ← 두 번째 from (OR)
matchLabels: { role: client }
Gateway API 디버깅
flowchart LR
GC[GatewayClass]
GW[Gateway]
HR[HTTPRoute]
SVC[Service]
GC --> GW --> HR --> SVC
GC -.- C1[Accepted 조건
controller 매칭]
GW -.- C2[Programmed 조건
LB 프로비저닝]
HR -.- C3[ResolvedRefs 조건
parentRef / backendRef 매칭]
SVC -.- C4[Endpoints Health Check]
style GC fill:#4286f4,stroke:#2a6acf,color:#fff
style GW fill:#4286f4,stroke:#2a6acf,color:#fff
style HR fill:#4286f4,stroke:#2a6acf,color:#fff
style SVC fill:#4286f4,stroke:#2a6acf,color:#fff
style C1 fill:#fbbc04,stroke:#c99603,color:#000
style C2 fill:#fbbc04,stroke:#c99603,color:#000
style C3 fill:#fbbc04,stroke:#c99603,color:#000
style C4 fill:#fbbc04,stroke:#c99603,color:#000상태 점검 순서
# 1. GatewayClass 상태 확인 (Accepted 조건)
kubectl get gatewayclass
kubectl describe gatewayclass <name>
# 2. Gateway 상태 확인 (Programmed 조건 = LB 프로비저닝)
kubectl get gateway -A
kubectl describe gateway <name> -n <ns>
# 3. HTTPRoute 상태 확인 (ResolvedRefs = parentRef/backendRef 매칭)
kubectl get httproute -A
kubectl describe httproute <name> -n <ns>
# 4. 최종 Target Group Health 확인
aws elbv2 describe-target-health --target-group-arn <tg-arn>
netshoot 실전 디버깅
| 모드 |
내용 |
| Ephemeral Container |
기존 파드에 디버그 컨테이너 주입 - 파드 재시작 없이 네트워크 진단 |
| 포함 도구 |
curl, dig, tcpdump, iperf3, ss, traceroute, mtr, nslookup |
| 독립 디버깅 파드 |
임시 파드로 클러스터 내부 네트워크 테스트 - 종료 시 자동 삭제 (--rm) |
실전 사용법
# 기존 파드에 ephemeral container로 추가
kubectl debug <pod-name> -it --image=nicolaka/netshoot
# 독립 디버깅 파드 실행
kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot -- bash
# DNS 해석 확인
dig <service>.<namespace>.svc.cluster.local
# TCP 연결 테스트
curl -v http://<service>.<namespace>.svc.cluster.local:<port>/health
# 패킷 캡처 (특정 파드 IP 트래픽)
tcpdump -i any host <pod-ip> -n
# 소켓 상태 확인
ss -tunap
VI. PVC 마운트 실패 4대 패턴
개요
PVC 마운트가 실패하는 4가지 패턴을 살펴봅시다.
| 패턴 |
원인 |
대응 |
| AZ 불일치 |
EBS 볼륨은 단일 AZ에 존재 - 파드가 다른 AZ 노드에 스케줄되면 마운트 실패 |
volumeBindingMode: WaitForFirstConsumer |
| 볼륨 제한 초과 |
인스턴스 타입별 최대 EBS 볼륨 수 제한 (Nitro: 타입별 상이, 최대 128개) |
더 큰 인스턴스 타입 또는 볼륨 분산 |
| ReadWriteOnce (RWO) |
EBS는 RWO만 지원 - 여러 노드 동시 마운트 불가 → Multi-Attach 에러 |
다중 접근 필요 시 EFS (ReadWriteMany) |
| Detach 지연 (~6분) |
이전 파드 삭제 후에도 EBS 볼륨이 즉시 해제 안 됨 - AWS API detach 최대 6분 |
강제 detach는 데이터 손실 위험 - 기다릴 것 |
EBS vs EFS 비교
| 항목 |
EBS (gp3) |
EFS |
| 접근 모드 |
ReadWriteOnce (단일 노드) |
ReadWriteMany (다중 노드) |
| 성능 |
최대 16,000 IOPS / 1,000 MB/s |
탄력적 처리량 (Bursting / Provisioned) |
| AZ 제약 |
단일 AZ 종속 |
다중 AZ 자동 복제 |
| 적합 시나리오 |
DB, StatefulSet, 단일 파드 |
공유 파일, CMS, 로그 수집 |
| 비용 |
용량 + IOPS 기반 과금 |
사용량 기반 과금 (GB당) |
| CSI Driver |
ebs.csi.aws.com |
efs.csi.aws.com |
| Auto Mode |
내장 지원 (gp3 기본) |
EFS CSI 별도 설치 필요 |
| 주요 제약 |
Multi-Attach 불가, AZ 종속 |
Mount Target SG TCP 2049 필수 |
판단 기준: 여러 파드가 동시에 같은 볼륨에 접근해야 하면 EFS, 단일 파드 고성능 I/O가 필요하면 EBS (gp3)
스토리지 성능 최적화
| 주제 |
내용 |
| gp3 IOPS 튜닝 |
기본 3,000 IOPS / 125 MB/s → 최대 16,000 IOPS / 1,000 MB/s. IOPS : 처리량 비율 최소 4:1 |
| 볼륨 확장 |
allowVolumeExpansion: true 필수. PVC patch로 온라인 확장 가능 - 축소는 불가 |
| 고아 볼륨 정리 |
Finalizer 수동 제거 시 고아 EBS 볼륨 발생 → 주기적으로 available 상태 볼륨 확인 (비용 낭비 방지) |
실전 명령어
# gp3 고성능 StorageClass 생성
cat <<EOF | kubectl apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata: { name: fast-ebs }
provisioner: ebs.csi.aws.com
parameters: { type: gp3, iops: "16000", throughput: "1000" }
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
EOF
# 볼륨 확장 (온라인)
kubectl patch pvc <pvc> -p '{"spec":{"resources":{"requests":{"storage":"50Gi"}}}}'
# 고아 볼륨 찾기
aws ec2 describe-volumes \
--filters "Name=tag:kubernetes.io/created-for/pvc/name,Values=*" \
--query 'Volumes[?State==`available`].{ID:VolumeId,PVC:Tags[?Key==`kubernetes.io/created-for/pvc/name`]|[0].Value}'
Auto Mode 스토리지 제약
| 항목 |
동작 |
| 내장 EBS 드라이버 |
gp3 기본 지원 - 별도 CSI Driver 설치 불필요. WaitForFirstConsumer 자동, allowVolumeExpansion: true 기본 |
gp2 미지원 |
기본 gp3. gp2 StorageClass 사용 불가, io2 Block Express 미지원. 기본 EBS 암호화 제공 |
| EFS 별도 설치 |
EFS CSI Driver는 Auto Mode 자동 관리 대상 아님. ReadWriteMany 필요 시 수동 설치. FSx for Lustre도 별도 |
Auto Mode 지원 매트릭스
| 볼륨 타입 |
지원 상태 |
gp3 |
내장 지원 |
gp2 |
미지원 |
io2 |
제한적 (Block Express 미지원) |
| EFS |
별도 설치 필요 |
| FSx for Lustre |
별도 설치 필요 |
VII. GPU 진단
워크플로우 살펴보기
nvidia-smi → Driver → CUDA → Device Plugin → DCGM 순서로 계층별 진단
flowchart TB
START([GPU 문제 발생])
D1[nvidia-smi 실행?]
D2[GPU 인식됨?]
D3[CUDA 버전 호환?]
D4[Device Plugin 동작?]
D5[DCGM 메트릭 정상?]
A1[Driver 설치 확인]
A2[Driver/CUDA 매칭]
A3[ClusterPolicy 확인]
A4[워크로드 레벨 디버깅]
START --> D1
D1 -->|실패| A1
D1 -->|성공| D2
D2 -->|미인식| A1
D2 -->|인식됨| D3
D3 -->|불일치| A2
D3 -->|호환| D4
D4 -->|미동작| A3
D4 -->|정상| D5
D5 -->|정상| A4
style START fill:#ff4444,stroke:#cc3636,color:#fff
style D1 fill:#fbbc04,stroke:#c99603,color:#000
style D2 fill:#fbbc04,stroke:#c99603,color:#000
style D3 fill:#fbbc04,stroke:#c99603,color:#000
style D4 fill:#fbbc04,stroke:#c99603,color:#000
style D5 fill:#fbbc04,stroke:#c99603,color:#000
style A1 fill:#10b981,stroke:#0d8a63,color:#fff
style A2 fill:#10b981,stroke:#0d8a63,color:#fff
style A3 fill:#10b981,stroke:#0d8a63,color:#fff
style A4 fill:#10b981,stroke:#0d8a63,color:#fffCUDA XID 에러 패턴
| XID |
의미 |
원인 |
조치 |
| 31 |
GPU memory page fault |
잘못된 메모리 접근 |
드라이버 업데이트, 메모리 할당 검증 |
| 43 |
GPU stopped responding |
GPU 응답 없음 |
노드 재시작 |
| 48 |
Double bit ECC error |
하드웨어 메모리 결함 |
노드 교체 필수 (영구 결함) |
| 74 |
NVLink error |
GPU 간 통신 실패 |
NVLink 토폴로지 확인, 케이블 점검 |
| 79 |
GPU fallen off the bus |
PCIe 통신 단절 |
노드 교체 필수 (하드웨어 결함) |
| 94 |
Contained error |
메모리 무결성 오류 |
ECC 모드 확인, 노드 교체 검토 |
XID 에러 확인
# 커널 로그에서 XID 에러 검색
kubectl debug node/<gpu-node> -it --image=ubuntu
dmesg | grep -i "xid"
# 출력 예시 (하드웨어 결함)
# [123.456] NVRM: Xid (PCI:0000:10:1c): 79, GPU has fallen off the bus.
# → XID 48, 79: 즉시 노드 교체
NCCL Timeout 디버깅
| 체크 포인트 |
내용 |
| 멀티노드 통신 |
분산 학습 시 노드 간 NCCL 타임아웃 - 파드 간 통신 확인이 최우선 |
| EFA 설정 |
p4d/p5 인스턴스: EFA Device Plugin 설치 필수, vpc.amazonaws.com/efa 리소스 요청 |
| Security Group |
동일 SG 내부 모든 트래픽 허용 - SG self-referencing rule 확인 |
| 환경변수 |
NCCL_SOCKET_IFNAME, NCCL_IB_DISABLE 설정, WORLD_SIZE / RANK 매칭 필수 |
NCCL 디버그 환경변수
env:
- name: NCCL_DEBUG
value: "INFO" # 디버그 로그 활성화
- name: NCCL_DEBUG_SUBSYS
value: "ALL"
- name: NCCL_SOCKET_IFNAME
value: "eth0" # VPC CNI 기본 인터페이스
- name: NCCL_IB_DISABLE
value: "1" # EKS에서 InfiniBand 비활성화
# 파드 간 통신 테스트
kubectl exec -it <pod> -- nc -zv <target-pod-ip> 12345
vLLM 디버깅
| 주제 |
내용 |
gpu_memory_utilization |
기본 0.9 (GPU 메모리 90%). OOM 시 0.85 → 0.8 단계적 감소, 낭비 시 0.95까지 증가 |
| OOM vs KV Cache |
모델 로드 OOM → 더 큰 GPU 또는 Quantization (AWQ / GPTQ) / "No available blocks" → KV Cache 부족, max_model_len 감소 |
| Tensor Parallel |
--tensor-parallel-size = 파드 GPU 수 일치. H100×8 → TP=8 (70B 모델). TP 수는 hidden dim 약수 (2, 4, 8) |
vLLM 파라미터 튜닝 예시
args:
- --model=/models/llama-3.1-70b
- --tensor-parallel-size=4 # GPU 수와 일치
- --gpu-memory-utilization=0.85 # OOM 시 감소 (기본 0.9)
- --max-model-len=8192 # KV Cache 크기 결정
- --max-num-batched-tokens=8192 # 처리량/지연 균형
- --max-num-seqs=256 # 동시 처리 시퀀스 수
- --swap-space=4 # CPU 메모리 스왑 공간 (GiB)
튜닝 순서: OOM → gpu_memory_utilization 감소 → max_model_len 감소 → max_num_seqs 감소
GPU Operator 컴포넌트 구조
flowchart TB
CP[ClusterPolicy]
D[NVIDIA Driver]
CT[Container Toolkit]
DP[Device Plugin]
DE[DCGM Exporter]
GFD[GPU Feature Discovery]
OV[Operator Validator]
NODE([Node: nvidia.com/gpu])
CP --> D
CP --> CT
CP --> DP
CP --> DE
CP --> GFD
CP --> OV
D --> NODE
CT --> NODE
DP --> NODE
DE --> NODE
GFD --> NODE
OV --> NODE
style CP fill:#4286f4,stroke:#2a6acf,color:#fff
style D fill:#10b981,stroke:#0d8a63,color:#fff
style CT fill:#10b981,stroke:#0d8a63,color:#fff
style DP fill:#10b981,stroke:#0d8a63,color:#fff
style DE fill:#10b981,stroke:#0d8a63,color:#fff
style GFD fill:#10b981,stroke:#0d8a63,color:#fff
style OV fill:#10b981,stroke:#0d8a63,color:#fff
style NODE fill:#fbbc04,stroke:#c99603,color:#000진단 명령어
# ClusterPolicy 상태
kubectl get clusterpolicy -A
kubectl describe clusterpolicy gpu-cluster-policy
# 각 컴포넌트 파드 상태
kubectl get pods -n gpu-operator
# Driver 파드 로그 (설치 실패 시)
kubectl logs -n gpu-operator nvidia-driver-daemonset-<pod-id>
# 에러: "Kernel headers not found" → AMI에 kernel-devel 필요
# 에러: "nouveau driver is loaded" → nouveau 블랙리스트 필요
# Device Plugin 로그
kubectl logs -n gpu-operator nvidia-device-plugin-daemonset-<pod-id>
# 정상: "Detected NVIDIA devices: 8"
Auto Mode에서의 GPU 워크로드
| 제약 |
내용 |
devicePlugin=false 필수 |
Auto Mode는 GPU Driver 자동 관리. GPU Operator 설치 시 Device Plugin 충돌 - ClusterPolicy에서 반드시 비활성화 |
| 레이블 기반 격리 |
Auto Mode 노드에는 GPU Operator 스케줄 방지. MNG 노드에만 GPU Operator 배포 + Taint로 GPU 워크로드 격리 |
| 하이브리드 구성 (권장) |
Auto Mode = 일반 워크로드 / MNG (GPU) = GPU 전용. DCGM · GFD 메트릭은 정상 수집 가능 |
Auto Mode + GPU 하이브리드 설정
# ClusterPolicy: Device Plugin 비활성화 필수
apiVersion: nvidia.com/v1
kind: ClusterPolicy
spec:
driver:
enabled: true
devicePlugin:
enabled: false # ← Auto Mode 충돌 방지 핵심 설정
dcgm:
enabled: true # 메트릭 수집 가능
gfd:
enabled: true # GPU Feature Discovery 가능
# MNG 노드에 Taint 추가
# nvidia.com/gpu=true:NoSchedule
# GPU 파드에 Toleration 필수
GPU 진단 체크리스트
Step 1. GPU 인식 확인
Step 2. 스케줄링 확인
Step 3. vLLM OOM
Step 4. NCCL Timeout (멀티노드)
VIII. Auto Mode vs Standard Mode
개요
| 항목 |
Standard Mode |
Auto Mode |
디버깅 영향 |
| 노드 관리 |
사용자 (MNG / Karpenter) |
AWS 관리 (NodePool) |
NodePool CRD로 상태 확인 |
| VPC CNI |
수동 설정 / 업그레이드 |
자동 관리 |
Custom CNI 설정 불가 |
| GPU Driver |
GPU Operator 설치 |
AWS 관리 |
devicePlugin=false 필수 |
| 스토리지 |
EBS CSI 별도 설치 |
내장 (gp3) |
io2 BE 미지원, EFS 별도 |
| CoreDNS |
Add-on 관리 |
자동 관리 |
Custom 설정 제한 |
| 노드 SSH |
가능 (MNG) |
제한적 (SSM) |
kubectl debug node 필수 |
| Auto Scaling |
Karpenter / CA |
NodePool |
Spot 중단 자동 처리 |
핵심: Auto Mode는 편리하지만 커스터마이징 제한 - GPU, 고성능 스토리지, Custom CNI가 필요하면 하이브리드 구성 필수
NodePool & NodeClaim
| 리소스 |
역할 |
특징 |
| NodePool CRD |
Auto Mode의 노드 그룹 정의 |
인스턴스 타입 · 용량 타입 · AZ 제약 설정 / Karpenter NodePool과 유사 |
| 인스턴스 타입 선택 |
requirements에서 허용 인스턴스 지정 |
Spot + On-Demand 폴백 설정, 다양한 타입으로 가용성 확보 |
| NodeClaim |
실제 노드 요청 (EC2 인스턴스 1:1) |
Phase: Pending → Launched → Ready / Instance ID ↔ Node Name 매핑 |
진단 명령어
# NodePool 목록 및 상태
kubectl get nodepools
kubectl describe nodepool default
# NodeClaim 목록 (실제 노드 요청)
kubectl get nodeclaims -o wide
# NodeClaim과 Node 매핑
kubectl get nodeclaims -o json | \
jq -r '.items[] | "\(.metadata.name) → \(.status.nodeName)"'
# 인스턴스 타입 선택 실패 시: Pod 리소스 vs NodePool requirements 확인
kubectl describe pod <pending-pod>
kubectl get nodepool <name> -o yaml | grep -A 10 requirements
하이브리드 구성 시 주의점
| 이슈 |
핵심 대응 |
| MNG 추가 시 충돌 |
Auto Mode NodePool과 MNG가 같은 Pod를 경쟁 스케줄링 → 반드시 Taint/Toleration으로 분리 |
| 레이블 전략 |
MNG에 workload=gpu 레이블 → Pod nodeSelector로 명확 타겟팅. Auto Mode 노드에는 일반 워크로드만 |
| 테인트 분리 |
GPU MNG: nvidia.com/gpu=true:NoSchedule → GPU Pod만 Toleration으로 스케줄, 일반 Pod 침범 방지 |
| 하이브리드 패턴 |
Auto Mode = 웹 서버 · API · 배치 / GPU MNG = vLLM · 학습 Job · GPU Operator |
GPU MNG 생성 예시
aws eks create-nodegroup \
--cluster-name <cluster> \
--nodegroup-name gpu-nodes \
--instance-types g5.2xlarge g5.4xlarge \
--taints key=nvidia.com/gpu,value=true,effect=NO_SCHEDULE
NodeClaim 라이프사이클
flowchart LR
P[Pending]
L[Launched]
R[Registered]
I[Initialized]
RDY[Ready]
D[Drifted]
C[Consolidation]
T[Terminating]
DEL[Deleted]
P -->|EC2 시작| L
L -->|kubelet| R
R -->|Taints| I
I -->|Node Ready| RDY
RDY -->|AMI 변경| D
RDY -->|유휴 / 저활용| C
C --> D
D --> T
T --> DEL
style P fill:#fbbc04,stroke:#c99603,color:#000
style L fill:#4286f4,stroke:#2a6acf,color:#fff
style R fill:#4286f4,stroke:#2a6acf,color:#fff
style I fill:#4286f4,stroke:#2a6acf,color:#fff
style RDY fill:#10b981,stroke:#0d8a63,color:#fff
style D fill:#fbbc04,stroke:#c99603,color:#000
style C fill:#fbbc04,stroke:#c99603,color:#000
style T fill:#ff4444,stroke:#cc3636,color:#fff
style DEL fill:#ff4444,stroke:#cc3636,color:#fffNodeClaim 상태 모니터링
# NodeClaim 상태 확인
kubectl get nodeclaims -o wide
# NAME TYPE ZONE CAPACITY READY AGE
# default-abc c6i.2xlarge us-east-1a 8 True 2d
# Drift 상태 확인
kubectl get nodeclaims -o json | jq -r '.items[] |
select(.status.conditions[] | select(.type=="Drifted" and .status=="True"))
| .metadata.name'
# 교체 진행 모니터링
watch -n 5 'kubectl get nodeclaims -o wide'