13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] Karpenter 디버깅

  • [Kubernetes] Karpenter 프로비저닝 실패 디버깅 실제 사례 분석

    [Kubernetes] Karpenter 프로비저닝 실패 디버깅 실제 사례 분석

    [Kubernetes] Karpenter 프로비저닝 실패 디버깅 실제 사례 분석

    Karpenter 프로비저닝 실패 때문에 새 파드(Pod, 쿠버네티스에서 배포되는 최소 실행 단위)는 계속 Pending 상태인데, 오토스케일링(auto scaling, 부하에 따라 자동으로 늘고 줄어드는 기능)은 움직이지 않고, 로그만 한참 들여다보셨던 적 있으신가요? 저는 홈랩이든 업무 환경이든 이런 상황을 몇 번 겪었는데요. 처음엔 “분명 노드가 부족한데 왜 안 뜨지?” 싶었고, 실제로는 Karpenter 설정 자체보다 IAM(Role, 권한 역할), 서브넷(Subnet, 네트워크 구간) 태그, 인스턴스 제약 조건 같은 바깥 조건에서 막히는 경우가 많더라고요. 이번 글에서는 제가 실제로 디버깅할 때 밟는 순서대로, Karpenter 프로비저닝 실패를 어떻게 좁혀가는지 정리해보겠습니다.

    특히 Kubernetes 노드 문제 해결이나 Karpenter 디버깅이 익숙하지 않은 분들은, 무작정 로그부터 보는 것보다 “스케줄링 실패 → 프로비저닝 판단 → 클라우드 리소스 생성 → 노드 조인(join, 클러스터 참여)” 이 흐름으로 보면 훨씬 덜 헷갈리실 거예요. 저도 처음엔 이걸 한 덩어리로 봐서 삽질 좀 했습니다 ㅎㅎ

    Karpenter 프로비저닝 실패 흐름을 보여주는 Kubernetes 아키텍처 다이어그램

    Karpenter 프로비저닝 실패가 어느 단계에서 발생하는지 한눈에 보여주는 개요 이미지입니다.

    Karpenter 프로비저닝 실패를 볼 때 먼저 이해해야 할 흐름

    쉽게 말해 Karpenter는 “스케줄되지 못한 워크로드가 있네? 그럼 조건에 맞는 노드를 하나 띄워보자” 하고 판단하는 컴포넌트입니다. 여기서 중요한 포인트는, 단순히 노드를 많이 만드는 도구가 아니라 스케줄링 제약 조건을 해석해서 그에 맞는 노드를 계산한다는 점입니다.

    문제가 나는 지점은 보통 4군데입니다

    1. 파드 스케줄링 조건이 너무 빡빡한 경우
      예: nodeSelector, affinity, toleration, resource request가 충돌합니다.
    2. Karpenter가 적절한 노드 후보를 계산하지 못하는 경우
      예: 인스턴스 타입 제약, capacity type, 아키텍처 제약이 과합니다.
    3. 클라우드 리소스 생성 단계에서 실패하는 경우
      예: IAM 권한, 보안 그룹(Security Group), 서브넷 태그, 용량 부족 등이 걸립니다.
    4. 노드는 생성됐지만 클러스터에 조인하지 못하는 경우
      예: 부트스트랩(user data), 인증, 네트워크 경로 문제가 있습니다.

    이 네 구간만 분리해서 봐도 디버깅 난이도가 확 내려갑니다. 실제로 써보니까 “Karpenter가 고장났다”기보다는, 주변 조건 하나가 꼬여서 연쇄적으로 실패하는 경우가 더 많았습니다.

    Kubernetes 노드 문제 해결을 위한 기본 점검 순서

    제가 현장에서 가장 먼저 하는 건 거창한 분석이 아니라 증상 분리입니다. 파드가 문제인지, Karpenter 컨트롤러(controller, 제어 루프) 판단이 문제인지, 아니면 클라우드 API 호출이 막히는지부터 확인해야 하거든요.

    1. Pending 파드부터 확인

    kubectl get pods -A
    kubectl describe pod <pod-name> -n <namespace>

    여기서 꼭 보는 건 Events입니다. 예를 들어 다음 같은 메시지가 보이면 방향이 잡힙니다.

    • 0/3 nodes are available: 기존 노드에 스케줄이 안 되는 상황입니다.
    • Insufficient cpu 또는 Insufficient memory: 리소스 부족입니다.
    • node(s) had untolerated taint: 톨러레이션(toleration, 테인트 허용 설정) 문제입니다.
    • node affinity/selector mismatch: 파드 조건이 너무 좁습니다.

    혹시 여기서 이미 affinity나 nodeSelector 충돌이 보이면, Karpenter 이전에 워크로드 정의부터 손봐야 합니다. 이걸 놓치면 Karpenter 로그만 하루 종일 보게 되거든요.

    2. Karpenter 로그 확인

    kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter --tail=200
    kubectl get events -A --sort-by=.lastTimestamp

    로그에서는 이런 식의 단서를 찾습니다.

    • 프로비저닝 후보를 계산했는지
    • 요구사항(requirements) 때문에 제외된 인스턴스 타입이 있는지
    • 클라우드 제공자(provider) 호출에서 에러가 나는지
    • 생성 이후 노드 등록(register)이 안 되는지

    제가 직접 해보니, 로그를 길게 읽는 것보다 에러 키워드를 먼저 잡는 게 훨씬 빨랐습니다. 예를 들면 access denied, no subnets found, insufficient capacity, launch template 관련 문구 같은 것들이요.

    3. 노드 생성 여부와 조인 여부 분리

    kubectl get nodes
    kubectl get nodeclaims
    kubectl get nodepools
    kubectl describe nodeclaim <name>

    환경에 따라 리소스 이름은 다를 수 있지만, 핵심은 같습니다. 클라우드 인스턴스는 떴는데 노드가 안 보이는지, 아니면 인스턴스 자체가 생성되지 않았는지를 나눠서 봐야 합니다. 이 차이가 엄청 큽니다.

    Karpenter 디버깅을 위한 파드 이벤트와 로그 비교 이미지

    파드 이벤트와 Karpenter 로그를 같이 놓고 보면 어디서 막히는지 훨씬 빨리 찾을 수 있습니다.

    실전 사례: Karpenter 디버깅을 이렇게 풀었습니다

    이건 제가 자주 보는 전형적인 패턴입니다. 특정 워크로드가 배포된 뒤 Pending 상태가 길게 이어졌고, 클러스터 오토스케일링 오류 분석이 필요했던 상황이었습니다. 기존 노드는 여유가 없었고, Karpenter가 새 노드를 올려줘야 정상인데 아무 변화가 없었죠.

    증상

    • 파드는 Pending 상태 유지
    • Karpenter는 동작 중
    • 기존 노드는 리소스 부족
    • 새 노드는 추가되지 않음

    제가 먼저 확인한 파드 스펙

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sample-workload
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: sample-workload
      template:
        metadata:
          labels:
            app: sample-workload
        spec:
          nodeSelector:
            kubernetes.io/arch: amd64
          tolerations:
            - key: "workload-type"
              operator: "Equal"
              value: "batch"
              effect: "NoSchedule"
          containers:
            - name: app
              image: nginx:stable
              resources:
                requests:
                  cpu: "1"
                  memory: "1Gi"

    겉으로 보면 큰 문제 없어 보이죠. 저도 처음엔 워크로드 쪽은 괜찮다고 생각했었습니다. 근데 여기서 Karpenter 쪽 제약과 같이 봐야 했습니다.

    확인한 Karpenter 측 제약 예시

    apiVersion: karpenter.sh/v1
    kind: NodePool
    metadata:
      name: general
    spec:
      template:
        spec:
          requirements:
            - key: kubernetes.io/arch
              operator: In
              values:
                - amd64
            - key: kubernetes.io/os
              operator: In
              values:
                - linux
            - key: karpenter.sh/capacity-type
              operator: In
              values:
                - spot

    여기서 문제는 단순 설정 문법이 아니었습니다. 워크로드는 특정 톨러레이션을 요구하고, NodePool은 spot만 허용하며, 실제 가용 영역이나 계정 조건상 적절한 인스턴스 풀이 너무 좁아진 상태였던 거죠. 게다가 서브넷 태그도 일부 누락돼 있었습니다. 결국 Karpenter 입장에서는 만들 수 있는 노드 후보가 사실상 거의 없었던 셈입니다.

    정리하면 원인은 두 개였습니다

    구분 문제 내용 영향
    네트워크 일부 서브넷 태그 누락 적절한 서브넷 탐색 실패
    용량 제약 spot만 허용하고 요구사항이 좁음 프로비저닝 후보 부족

    이런 경우 진짜 헷갈립니다. 로그에는 하나의 에러만 크게 보일 때도 있지만, 실제론 조건이 두세 개 겹쳐서 실패하는 경우가 많거든요.

    제가 적용했던 점검 명령어와 확인 포인트

    아래 명령어들은 특정 클라우드에 종속되지 않게, 쿠버네티스 관점에서 먼저 확인할 수 있는 것들입니다. 이 순서대로 보면 꽤 안정적으로 원인을 줄여갈 수 있었습니다.

    1. 파드 이벤트 확인
    2. Karpenter 로그에서 프로비저닝 시도 여부 확인
    3. NodePool/NodeClaim 조건 확인
    4. 생성된 노드 존재 여부 확인
    5. 클라우드 콘솔 또는 API에서 인스턴스 생성 실패 원인 확인
    kubectl describe pod <pod-name> -n <namespace>
    kubectl logs -n karpenter -l app.kubernetes.io/name=karpenter --since=30m
    kubectl get nodepools -o yaml
    kubectl get nodeclaims -o yaml
    kubectl get nodes -o wide

    여기서 중요한 포인트! Karpenter 디버깅은 “쿠버네티스 안쪽 정보”와 “클라우드 바깥 정보”를 같이 봐야 끝이 납니다. 쿠버네티스 쪽만 보면 “왜 안 되지?”에서 끝나고, 클라우드 쪽만 보면 “인스턴스가 왜 안 뜨지?”로 끝나더라고요.

    Karpenter 프로비저닝 실패 원인인 NodePool 제약 조건 다이어그램

    NodePool 제약 조건이 겹칠수록 프로비저닝 가능한 후보가 급격히 줄어드는 상황을 설명하는 이미지입니다.

    ⚠️ 실제로 많이 만나는 Karpenter 프로비저닝 실패 원인

    이 섹션은 제가 여러 번 겪으면서 “아, 이건 체크리스트로 빼야겠다” 싶었던 것들입니다. 운영 중인 분들이라면 특히 공감하실 거예요.

    1. 파드 요구사항이 너무 강한 경우

    • nodeSelector가 특정 아키텍처나 라벨만 강제
    • podAntiAffinity가 과도하게 설정됨
    • requests가 커서 맞는 노드가 매우 제한적
    • toleration이 없어서 tainted node에 못 올라감

    처음엔 Karpenter가 노드를 안 만드는 줄 알았는데, 실제론 만들어도 스케줄이 안 맞는 상태라 계산상 제외되는 경우가 있었습니다.

    2. NodePool 요구사항이 좁은 경우

    • 특정 인스턴스 계열만 허용
    • spot만 허용
    • 특정 zone만 허용
    • 아키텍처와 운영체제 조건이 불필요하게 제한됨

    이건 비용 최적화하려다 자주 생깁니다. 저도 spot 위주로 예쁘게 짜보려다가, 정작 트래픽이 몰릴 때 후보가 없어서 자동 확장이 안 된 적이 있었거든요. 그래서 운영 환경에서는 제약은 최소화하고, 꼭 필요한 정책만 남기는 쪽이 더 안전했습니다.

    3. 클라우드 리소스 검색 실패

    • 서브넷 태그 누락
    • 보안 그룹 태그 누락
    • IAM 권한 부족

    이 부분은 쿠버네티스 YAML만 아무리 봐도 안 나옵니다. 특히 태그 기반 디스커버리(discovery, 자동 탐색)를 쓰는 경우라면, 네트워크 리소스가 올바르게 식별되는지 꼭 확인하셔야 합니다.

    4. 노드 생성 후 조인 실패

    • 부트스트랩 스크립트 문제
    • 클러스터 API 엔드포인트 접근 문제
    • 노드 인증 관련 설정 누락

    이 경우는 인스턴스는 생성되는데 kubectl get nodes에 안 보입니다. 그래서 “Karpenter가 아무것도 안 했다”고 오해하기 쉬워요. 근데 사실 한 단계는 진행된 상태인 거죠.

    문제를 줄이기 위한 운영 팁

    실제로 써보니까 아래 네 가지가 제일 효과 있었습니다.

    • NodePool 제약을 처음부터 너무 촘촘하게 만들지 않기
    • 파드 requests/limits를 현실적으로 설정하기
    • 서브넷, 보안 그룹, IAM 같은 외부 조건을 체크리스트화하기
    • 이벤트와 로그를 함께 보기

    특히 Kubernetes 노드 문제 해결이 필요한 상황에서 오토스케일링 오류 분석은 재현이 어려운 경우가 많아서, 문제가 생겼을 때 바로 볼 수 있는 로그 수집 체계를 미리 만들어두는 게 좋습니다. 이전 글에서 다뤘던 클러스터 이벤트 정리 방식이 있다면 같이 묶어보셔도 좋고요. 이 부분은 다음 글에서 더 깊게 다뤄볼 예정입니다.

    검증: 수정 후 무엇을 확인했나

    저는 설정을 손본 뒤 항상 세 가지를 확인합니다. 단순히 노드가 떠도 끝이 아니거든요.

    1. Pending 파드가 실제로 Running으로 전환되는지
    2. 새 노드가 기대한 라벨과 용량으로 조인되는지
    3. 불필요하게 과한 스케일아웃이 일어나지 않는지
    kubectl get pods -A -w
    kubectl get nodes --show-labels
    kubectl top nodes

    문제가 해결되면 보통 흐름이 이렇게 바뀝니다.

    • Pending 파드 발생
    • Karpenter가 새 노드 계산 및 생성
    • 노드가 클러스터에 조인
    • 파드가 새 노드에 스케줄됨

    드디어 됐다! 하는 순간이 여기입니다. 특히 오래 Pending이던 워크로드가 새 노드에 바로 올라가는 걸 보면, 이거 진짜 편하더라고요. 다만 성공했다고 끝내지 말고, 왜 실패했는지 기록해두는 게 다음 장애 때 훨씬 큰 도움이 됩니다.

    Karpenter 프로비저닝 실패 해결 후 Pending 파드가 Running으로 전환된 결과 이미지

    수정 전후를 비교해 Pending 파드가 Running으로 바뀌는 결과 확인 이미지입니다.

    정리: Karpenter 디버깅은 순서가 전부입니다

    Karpenter 프로비저닝 실패를 잡을 때 가장 중요한 건, 증상을 한 번에 해석하려고 하지 않는 겁니다. 저도 처음엔 로그 한 줄에서 답을 찾으려 했는데, 실제론 파드 조건, NodePool 제약, 클라우드 리소스 검색, 노드 조인 이 네 단계를 분리해서 봐야 풀리더라고요.

    혹시 지금도 Kubernetes 노드 문제 해결 때문에 막혀 계시다면, 아래 체크리스트부터 차근차근 보시면 좋겠습니다.

    • 파드 Events에 스케줄링 실패 원인이 보이는가
    • Karpenter가 실제로 프로비저닝을 시도했는가
    • NodePool 제약이 과하지 않은가
    • 서브넷, 보안 그룹, IAM 조건이 맞는가
    • 생성된 노드가 클러스터에 정상 조인하는가

    이 순서만 익혀도 Karpenter 디버깅 속도가 꽤 빨라집니다. 저도 처음엔 헷갈렸는데, 몇 번 정리해두고 나니 장애 대응 시간이 확 줄었습니다. 마무리 전에 요약 하나 더 남겨볼게요.

    Karpenter 프로비저닝 실패 디버깅 체크리스트 인포그래픽

    Karpenter 프로비저닝 실패 원인과 대응 순서를 한 장으로 정리한 요약 이미지입니다.

    FAQ: 자주 헷갈리는 포인트

    Q1. 파드가 Pending이면 무조건 Karpenter 문제인가요?

    아닙니다. 파드 스펙의 affinity, toleration, resource request가 원인인 경우도 많습니다.

    Q2. 노드가 생성되지 않으면 어디부터 봐야 하나요?

    파드 이벤트, Karpenter 로그, NodePool 조건을 먼저 보고, 그 다음 클라우드 리소스 조건을 확인하는 게 좋습니다.

    Q3. spot만 허용해도 괜찮을까요?

    가능은 하지만, 워크로드 특성과 가용성 요구사항을 고려해야 합니다. 운영에선 제약을 너무 좁히면 Karpenter 프로비저닝 실패 가능성이 올라가더라고요.

    마무리

    이번 글은 특정 버전 문법을 깊게 파기보다는, 문제가 났을 때 어떤 순서로 생각해야 하는지에 집중해봤습니다. 사실 운영에서는 정답 YAML 하나보다, 실패를 분해해서 보는 관점이 더 오래 가거든요. 다음 글에서는 Karpenter와 Cluster Autoscaler를 어떤 기준으로 나눠서 볼지, 그리고 워크로드별로 어떤 제약을 어디까지 허용할지 이어서 정리해보겠습니다.

  • [k8s] Karpenter 오토스케일링 문제 해결: 노드 프로비저닝 실패 디버깅 가이드

    [k8s] Karpenter 오토스케일링 문제 해결: 노드 프로비저닝 실패 디버깅 가이드

    [Kubernetes] Karpenter 문제 해결: 노드 프로비저닝 실패 디버깅 가이드

    Karpenter 문제 해결 때문에 밤에 이벤트 로그 붙잡고 계신 적, 한 번쯤 있으시죠? 저도 처음엔 “오토스케일링은 붙여놓으면 알아서 되겠지” 했다가, 정작 트래픽이 몰리는 순간 Pod(파드, 쿠버네티스의 최소 배포 단위)는 Pending(펜딩, 스케줄 대기)인데 노드는 안 뜨고, Karpenter 로그는 뭔가 말은 많은데 핵심은 안 보이는 상황을 여러 번 겪었습니다. 특히 Kubernetes 노드 프로비저닝이 실패하면 서비스 지연이 바로 드러나기 때문에, 이 구간은 감으로 보면 안 되더라고요.

    이번 글은 AWS 환경에서 Karpenter를 운영할 때 자주 만나는 오토스케일링 실패 패턴을 기준으로 정리했습니다. 제가 홈랩과 실무 환경에서 직접 부딪히며 정리한 흐름이라, 문서만 읽었을 때보다 훨씬 빠르게 원인을 좁히는 데 도움이 되실 겁니다. 핵심은 하나입니다. Karpenter 디버깅은 “Pending Pod → 스케줄링 조건 → Karpenter 로그 → 클라우드 리소스” 순서로 봐야 한다는 점입니다.

    Karpenter 문제 해결을 위한 오토스케일링 아키텍처 개요 이미지

    Pending Pod, Karpenter controller, AWS 인프라 리소스가 어떻게 연결되는지 보여주는 전체 흐름도입니다.

    Karpenter 문제 해결, 왜 이렇게 헷갈릴까요?

    쉽게 말해 Karpenter는 “Pod가 요구하는 조건에 맞춰 새 노드를 빠르게 계산하고 띄워주는 컨트롤러”입니다. 그런데 실제로는 중간에 확인할 레이어가 꽤 많습니다. Kubernetes scheduler(스케줄러), Karpenter controller(컨트롤러), IAM(권한 체계), subnet/security group(서브넷/보안 그룹), instance type(인스턴스 타입) 조건, 그리고 quota(할당량)까지 다 연결돼 있거든요.

    저도 처음엔 Karpenter 로그만 뒤졌습니다. 근데 실제로 써보니까 문제의 출발점은 의외로 Pod 스펙인 경우가 많더라고요. 예를 들어 CPU 요청량이 과하게 잡혀 있거나, 특정 아키텍처만 허용했거나, taint/toleration(테인트/톨러레이션) 조합이 안 맞으면 Karpenter가 아무리 열심히 계산해도 답이 없습니다.

    • Pod 조건 문제: requests/limits, nodeSelector, affinity, toleration 충돌
    • Karpenter 설정 문제: NodePool(노드풀) 또는 기존 Provisioner(프로비저너) 제약이 너무 빡빡함
    • AWS 리소스 문제: IAM 누락, 태그 미설정, 서브넷 발견 실패
    • 용량 문제: 가용 영역 용량 부족, 인스턴스 타입 제한 과다, 계정 할당량 부족

    여기서 중요한 포인트! Karpenter 문제 해결은 “왜 노드가 안 떴지?”가 아니라 “왜 이 Pod를 만족하는 노드를 계산하지 못했지?”로 질문을 바꾸면 훨씬 빨라집니다.

    Karpenter 디버깅의 기본 원리

    제가 실제로 보는 순서는 거의 고정입니다. 삽질 좀 했습니다 ㅎㅎ 결국 순서를 정해두는 게 제일 낫더라고요.

    1. Pending 상태의 Pod를 확인합니다.
    2. 해당 Pod의 이벤트와 스케줄링 실패 메시지를 읽습니다.
    3. Karpenter controller 로그에서 같은 시점의 에러를 확인합니다.
    4. NodePool/EC2NodeClass 또는 기존 Provisioner/AWSNodeTemplate 조건을 검토합니다.
    5. AWS 측 리소스 발견, 권한, 용량 문제를 점검합니다.

    이 흐름을 안 지키면 로그는 잔뜩 보는데 정작 원인을 놓치기 쉽습니다. 특히 이벤트 메시지 한 줄이 전체 방향을 정해주는 경우가 많아요.

    Karpenter 디버깅 1단계: Pending Pod부터 확인하기

    가장 먼저 해야 할 일은 “안 뜨는 노드”가 아니라 “대기 중인 Pod”를 보는 겁니다. 아래 명령어부터 시작해 보세요.

    kubectl get pods -A --field-selector=status.phase=Pending
    kubectl describe pod -n <namespace> <pod-name>
    kubectl get events -A --sort-by=.lastTimestamp

    여기서 제가 제일 먼저 보는 건 다음 세 가지입니다.

    • Insufficient cpu/memory: 리소스 요청량이 현재/예상 노드와 맞지 않는 경우
    • node affinity mismatch: nodeSelector 또는 affinity 조건이 과도한 경우
    • untolerated taint: 노드 taint를 Pod가 허용하지 않는 경우

    예를 들어 이런 Pod 스펙은 흔히 문제를 만듭니다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: inflate
    spec:
      nodeSelector:
        kubernetes.io/arch: amd64
      containers:
        - name: pause
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
          resources:
            requests:
              cpu: "2"
              memory: "2Gi"

    이 자체는 틀린 YAML은 아닙니다. 다만 NodePool이 arm64만 허용하거나, 너무 작은 인스턴스 타입만 열어놨다면 Karpenter는 새 노드를 못 만듭니다. 결국 Kubernetes 노드 프로비저닝 실패처럼 보이지만, 시작점은 Pod 조건이죠.

    Karpenter 디버깅 2단계: 컨트롤러 로그에서 에러 패턴 찾기

    그다음은 Karpenter 로그입니다. 저는 보통 아래처럼 확인합니다.

    kubectl logs -n karpenter deploy/karpenter --since=30m
    kubectl logs -n karpenter deploy/karpenter --since=30m | grep -i "error"
    kubectl logs -n karpenter deploy/karpenter --since=30m | grep -i "provision"

    로그에서 자주 보는 키워드는 대략 이렇습니다.

    로그/증상 의미 우선 확인할 것
    no instance type met the scheduling requirements 조건에 맞는 인스턴스 타입이 없음 NodePool requirements, Pod requests
    insufficient capacity 가용 영역 또는 타입의 실시간 용량 부족 타입 범위 확대, AZ 분산
    access denied / unauthorized IAM 권한 문제 controller role, instance profile
    subnet/security group not found AWS 리소스 발견 실패 태그, selector 조건

    처음엔 이게 뭔가 싶었는데, 로그를 “읽는” 게 아니라 “분류”하기 시작하니까 속도가 확 달라지더라고요. 특히 access denied 계열은 쿠버네티스 문제가 아니라 거의 AWS 권한 문제였습니다.

    Karpenter 디버깅 과정에서 컨트롤러 로그와 NodePool을 점검하는 이미지

    Karpenter 컨트롤러 로그, NodePool 조건, EC2 관련 리소스를 함께 점검하는 디버깅 흐름 예시입니다.

    실전 구현: NodePool/Provisioner 조건 점검하기

    운영 중인 Karpenter 버전에 따라 리소스 이름이 다를 수 있습니다. 최근 계열은 NodePool/EC2NodeClass 조합을 많이 쓰고, 예전 문서에서는 Provisioner/AWSNodeTemplate 예제가 남아 있습니다. 그래서 제 습관은 “내 클러스터에 실제로 어떤 CRD가 있는지 먼저 보는 것”입니다.

    kubectl api-resources | grep -i karpenter
    kubectl get nodepools
    kubectl get ec2nodeclasses
    kubectl get provisioners

    예를 들어 NodePool이 너무 제한적으로 잡혀 있으면 문제가 생깁니다.

    apiVersion: karpenter.sh/v1
    kind: NodePool
    metadata:
      name: default
    spec:
      template:
        spec:
          requirements:
            - key: kubernetes.io/arch
              operator: In
              values: ["amd64"]
            - key: kubernetes.io/os
              operator: In
              values: ["linux"]

    여기서 흔한 실수는 이런 겁니다.

    • 인스턴스 패밀리 조건을 너무 좁게 설정
    • 특정 가용 영역만 허용
    • spot(스팟)만 허용했는데 해당 시점에 용량이 부족
    • daemonset(데몬셋) 오버헤드를 고려하지 않고 너무 작은 타입만 허용

    제가 직접 해보니 조건을 처음부터 정교하게 만드는 것보다, 일단 넉넉하게 열고 정상 프로비저닝을 확인한 뒤 점진적으로 좁히는 방식이 훨씬 덜 고생합니다.

    권장 점검 명령어

    kubectl describe nodepool <nodepool-name>
    kubectl get ec2nodeclass <class-name> -o yaml
    kubectl get provisioner <name> -o yaml

    설정에서 확인할 항목은 다음과 같습니다.

    1. 아키텍처 제약이 Pod와 맞는지
    2. 용량 타입(on-demand/spot) 제약이 과하지 않은지
    3. 서브넷/보안 그룹 선택 조건이 실제 AWS 태그와 일치하는지
    4. 노드 만료, 통합(consolidation) 정책이 과도하게 공격적이지 않은지

    ⚠️ 오토스케일링 실패를 만드는 대표 원인 5가지

    이 섹션은 제가 실제로 많이 봤던 순서대로 적겠습니다. Karpenter 디버깅에서 제일 시간을 많이 잡아먹는 부분이기도 합니다.

    1. IAM(권한 체계) 누락

    컨트롤러가 EC2 인스턴스를 생성하거나 조회할 권한이 없으면 노드가 당연히 안 뜹니다. 로그에 access denied, unauthorized 류 메시지가 보이면 거의 이쪽입니다. EKS에서 IRSA(IAM Roles for Service Accounts, 서비스어카운트용 IAM 역할)를 쓰는 경우, role annotation과 정책 연결 상태를 다시 확인해 보세요.

    2. Subnet/Security Group 태그 문제

    Karpenter는 보통 태그 기반으로 서브넷과 보안 그룹을 찾습니다. 근데 태그가 빠져 있거나 selector 조건과 안 맞으면 리소스를 발견하지 못합니다. 저는 이걸 네트워크 문제로 착각해서 한참 헤맸었는데, 사실은 태그 오타 하나였던 적도 있습니다.

    3. 인스턴스 타입 제한 과다

    예를 들어 특정 세대, 특정 패밀리, 특정 크기만 허용해 놓으면 실시간 수용 용량이 없을 때 바로 막힙니다. 운영에서는 후보군을 넓혀야 합니다. 특히 spot만 쓰는 환경은 더 그렇습니다.

    4. Pod 요청량 과다 또는 DaemonSet 오버헤드 미반영

    실제로 써보니까 작은 워크로드 하나만 보지 말고, CNI(컨테이너 네트워크 인터페이스), 로그 에이전트, 모니터링 에이전트 같은 데몬셋 자원까지 같이 봐야 하더라고요. 남는 것 같아도 실제 스케줄 가능 용량은 더 작습니다.

    5. AWS 계정 할당량 또는 AZ 용량 이슈

    설정이 멀쩡해도 특정 가용 영역에서 타입 수급이 안 될 수 있습니다. 이럴 땐 Karpenter 자체 버그보다 클라우드 용량 이슈일 가능성도 큽니다. 그래서 저는 가능한 한 여러 AZ와 여러 타입을 열어둡니다.

    kubectl get events -A --sort-by=.lastTimestamp
    kubectl describe pod -n <namespace> <pod-name>
    kubectl describe nodepool <nodepool-name>

    설정 예시: 디버깅용으로 조건을 단순화해보기

    문제가 길어질 때는 조건을 줄여서 정상 동작 여부부터 확인하는 게 좋습니다. 아래처럼 Pod를 단순화해서 테스트해 보세요.

    apiVersion: v1
    kind: Pod
    metadata:
      name: karpenter-debug
    spec:
      containers:
        - name: pause
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"

    이 Pod조차 스케줄이 안 되면, 애플리케이션 문제가 아니라 Karpenter 또는 인프라 레이어 문제로 보는 게 맞습니다. 반대로 이건 뜨는데 실제 서비스 Pod만 안 뜬다면, 그때는 requests/affinity/toleration을 더 자세히 보시면 됩니다.

    Kubernetes 노드 프로비저닝 점검을 위한 YAML 설정과 kubectl 디버깅 이미지

    디버깅용 Pod YAML, kubectl describe 출력, NodePool 설정을 함께 비교하는 장면을 표현한 이미지입니다.

    검증 방법: 노드가 정말 의도대로 프로비저닝됐는지 확인

    노드가 하나 떴다고 끝이 아닙니다. 저는 아래 순서로 꼭 확인합니다.

    1. 새 노드가 생성됐는지 확인
    2. Pending Pod가 Running으로 전환됐는지 확인
    3. 노드 라벨, taint, allocatable 자원이 기대와 맞는지 확인
    4. 불필요하게 큰 인스턴스가 뜨지 않았는지 확인
    kubectl get nodes
    kubectl get pods -A -o wide
    kubectl describe node <node-name>
    kubectl top nodes

    확인 포인트는 단순합니다.

    • Pod가 실제로 새 노드에 배치됐는가
    • 노드 라벨이 workload 조건과 맞는가
    • CPU/메모리 여유가 너무 적거나 너무 많지 않은가
    • 오토스케일링 실패가 반복되지 않는가

    드디어 됐다! 싶은 순간이 여기인데요, 여기서 끝내면 안 됩니다. 몇 분 후 consolidation이나 만료 정책 때문에 노드가 정리되면서 다시 Pending이 생길 수도 있거든요. 그래서 최소한 짧은 부하 테스트나 재배포 한 번은 꼭 해보시는 걸 권합니다.

    새 노드 생성 이후 Pending Pod가 Running으로 바뀌고 자원 사용량이 안정화된 결과 화면 예시입니다.

    운영 팁: 제가 실제로 적용하는 Karpenter 문제 해결 체크리스트

    혹시 이런 경험 있으신가요? 로그는 많은데 뭘 먼저 봐야 할지 몰라서 계속 왔다 갔다 하게 되는 상황이요. 그래서 저는 아래 체크리스트를 만들어 두고 그대로 봅니다.

    체크 항목 질문 판단 기준
    Pod 이벤트 왜 Pending인가? 스케줄링 실패 메시지가 명확해야 함
    Karpenter 로그 프로비저닝 계산을 했는가? instance type, subnet, 권한 관련 힌트 확인
    설정 제약 NodePool/Provisioner가 너무 좁은가? AZ, 타입, 아키텍처, 용량 타입 검토
    AWS 리소스 태그와 권한이 맞는가? subnet/security group 발견 가능해야 함
    검증 한 번 뜬 뒤 안정적으로 유지되는가? 재배포/부하 시 반복 확인

    이 체크리스트만 잘 지켜도 Karpenter 문제 해결 시간이 꽤 줄어듭니다. 그리고 이전 글에서 다룬 클러스터 리소스 요청량 설계 내용과 같이 보시면 더 이해가 쉬우실 겁니다. 다음 글에서는 Karpenter와 Cluster Autoscaler를 어떤 기준으로 나눠 볼지 정리해보려고 합니다.

    Karpenter 문제 해결 체크리스트를 요약한 인포그래픽 이미지

    Karpenter 문제 해결 순서를 한 장으로 요약한 체크리스트 인포그래픽입니다.

    정리: Karpenter 문제 해결은 순서가 전부입니다

    오늘 내용의 핵심은 복잡하지 않습니다. 오토스케일링 실패가 보이면 바로 AWS 콘솔부터 열지 말고, 먼저 Pending Pod 이벤트를 확인하세요. 그다음 Karpenter 로그, 그리고 NodePool/Provisioner 제약, 마지막으로 IAM과 네트워크 태그를 보는 흐름이 가장 효율적이었습니다.

    저도 처음엔 Karpenter가 너무 똑똑해서 알아서 다 해줄 줄 알았는데, 실제로는 “조건을 어떻게 열고, 어떤 로그를 먼저 읽느냐”가 훨씬 중요하더라고요. 특히 Kubernetes 노드 프로비저닝 이슈는 한 군데만 봐서는 잘 안 풀립니다. 대신 순서를 지키면 생각보다 금방 풀립니다.

    혹시 지금도 Karpenter 디버깅 중이시라면, 오늘 소개한 명령어 세트부터 그대로 실행해 보세요. 그리고 Pod 이벤트 한 줄, Karpenter 로그 한 줄만 정확히 읽어도 방향이 확 잡힐 겁니다. 이거 진짜 편하더라고요.