13년차의 서버실

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

[태그:] Pending Pod

  • [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를 어떤 기준으로 나눠서 볼지, 그리고 워크로드별로 어떤 제약을 어디까지 허용할지 이어서 정리해보겠습니다.