[AEWS] AWS EKS 운영 하면서, 경험했던 장애/이슈에 대한 대응 및Karpenter 트러블슈팅

Hi yena·2026년 4월 13일

AEWS

목록 보기
12/14

"간단한" AWS EKS 운영 하면서 경험했던 장애/이슈

registry.k8s.io 장애 대응 — 외부 이미지 레지스트리 의존성의 위험

이슈 관련 링크

🔸 배경

사이트 광고가 종료되어 eks 리소스가 감소되던 중 registry.k8s.io 의 장애로 인해 서비스에 영향이 발생했습니다. 당시 ingress-nginx가 외부 이미지 레지스트리를 직접 참조하고 있었고, 파드 재시작 시 이미지를 pull 하지 못해 ImagePullBackOff 상태에 빠지는 상황이 있었습니다

🔸 후속 조치
① Private Image Registry 전환
외부 레지스트리 의존성을 제거하기 위해 ingress-nginx 가 참조하는 이미지를 private registry 전환하여 관리하도록 변경했다. (이미지 pull이 외부 네트워크 장애에 영향받지 않도록 격리)
② ingress-nginx PodDisruptionBudget 설정
노드 드레인이나 롤링 업데이트 시 ingress-nginx 파드가 동시에 중단되는 것을 막기 위해 PodDisruptionBudget 을 설정했다. minAvailable 으로 최소 파드는 항상 Running 상태를 유지하도록 보장.

너무 간단한 후기라 추가로 아래 내용 정리하겠습니다!


Karpenter를 운영하다 보면 노드 프로비저닝이 예상대로 되지 않아 Pod가 Pending 상태에 묶이는 상황을 마주치게 된다. 이 글은 Karpenter 공식 트러블슈팅 문서를 바탕으로, 실제로 자주 마주치는 문제들을 유형별로 정리한 것이다.


목차

  1. 디버그 로그 활성화
  2. 설치/IAM 관련 문제
  3. 프로비저닝 문제
  4. 노드 기동/Ready 문제
  5. Deprovisioning 문제
  6. EC2NodeClass 검증 캐시
  7. 격리 네트워크 환경의 Pricing 오류

1. 디버그 로그 활성화

문제가 발생했을 때 가장 먼저 해야 할 일은 로그 레벨을 debug로 올리는 것이다. Karpenter의 기본 로그 레벨은 info이므로 프로비저닝 실패의 세부 원인이 출력되지 않는 경우가 많다.

# 환경 변수로 즉시 변경
kubectl set env deployment/karpenter -n kube-system LOG_LEVEL=debug

# Helm 설치 시 옵션으로 지정하는 경우
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
  --set logLevel=debug \
  ...

주의: debug 로그는 엄청난 양의 로그를 생성하여 CloudWatch 비용 등에 영향을 줄 수 있다. 문제 분석이 끝나면 반드시 LOG_LEVEL=info로 되돌려야 한다.


2. 설치/IAM 관련 문제

2-1. Service Linked Role 미생성

EC2 Spot을 처음 사용하는 AWS 계정이라면, Spot용 Service Linked Role이 없어서 아래 오류가 발생한다.

AuthFailure.ServiceLinkedRoleCreationNotPermitted: The provided credentials do not have permission 
to create the service-linked role for EC2 Spot Instances

해결 방법은 해당 Spot용 Role을 직접 생성하면 된다.

aws iam create-service-linked-role --aws-service-name spot.amazonaws.com

2-2. STS 자격증명 I/O 타임아웃

Karpenter 설치 시 아래와 같은 오류가 나타난다면, Karpenter가 STS 엔드포인트에 접근하지 못하는 것이다. dnsPolicy: ClusterFirst로 설정된 상태에서 클러스터 내 DNS 서비스가 아직 기동되지 않았을 때 주로 발생한다.

WebIdentityErr: failed to retrieve credentials
caused by: Post "https://sts.us-east-1.amazonaws.com/": dial tcp: i/o timeout

두 가지 해결 방법이 있다.

방법 1: Karpenter의 dnsPolicyDefault로 변경하여 VPC DNS를 직접 사용하도록 한다.

helm upgrade --install karpenter ... --set dnsPolicy=Default

방법 2: MNG 또는 Fargate가 DNS 파드 용량을 충분히 확보하도록 설정한다.

2-3. NodePool/EC2NodeClass spec 필드 오류

Karpenter 버전을 업그레이드한 뒤 CRD가 변경된 경우, 아래와 같은 오류가 발생할 수 있다.

Error from server (BadRequest): strict decoding error: unknown field "spec.template.spec.nodeClassRef.foo"

이 경우 공식 Upgrade Guide에 따라 CRD를 업데이트해야 한다.


3. 프로비저닝 문제

3-1. no instance type met the scheduling requirements 로그

가장 흔히 마주치는 로그 메시지 중 하나다. 이 메시지의 의미는 다음 두 가지 중 하나이다.

Case A — 인스턴스 타입/리소스 조건 불충족

Pod의 리소스 요청량, NodePool의 인스턴스 타입 제약, DaemonSet의 리소스 오버헤드가 합쳐져서 어떤 인스턴스 타입도 조건을 충족하지 못하는 상태다. NodePool의 requirements에 허용 인스턴스 타입이 너무 좁게 설정되어 있지는 않은지 확인한다.

Case B — 특정 AZ에서 해당 인스턴스 타입의 Offering 부재

had a required offering 문구가 함께 나타나면 AZ 문제다. Stateful 워크로드에서 EBS 볼륨이 연결된 AZ와 Pod가 스케줄링되려는 AZ가 다를 때 자주 발생한다. NodePool의 topology.kubernetes.io/zone 설정과 실제 서브넷 AZ를 대조해봐야 한다.

3-2. Security Groups for Pods 사용 시 ContainerCreating 장기 지속

Security Groups for Pods를 사용하면 Karpenter가 노드를 정상 기동했음에도 Pod가 최대 30분 동안 ContainerCreating 상태에 머무는 현상이 발생할 수 있다. 이는 Karpenter와 amazon-vpc-resource-controller 간의 상호작용 문제다.

해결 방법은 NodePool에 아래 레이블을 추가하는 것이다.

apiVersion: karpenter.sh/v1
kind: NodePool
spec:
  template:
    metadata:
      labels:
        vpc.amazonaws.com/has-trunk-attached: "false"

3-3. PVC를 사용하는 Pod의 볼륨 한도 초과

PVC를 대량으로 사용할 때 볼륨 어태치 한도 초과로 스케일업에 실패할 수 있다. 두 가지 케이스가 있다.

인-트리 스토리지 플러그인 사용 중인 경우: Karpenter는 AWSElasticBlockStore 같은 인-트리 플러그인을 지원하지 않으며, 이 경우 노드의 최대 볼륨 어태치 수를 제대로 계산하지 못한다. 로그에 아래와 같은 에러가 나타나면 StorageClass와 PV를 CSI 드라이버로 마이그레이션해야 한다.

PersistentVolume source 'AWSElasticBlockStore' uses an in-tree storage plugin 
which is unsupported by Karpenter

Kubernetes 스케줄러-CSINode 레이스 컨디션의 경우: 노드 등록 시 스케줄러가 CSINode보다 먼저 실행되어 실제보다 더 많은 볼륨을 어태치할 수 있다고 판단하는 경우다. topologySpreadConstraintspodAntiAffinity를 적용해 단일 노드에 PVC가 집중되지 않도록 분산하는 것이 권장된다. EBS CSI 드라이버는 startupTaint를 지원하므로 NodePool에 아래와 같이 설정할 수 있다.

apiVersion: karpenter.sh/v1
kind: NodePool
spec:
  template:
    spec:
      startupTaints:
        - key: ebs.csi.aws.com/agent-not-ready
          effect: NoExecute

3-4. CNI의 IP 할당 실패

Pod가 ContainerCreating 상태에서 멈추고 아래와 같은 에러가 나타나면 CNI의 IP 할당 실패다.

failed to assign an IP address to container

주요 원인은 두 가지다.

maxPods 설정이 인스턴스 지원 Pod 밀도를 초과하는 경우: ENI 수와 ENI당 IP 수의 곱이 실제 Pod 수용 한도다. 해결 방법으로는 Prefix Delegation 활성화, maxPods 값 조정, 또는 Security Groups for Pods 사용 시 RESERVED_ENIS=1 설정이 있다.

서브넷 IP 주소 소진의 경우: 특정 서브넷의 IP가 고갈된 상태다. topologySpreadConstraints로 파드를 AZ에 분산하거나, 서브넷의 CIDR 범위를 늘리거나, IPv6 전환을 고려할 수 있다.

3-5. Topology Spread Constraint 충족 불가

아래 상황처럼 Pod의 NodePool 호환성과 실제 AZ 제약이 불일치하면 Karpenter가 일부 레플리카를 스케줄링하지 못한다.

  • NodePool A: 모든 AZ 허용
  • NodePool B: us-east-1a, us-east-1b만 허용
  • Pod: NodePool B만 호환, TopologySpreadConstraint로 3개 AZ에 분산 요구

이 경우 Pod 스펙에 nodeAffinity를 명시해 eligible 도메인을 NodePool 범위와 일치시켜야 한다.

nodeAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
      - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values: ['us-east-1a', 'us-east-1b']

4. 노드 기동/Ready 문제

4-1. 노드가 생성되지 않음

Karpenter 로그에서 아래와 같이 잘못된 블록 디바이스 매핑이나 기타 EC2 fleet 오류가 보인다면, 커스텀 Launch Template 설정을 점검해야 한다.

Could not launch node, launching instances, with fleet error(s), 
InvalidBlockDeviceMapping: Invalid device name /dev/xvda

4-2. 노드가 초기화되지 않음 (Nodes not initialized)

Karpenter는 아래 세 가지 조건이 모두 충족되어야 노드를 initialized로 판단한다.

  1. Node Readiness: Ready 컨디션이 True여야 한다.
  2. Expected 리소스 등록: ec2:DescribeInstanceTypes로 조회된 리소스가 node.status.allocatable에 모두 등록되어 있어야 한다. GPU 인스턴스에서 NVIDIA 플러그인 데몬이 없거나, vpc.amazonaws.com/pod-eni 리소스가 등록되지 않는 경우가 대표적이다.
  3. Startup Taint 제거: NodePool의 startupTaints가 노드 spec에서 완전히 제거되어야 한다.

4-3. Node NotReady

노드가 뜨긴 했는데 클러스터에 Join하지 못하는 경우다. IAM 권한, Security Group, 네트워킹 설정을 점검해야 한다. AL2 기반 노드라면 SSM을 통해 직접 접속하여 kubelet 로그를 확인하는 것이 가장 빠르다.

자주 보이는 kubelet 오류 패턴은 다음과 같다.

# CNI 초기화 실패 → IAM 권한 문제 가능성
KubeletNotReady runtime network not ready: NetworkPluginNotReady

# API 서버 등록 실패 → aws-auth ConfigMap 설정 문제 가능성
Unable to register node with API server: Unauthorized

aws-auth ConfigMap에 Karpenter 노드 Role이 등록되어 있는지 확인한다.

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

4-4. 암호화된 EBS 볼륨으로 인한 노드 즉시 종료

커스텀 Launch Template에서 암호화된 EBS 볼륨을 사용하거나, EC2NodeClass의 Block Device Mapping에 CMK 암호화가 설정된 경우, 노드가 생성 직후 종료될 수 있다. KMS 키 정책에 해당 IAM 주체가 허용되어 있지 않은 경우다. EBS가 권장하는 방식은 특정 Role을 명시하는 대신 kms:ViaService 조건으로 계정 전체에 접근을 허용하는 것이다.

4-5. NTH(Node Termination Handler)와의 충돌

Karpenter와 AWS Node Termination Handler를 함께 운영하는 경우, Spot 재조정 권고(Rebalance Recommendation)에 NTH가 반응해서 노드를 삭제하면 Karpenter가 동일한 인스턴스 타입으로 다시 프로비저닝하고, 또다시 재조정 권고가 발생하는 무한 루프가 생길 수 있다.

Karpenter 공식 권장사항은 Spot 노드 운영 시 Spot Rebalance Recommendation 대응을 비활성화하는 것이다. NTH를 유지해야 한다면 아래 설정을 적용한다.

enableSpotInterruptionDraining: false
enableRebalanceDraining: false

5. Deprovisioning 문제

5-1. 노드가 삭제되지 않는 경우

Karpenter가 노드를 삭제하지 않는 주요 원인은 다음과 같다.

초기화 미완료: karpenter.sh/initialized 레이블이 없는 노드는 Karpenter가 deprovisioning 대상으로 고려하지 않는다. 위의 노드 초기화 문제를 참고한다.

PDB(Pod Disruption Budget) 차단: Karpenter는 PDB를 존중하여 Pod를 강제 삭제하지 않는다. PDB의 minAvailable이나 maxUnavailable 설정으로 인해 파드 Eviction이 불가능한 경우 노드 삭제가 진행되지 않는다.

karpenter.sh/do-not-disrupt annotation: annotation이 붙은 Pod가 있는 노드는 Consolidation 대상에서 제외된다. 삭제를 원하면 해당 어노테이션을 제거한다.

스케줄링 제약으로 인한 Consolidation 불가: Pod를 다른 노드로 이동시키려 했을 때 inter-pod affinity/anti-affinity나 TopologySpreadConstraint로 인해 스케줄링이 불가한 경우, Consolidation이 실행되지 않는다.


6. EC2NodeClass 검증 캐시

IAM 권한을 수정하거나 서브넷/보안그룹 태그를 변경한 경우, Karpenter가 이전 상태를 캐시하고 있을 수 있다. 아래 명령어로 강제 재검증을 트리거할 수 있다.

kubectl annotate ec2nodeclass default \
  karpenter.sh/force-refresh="$(date +%s)" \
  --overwrite

7. 격리 네트워크 환경의 Pricing 오류

IGW나 NAT Gateway 없이 완전히 격리된 프라이빗 서브넷에서 Karpenter를 운영하는 경우, 아래와 같은 Pricing API 타임아웃 오류가 주기적으로 발생한다.

ERROR controller.aws.pricing  updating on-demand pricing
caused by: Post "https://api.pricing.us-east-1.amazonaws.com/": dial tcp: i/o timeout
using existing pricing data from 2022-08-17T00:19:52Z

Price List Query API의 VPC 엔드포인트가 존재하지 않기 때문에 발생하는 문제다. Karpenter는 바이너리에 내장된 가격 데이터를 폴백으로 사용하므로 즉각적인 장애로 이어지지는 않지만, 가격 데이터가 Karpenter 버전 업그레이드 시에만 갱신된다. 오류 메시지를 없애려면 환경 변수를 설정한다.

# 격리 VPC 환경에서 Pricing 조회 비활성화
AWS_ISOLATED_VPC=true
# 또는 --aws-isolated-vpc 플래그 사용

정리: 트러블슈팅 체크리스트

Karpenter로 인해 Pod가 Pending 상태에 머물거나 노드 프로비저닝이 실패할 때, 아래 순서로 확인하면 대부분의 문제를 빠르게 좁혀갈 수 있다.

단계확인 명령주목할 시그널
1. Pod 이벤트 확인kubectl describe pod <pod>FailedScheduling 여부
2. NodeClaim 존재 확인kubectl get nodeclaimNo resources found이면 Karpenter가 요청 자체를 안 만든 것
3. Karpenter 로그 확인kubectl logs -n kube-system -l app.kubernetes.io/name=karpenterERROR, WARN, discovered subnets []
4. debug 로그 활성화kubectl set env deployment/karpenter -n kube-system LOG_LEVEL=debug세부 원인 파악
5. EC2NodeClass 설정 확인kubectl get ec2nodeclass default -o yamlsubnetSelectorTerms, securityGroupSelectorTerms 태그
6. 실제 리소스 태그 대조aws ec2 describe-subnets --filters ...서브넷에 태그가 실제로 붙어 있는지
7. IAM 정책 확인aws sts decode-authorization-message누락된 권한 액션
8. NodePool 설정 검토kubectl get nodepool default -o yamlspot-only 여부, 허용 인스턴스 타입, AZ 범위

0개의 댓글