Skip to main content

GPU 비용은 왜 항상 예상보다 크게 나오는가

AI 인프라 비용을 처음 마주한 팀은 대부분 비슷한 표정을 짓는다. 개발 환경에서는 "GPU 한두 장이면 되겠지"라고 생각한다. 데모도 잘 돈다. 몇 명이 동시에 써도 문제없다. 그런데 프로덕션에 올리는 순간 계산이 달라진다. 낮에는 사용자가 몰리고, 밤에는 배치 임베딩이 돌고, 새 모델을 테스트하는 팀은 별도 GPU를 요구한다. 추론 서버는 사용자가 없을 때도 모델을 VRAM에 올린 채 대기한다. 학습 잡은 10시간 돌다가 실패하고, 실패한 체크포인트를 다시 읽느라 스토리지와 네트워크 비용까지 늘어난다.

GPU 비용이 무서운 이유는 단순히 시간당 단가가 높아서가 아니다. GPU는 "조금만 낭비해도" 비용이 크게 보인다. CPU 노드 한두 대가 한 시간 놀고 있는 것과 H100 노드 한 대가 한 시간 놀고 있는 것은 청구서에서 전혀 다른 무게를 가진다. 특히 쿠버네티스에서는 파드가 nvidia.com/gpu: 1을 요청하는 순간, 그 파드가 GPU를 10%만 쓰더라도 스케줄러 관점에서는 GPU 한 장이 통째로 예약된다. GPU 사용률이 낮은데도 클러스터는 "이미 꽉 찼다"고 판단할 수 있다.

2026년 쿠버네티스 최적화 보고서들이 공통으로 지적하는 문제도 여기에 있다. AI 워크로드가 쿠버네티스로 올라오고 있지만, 많은 기업 클러스터에서 GPU 평균 사용률은 여전히 매우 낮게 측정된다. Cast AI의 2026년 보고서는 최적화되지 않은 쿠버네티스 클러스터에서 GPU 사용률이 평균 5% 수준에 머문다고 분석했다.1 숫자 자체는 표본과 측정 방식에 따라 달라질 수 있지만, 메시지는 분명하다. GPU 부족보다 먼저 봐야 할 것은 "이미 산 GPU를 제대로 쓰고 있는가"다.

이 장의 목표는 GPU 비용을 무작정 줄이는 것이 아니다. 비용을 줄인다는 말은 종종 위험한 방향으로 흐른다. 필요한 GPU를 줄여서 지연 시간이 늘어나고, 개발팀이 실험을 못 하게 되고, 장애 대응 여유 용량까지 없애는 식이다. 이 장에서 말하는 최적화는 다르다. 같은 돈으로 더 많은 토큰을 처리하고, 같은 GPU로 더 많은 모델을 안정적으로 운영하며, 비용 증가를 사전에 통제하는 운영 체계를 만드는 것이다.

핵심 관점은 하나다.

GPU 비용은 시간당 인스턴스 가격이 아니라 tokens-per-second-per-dollar로 봐야 한다.

시간당 10달러짜리 GPU가 비싸 보이더라도 초당 5,000토큰을 안정적으로 처리하면 시간당 3달러짜리 GPU보다 경제적일 수 있다. 반대로 저렴한 GPU를 여러 대 붙였는데 모델 로딩, 네트워크 병목, 낮은 배치 효율 때문에 초당 토큰 처리량이 낮다면 싸게 산 것이 아니다. AI 인프라의 FinOps는 이 차이를 숫자로 드러내는 일에서 시작한다.

GPU 비용 최적화와 FinOps

시간당 비용에서 토큰당 비용으로

전통적인 클라우드 비용 관리는 인스턴스 단가에서 출발했다. g5.xlarge가 시간당 얼마인지, A100 노드가 월 얼마인지, 예약 인스턴스를 쓰면 몇 퍼센트 싸지는지를 계산했다. 이 방식은 웹 서버나 배치 워커에는 꽤 잘 맞았다. CPU와 메모리 사용량이 비용과 비교적 선형적으로 움직였기 때문이다.

LLM 추론에서는 다르다. 같은 GPU라도 모델 크기, 양자화 방식, 컨텍스트 길이, 동시 요청 수, KV 캐시 효율, 배치 전략에 따라 처리량이 크게 달라진다. 따라서 다음 질문을 먼저 던져야 한다.

"이 GPU 한 시간으로 몇 개의 의미 있는 결과를 만들었는가?"

이를 가장 단순하게 표현하면 다음과 같다.

tokens_per_second_per_dollar
= 초당 생성 또는 처리한 토큰 수 / 초당 GPU 비용

시간 단위로 보면 이렇게 바꿀 수 있다.

tokens_per_dollar
= 한 시간 동안 처리한 총 토큰 수 / 시간당 인프라 비용

여기서 인프라 비용에는 GPU 노드 비용만 넣으면 안 된다. 운영 관점에서는 적어도 다음 항목을 함께 봐야 한다.

  • GPU 노드 비용: 온디맨드, Spot, 예약 인스턴스, Savings Plan, 베어메탈 감가상각 비용

  • 주변 비용: CPU 노드, 로드밸런서, 스토리지, 모델 아티팩트 저장소, 로그와 메트릭 저장 비용

  • 데이터 이동 비용: AZ 간 트래픽, 리전 간 복제, 모델 다운로드, 벡터 DB와의 통신 비용

  • 운영 비용: 장애 대응, 수동 스케일링, 실험 환경 방치로 인한 인건비성 비용

물론 모든 항목을 처음부터 완벽하게 계산할 필요는 없다. 처음에는 GPU 노드 비용과 토큰 처리량만 잡아도 충분하다. 중요한 것은 "GPU가 몇 장인가"에서 "GPU가 얼마의 가치를 만들었는가"로 질문을 바꾸는 것이다.

LLM 추론에서는 토큰 처리량만으로도 부족하다. 사용자는 전체 처리량이 높아도 첫 응답이 늦으면 느리다고 느낀다. 그래서 비용 지표는 성능 지표와 짝을 이뤄야 한다. 특히 다음 지표를 함께 본다.

  • GPU utilization: GPU 연산 유닛이 실제로 얼마나 바쁘게 일했는지

  • VRAM utilization: 모델 가중치와 KV 캐시가 GPU 메모리를 얼마나 차지하는지

  • queue depth: 대기 중인 요청이나 작업이 얼마나 쌓여 있는지

  • TTFT(Time To First Token): 사용자가 요청한 뒤 첫 토큰이 나오기까지 걸리는 시간

  • ITL(Inter-Token Latency): 토큰과 토큰 사이의 생성 간격

  • token throughput: 초당 처리한 입력·출력 토큰 수

FinOps Foundation도 AI/ML 워크로드에서 성능 SLO와 비용 KPI를 함께 보는 "dual-signal autoscaling"을 강조한다. 즉 지연 시간만 보고 스케일아웃하면 비용이 폭발하고, 비용만 보고 스케일인을 하면 서비스 품질이 무너진다.2

좋은 대시보드는 다음과 같은 질문에 답해야 한다.

"지난 24시간 동안 chat-prod 모델은 GPU 비용 100달러로 몇 토큰을 처리했는가? 그때 p95 TTFT는 얼마였는가? GPU 사용률은 높았는데 VRAM 사용률이 낮았는가, 아니면 반대였는가?"

이 질문에 답할 수 있으면 비용 최적화는 감이 아니라 엔지니어링이 된다.

낭비의 네 가지 얼굴

GPU 비용 최적화를 시작하면 "GPU 사용률을 올리자"라는 말이 가장 먼저 나온다. 맞는 말이지만 충분하지 않다. GPU 사용률 하나만 보고 판단하면 잘못된 결론에 빠질 수 있다. 예를 들어 GPU 사용률이 95%라고 해서 반드시 좋은 상태는 아니다. 요청 큐가 길게 쌓이고 TTFT가 악화되고 있다면, 그것은 효율이 아니라 포화다. 반대로 GPU 사용률이 30%라도 초저지연 서비스에서 여유 용량을 의도적으로 둔 것이라면 낭비가 아닐 수 있다.

운영자는 낭비를 적어도 네 가지로 나눠 봐야 한다.

유휴 GPU

가장 쉬운 낭비는 유휴 GPU다. 노드는 떠 있고 비용은 발생하지만, GPU 사용률이 낮고 큐도 비어 있다. 추론 서비스가 밤새 사용자 요청 없이 대기하거나, 학습 잡이 끝났는데 노드가 내려가지 않는 경우가 여기에 해당한다.

유휴 GPU는 오토스케일링과 노드 통합으로 해결한다. KEDA로 파드를 0까지 줄이고, Karpenter나 Cluster Autoscaler가 빈 GPU 노드를 제거하게 만든다. 단, 실시간 추론 서비스는 콜드 스타트 비용이 크다. 모델 가중치 수십 GB를 다운로드하고 로딩하는 데 몇 분이 걸릴 수 있다. 따라서 모든 서비스를 scale-to-zero로 만들기보다, 지연 허용도가 높은 엔드포인트부터 적용해야 한다.

VRAM 병목

두 번째는 VRAM 병목이다. GPU 연산 사용률은 낮은데 VRAM이 꽉 차서 더 많은 요청을 받지 못하는 상황이다. LLM 추론에서는 흔하다. 모델 가중치와 KV 캐시가 메모리를 차지하기 때문이다. 컨텍스트 길이를 무작정 크게 잡으면 실제 사용자는 짧은 질문을 하는데도 서버는 긴 컨텍스트를 처리할 수 있도록 많은 메모리를 예약한다.

이때는 GPU를 더 사기 전에 컨텍스트 길이, KV 캐시 설정, 양자화, 배치 크기를 봐야 한다. GKE의 LLM 추론 최적화 가이드도 양자화, 텐서 병렬 처리, 모델 메모리 최적화를 비용 효율을 높이는 핵심 방법으로 제시한다.3 예를 들어 --max-model-len을 실제 서비스 요구에 맞게 줄이는 것만으로 더 작은 GPU에서 같은 모델을 운영할 수 있다.

큐 적체

세 번째는 큐 적체다. GPU 사용률도 높고 VRAM도 꽉 차 있으며 요청 큐가 계속 늘어난다. 이때 비용을 줄이겠다고 파드를 줄이면 장애가 된다. 큐 적체는 비용 낭비라기보다 용량 부족 신호다. 다만 큐가 쌓이는 이유를 구분해야 한다. 정말 트래픽이 늘었는지, 배치 설정이 비효율적인지, 특정 긴 프롬프트가 전체 배치를 막고 있는지, 디스패처가 잘못된 모델 서버로 요청을 보내는지 확인해야 한다.

큐 적체 상태에서는 tokens-per-dollar보다 SLO가 먼저다. 비용 최적화는 서비스가 정상 상태로 돌아온 뒤에 해야 한다.

과도한 오버프로비저닝

네 번째는 오버프로비저닝이다. 사용량 대비 GPU를 너무 크게 잡거나, 개발·테스트 환경에 프로덕션과 같은 GPU를 배정하는 경우다. 7B 임베딩 모델이 H100 전체를 독점하고 있다면 거의 확실히 낭비다. 개발자가 하루에 몇 번 테스트하는 모델 서버가 24시간 A100을 붙잡고 있다면, 그것도 낭비다.

오버프로비저닝은 MIG, time-slicing, fractional GPU, 작은 GPU 타입, 양자화로 해결한다. 비용 최적화의 가장 큰 성과는 보통 여기서 나온다. "GPU를 덜 쓴다"가 아니라 "작은 일에 큰 GPU를 쓰지 않는다"가 핵심이다.

GPU 공유 전략: MIG, time-slicing, fractional GPU

쿠버네티스에서 GPU 비용이 낭비되는 가장 오래된 이유는 GPU가 정수 리소스로 보였기 때문이다. 파드가 nvidia.com/gpu: 1을 요청하면 GPU 한 장이 통째로 할당된다. 모델이 VRAM 8GB만 쓰더라도 80GB GPU 전체가 묶일 수 있다. 이 문제를 풀기 위해 등장한 대표 전략이 MIG와 time-slicing이다.

MIG: 격리가 필요한 프로덕션 추론의 기본 선택

MIG(Multi-Instance GPU)는 A100, H100 같은 NVIDIA GPU를 하드웨어 수준에서 여러 개의 작은 인스턴스로 나누는 기능이다. 각 인스턴스는 독립된 메모리와 캐시, 연산 자원을 가진다. 쿠버네티스에서는 이 MIG 인스턴스를 별도의 GPU 리소스처럼 요청할 수 있다.

예를 들어 작은 임베딩 모델이나 경량 분류 모델은 전체 A100이 필요 없을 수 있다. 이때 1g.10gb 또는 2g.20gb 같은 MIG 프로파일로 나누면 한 장의 GPU에서 여러 모델을 안정적으로 운영할 수 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: embedding-service
  namespace: ai-serving
spec:
  replicas: 3
  selector:
    matchLabels:
      app: embedding-service
  template:
    metadata:
      labels:
        app: embedding-service
        model: embedding-v2
        cost-center: search
    spec:
      nodeSelector:
        gpu.nvidia.com/mig-enabled: "true"
      tolerations:
        - key: "nvidia.com/gpu"
          operator: "Exists"
          effect: "NoSchedule"
      containers:
        - name: server
          image: registry.example.com/embedding:v2
          resources:
            requests:
              cpu: "2"
              memory: 8Gi
              nvidia.com/mig-1g.10gb: 1
            limits:
              cpu: "4"
              memory: 16Gi
              nvidia.com/mig-1g.10gb: 1

MIG의 장점은 예측 가능성이다. 한 워크로드가 갑자기 GPU를 많이 써도 다른 MIG 인스턴스에 직접적인 영향을 주지 않는다. 그래서 프로덕션 추론, 유료 고객 트래픽, SLA가 있는 엔드포인트에는 MIG가 잘 맞는다. NVIDIA도 프로덕션 환경에서 엄격한 격리와 지연 시간 보장이 필요할 때 MIG가 time-slicing보다 적합하다고 설명한다.4

단점은 유연성이 낮다는 점이다. 한 번 나눈 MIG 파티션은 필요에 따라 즉시 자유롭게 크기를 바꾸기 어렵다. 1g.10gb 인스턴스들이 놀고 있는데 옆의 큰 모델이 더 많은 메모리를 빌려 쓰는 식의 탄력적 공유도 어렵다. 따라서 MIG는 워크로드 패턴이 어느 정도 안정된 프로덕션 추론 풀에 먼저 적용하는 것이 좋다.

Time-slicing: 간헐적 개발·테스트 환경의 비용 절감 도구

Time-slicing은 여러 파드가 하나의 GPU를 시간 단위로 나눠 쓰게 하는 방식이다. 하드웨어를 물리적으로 쪼개는 것이 아니라, GPU 실행 시간을 번갈아 사용하는 개념에 가깝다. GPU를 지원하는 개발 환경, 테스트 추론, 간헐적 배치 작업에는 매우 유용하다.

예를 들어 개발자 4명이 각자 작은 모델을 테스트한다고 하자. 모두가 동시에 GPU를 100% 쓰지는 않는다. 이때 각자에게 A100 한 장씩 주면 비용은 네 배가 되지만 대부분의 시간 GPU는 놀고 있다. Time-slicing을 적용하면 한 장의 GPU를 여러 "공유 슬롯"처럼 노출할 수 있다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config
  namespace: gpu-operator
data:
  any: |-
    version: v1
    sharing:
      timeSlicing:
        resources:
          - name: nvidia.com/gpu
            replicas: 4

이 설정은 실제 GPU 한 장을 쿠버네티스에 네 개의 공유 가능한 GPU처럼 보이게 만든다. 파드는 여전히 nvidia.com/gpu: 1을 요청하지만, 그것은 물리 GPU 전체가 아니라 time-slicing 슬롯 하나를 의미한다.

apiVersion: v1
kind: Pod
metadata:
  name: dev-llm-test
  namespace: ai-dev
spec:
  containers:
    - name: notebook
      image: registry.example.com/llm-dev:latest
      resources:
        requests:
          cpu: "4"
          memory: 16Gi
          nvidia.com/gpu: 1
        limits:
          cpu: "8"
          memory: 32Gi
          nvidia.com/gpu: 1

Time-slicing의 장점은 밀도다. 작은 작업을 많이 올릴 수 있다. 단점은 격리다. 한 파드가 GPU를 오래 점유하면 다른 파드의 지연 시간이 흔들릴 수 있다. 메모리 격리도 MIG만큼 강하지 않다. 따라서 운영 기준은 명확해야 한다.

프로덕션 실시간 추론에는 MIG를 우선 적용한다. 개발·테스트, 내부 도구, 간헐적 실험에는 time-slicing을 우선 적용한다. 지연 시간 변동이 허용되는 배치 작업은 time-slicing을 검토하되, 학습처럼 장시간 GPU를 꽉 채우는 작업에는 오히려 전체 GPU를 주는 편이 나을 수 있다.

Fractional GPU와 DRA의 방향

MIG와 time-slicing은 모두 "GPU를 나눠 쓰기" 위한 방법이다. 2026년의 흐름은 여기서 한 걸음 더 나아가 GPU를 더 구조적으로 요청하고 할당하는 방향으로 움직이고 있다. 앞 장에서 다룬 DRA(Dynamic Resource Allocation)는 GPU 같은 특수 하드웨어를 단순한 정수 리소스가 아니라 속성과 정책을 가진 장치로 다루게 해준다.

운영자가 원하는 최종 상태는 이런 것이다.

apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: small-inference-gpu
spec:
  spec:
    devices:
      requests:
        - name: gpu
          deviceClassName: mig-small-inference
          count: 1

애플리케이션 팀은 "A100의 몇 번째 MIG 파티션"을 알 필요가 없다. 대신 "작은 추론용 GPU 클래스 하나"를 요청한다. 플랫폼 팀은 그 뒤에서 MIG, time-slicing, 특정 GPU 세대, 토폴로지 정책을 관리한다. 이것이 AI 인프라에서 IDP와 FinOps가 만나는 지점이다. 개발자에게는 단순한 선택지를 주고, 플랫폼은 비용 효율적인 배치를 자동으로 만든다.

모델 자체를 가볍게 만드는 비용 최적화

GPU 비용을 줄이는 이야기를 하면 인프라만 떠올리기 쉽다. 하지만 LLM 추론 비용의 절반은 모델 선택에서 결정된다. 너무 큰 모델, 너무 긴 컨텍스트, 불필요하게 높은 정밀도는 쿠버네티스가 아무리 좋아도 비싸다.

모델 최적화는 이 책의 주제가 아니지만, 운영자가 알아야 할 최소한의 기준은 있다.

첫째, 모델 크기는 비즈니스 문제에 맞아야 한다. 모든 요청에 70B 모델이 필요하지 않다. 분류, 라우팅, 간단한 요약, 임베딩, 문서 태깅은 더 작은 모델로 충분한 경우가 많다. 큰 모델은 사용자가 체감하는 품질 차이가 분명한 구간에 배치해야 한다.

둘째, 양자화는 비용 최적화의 강력한 도구다. FP16 모델을 INT8 또는 INT4로 낮추면 VRAM 요구량이 크게 줄어든다. VRAM 요구량이 줄면 더 작은 GPU를 쓰거나, 같은 GPU에 더 많은 인스턴스를 올릴 수 있다. 물론 품질 손실 가능성이 있으므로 오프라인 평가와 A/B 테스트가 필요하다.

셋째, 컨텍스트 길이를 제한해야 한다. "최대 128K 컨텍스트 지원"은 제품 문구로는 멋지지만, 모든 요청에 그 길이를 허용하면 비용이 빠르게 증가한다. 대부분의 요청이 4K 안에 들어간다면 기본 한도를 4K 또는 8K로 두고, 긴 문서 분석 같은 특수 엔드포인트만 별도로 분리하는 편이 낫다.

넷째, 캐시를 활용해야 한다. 동일하거나 비슷한 요청을 매번 LLM에 보내면 비용은 선형으로 증가한다. RAG 시스템에서는 검색 결과 캐시, 임베딩 캐시, 세맨틱 캐시, 프롬프트 템플릿 캐시를 조합할 수 있다. 이 부분은 쿠버네티스 리소스 최적화보다 애플리케이션 설계에 가깝지만, tokens-per-dollar을 크게 끌어올리는 실전 수단이다.

중요한 것은 모델 최적화와 인프라 최적화를 분리하지 않는 것이다. 양자화로 VRAM 요구량이 줄면 MIG 프로파일을 바꿀 수 있다. 컨텍스트 길이를 줄이면 배치 크기를 늘릴 수 있다. 배치 효율이 좋아지면 필요한 레플리카 수가 줄어든다. 결국 모델 설정 하나가 쿠버네티스 비용 구조 전체를 바꾼다.

온디맨드, 예약, Spot을 워크로드별로 섞기

GPU를 싸게 쓰는 가장 쉬운 방법은 할인된 용량을 쓰는 것이다. 하지만 모든 워크로드에 Spot이나 Preemptible을 쓰면 안 된다. 중단되어도 되는 작업과 중단되면 안 되는 작업을 나눠야 한다.

학습과 배치 추론은 Spot에 잘 맞는다. 파인튜닝, 대량 임베딩 생성, 오프라인 평가, 데이터 전처리, 실험성 학습은 중간에 끊겨도 체크포인트와 재시도로 복구할 수 있다. 반대로 온라인 추론은 사용자가 기다리고 있다. SLA가 있는 실시간 챗봇, 결제 흐름에 붙은 AI 검증, 고객 지원 자동화는 중단 비용이 할인 이익보다 클 수 있다.

실전에서는 세 가지 용량을 섞는다.

  • 예약 또는 장기 약정 GPU: 항상 트래픽이 있는 핵심 온라인 추론

  • 온디맨드 GPU: 예측 불가능하지만 SLA가 중요한 피크 대응

  • Spot/Preemptible GPU: 중단 허용 가능한 학습, 배치 임베딩, 오프라인 평가

이 조합을 쿠버네티스에서 구현하려면 노드 풀을 워크로드 성격별로 나누고, taint와 toleration, nodeSelector, PriorityClass를 함께 사용한다.

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-spot-batch
spec:
  template:
    metadata:
      labels:
        workload-class: batch-ai
        capacity-type: spot
    spec:
      taints:
        - key: workload
          value: batch-ai
          effect: NoSchedule
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
        - key: node.kubernetes.io/instance-type
          operator: In
          values: ["g5.2xlarge", "g5.4xlarge", "g6.2xlarge"]
  limits:
    nvidia.com/gpu: "32"
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 60s

그리고 배치 작업 파드는 해당 노드 풀에만 올라가도록 지정한다.

apiVersion: batch/v1
kind: Job
metadata:
  name: nightly-embedding
  namespace: ai-batch
spec:
  template:
    spec:
      restartPolicy: OnFailure
      nodeSelector:
        workload-class: batch-ai
      tolerations:
        - key: workload
          operator: Equal
          value: batch-ai
          effect: NoSchedule
      containers:
        - name: worker
          image: registry.example.com/embedding-job:v1
          resources:
            requests:
              cpu: "8"
              memory: 32Gi
              nvidia.com/gpu: 1
            limits:
              cpu: "16"
              memory: 64Gi
              nvidia.com/gpu: 1
          env:
            - name: CHECKPOINT_PATH
              value: s3://ai-artifacts/checkpoints/nightly-embedding/

Spot을 쓸 때 가장 중요한 것은 체크포인트다. 중단 알림을 받고 현재 진행 상태를 저장할 수 있어야 한다. 학습이라면 epoch, optimizer state, model state를 저장해야 하고, 임베딩 작업이라면 "어느 문서까지 처리했는지"를 저장해야 한다. 큐 기반 작업이라면 메시지를 처리 완료로 표시하기 전에 결과 저장이 끝나야 한다.

온라인 추론은 별도 노드 풀에 둔다.

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu-ondemand-inference
spec:
  template:
    metadata:
      labels:
        workload-class: online-inference
        capacity-type: on-demand
    spec:
      taints:
        - key: workload
          value: online-inference
          effect: NoSchedule
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["on-demand"]
        - key: node.kubernetes.io/instance-type
          operator: In
          values: ["g5.4xlarge", "g6.4xlarge"]
  limits:
    nvidia.com/gpu: "16"
  disruption:
    consolidationPolicy: WhenEmpty
    consolidateAfter: 300s

여기서는 공격적인 scale-down보다 안정성이 중요하다. 노드가 비었다고 즉시 제거하면 다음 요청에서 모델 로딩 지연이 발생할 수 있다. 따라서 온라인 추론 풀은 최소 용량과 warm pool을 두고, 배치 풀은 가능한 한 빠르게 내려가는 식으로 다르게 운영한다.

낮에는 추론, 밤에는 학습

GPU 비용 최적화에서 의외로 큰 효과를 내는 전략은 시간대별 워크로드 전환이다. 많은 서비스는 낮에 사용자 트래픽이 많고 밤에는 줄어든다. 반대로 학습, 파인튜닝, 대량 임베딩, 오프라인 평가, 리포트 생성은 밤에 돌려도 된다. 그렇다면 낮에는 GPU를 온라인 추론에 쓰고, 밤에는 같은 GPU 풀을 배치 작업에 넘겨줄 수 있다.

이 방식은 특히 작은 팀에 유용하다. GPU를 넉넉히 살 수 없는 팀일수록 "언제 무엇을 돌릴 것인가"가 중요하다. 쿠버네티스에서는 Kueue, PriorityClass, CronJob, KEDA를 조합해 이를 구현할 수 있다.

낮에는 온라인 추론이 높은 우선순위를 가진다.

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: online-inference-critical
value: 100000
globalDefault: false
description: "SLA가 있는 온라인 LLM 추론"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: batch-ai-low
value: 1000
globalDefault: false
description: "중단 가능하거나 지연 가능한 AI 배치 작업"

야간 배치 작업은 낮은 우선순위로 큐에 들어간다. Kueue를 사용하면 팀별 GPU 쿼터와 큐를 관리할 수 있다. 낮 동안 대기하던 작업이 밤에 자원이 비면 실행된다.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-finetune
  namespace: ai-training
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            kueue.x-k8s.io/queue-name: ai-nightly
        spec:
          priorityClassName: batch-ai-low
          restartPolicy: OnFailure
          containers:
            - name: trainer
              image: registry.example.com/finetune:v3
              resources:
                requests:
                  cpu: "16"
                  memory: 128Gi
                  nvidia.com/gpu: 4
                limits:
                  cpu: "32"
                  memory: 192Gi
                  nvidia.com/gpu: 4

시간대별 전환에서 주의할 점은 "밤에 학습이 끝나지 않았는데 아침 트래픽이 시작되는" 상황이다. 이때 온라인 추론이 밀리면 안 된다. 우선순위와 선점 정책을 통해 배치 작업이 양보하도록 설계해야 한다. 학습 잡은 체크포인트를 남기고 중단될 수 있어야 한다. 배치 임베딩은 큐에 남은 작업을 다음 밤에 이어서 처리할 수 있어야 한다.

운영 정책은 다음처럼 간단한 문장으로 정리할 수 있다.

"오전 8시부터 자정까지는 온라인 추론이 GPU 우선권을 가진다. 새벽 1시부터 7시까지는 배치 큐가 남는 GPU를 사용한다. 온라인 추론의 p95 TTFT가 목표치를 넘으면 배치 작업은 자동으로 중단 또는 지연된다."

이 정책이 있으면 비용 최적화가 사람의 눈치 싸움이 아니라 클러스터의 규칙이 된다.

비용 귀속: 누가, 어떤 모델로, 얼마를 썼는가

FinOps의 첫 번째 단계는 가시성이다. 하지만 "이번 달 GPU 비용이 3천만 원입니다"라는 숫자는 가시성이 아니다. 그것은 청구서다. 엔지니어가 행동할 수 있는 가시성은 더 구체적이어야 한다.

"ai-search 네임스페이스의 embedding-v2 모델이 지난주 GPU 비용의 28%를 사용했다. 토큰당 비용은 전주 대비 18% 올랐고, 원인은 평균 입력 길이 증가와 batch size 감소다."

이 정도가 되어야 최적화가 가능하다.

쿠버네티스에서는 비용 귀속의 기본 단위를 네임스페이스와 레이블로 잡는다. 최소한 다음 레이블은 표준화하는 것이 좋다.

metadata:
  labels:
    team: search
    service: rag-api
    model: embedding-v2
    endpoint: internal-embedding
    environment: production
    cost-center: ai-platform

이 레이블은 단순한 장식이 아니다. OpenCost, Kubecost, 클라우드 비용 태그, Prometheus 메트릭, Grafana 대시보드, 알림 정책이 모두 이 레이블을 기준으로 비용을 나눈다. OpenCost는 쿠버네티스 비용을 클러스터, 노드, 네임스페이스, 컨트롤러, 서비스, 파드 단위로 볼 수 있게 해주는 오픈소스 도구이며, 2026년 AI 비용 추적에서는 GPU 사용량과 모델 학습·추론 비용을 워크로드 수준에서 보는 방향이 중요해지고 있다.5

기본 설치는 단순하다.

helm repo add opencost-charts https://opencost.github.io/opencost-helm-chart
helm repo update

helm install opencost opencost-charts/opencost \
  --namespace opencost \
  --create-namespace

실제 운영에서는 Prometheus의 GPU 메트릭과 클라우드 가격 정보를 연결해야 한다. NVIDIA DCGM Exporter가 GPU 사용률, 메모리 사용량, 전력, 온도 같은 지표를 내보내고, OpenCost나 Kubecost가 이를 비용 데이터와 연결한다. 여기서 중요한 것은 GPU 노드 비용을 단순히 파드 수로 나누지 않는 것이다. 가능한 한 다음 신호를 함께 사용해야 한다.

  • 파드의 GPU 요청량

  • 실제 GPU 사용률

  • VRAM 사용량

  • 실행 시간

  • 모델별 요청 수와 토큰 수

  • 엔드포인트별 트래픽과 SLO

완벽한 비용 배분은 어렵다. 특히 time-slicing 환경에서는 여러 파드가 같은 GPU를 공유하므로 실제 사용량 기반 배분이 더 중요해진다. 처음에는 요청량 기준으로 나누고, 이후 DCGM 메트릭과 토큰 메트릭을 결합해 정교화하면 된다.

Showback에서 Chargeback으로

비용 귀속에는 두 단계가 있다. 첫 번째는 showback이다. 각 팀이 얼마를 썼는지 보여주는 것이다. 실제로 돈을 청구하지는 않는다. 하지만 대시보드와 주간 리포트를 통해 "우리 팀의 모델 비용이 얼마나 되는지"를 알게 한다.

두 번째는 chargeback이다. 실제 예산이나 내부 정산에 비용을 반영하는 것이다. 이 단계부터는 조직 문화와 연결된다. 엔지니어는 비용을 줄이고 싶지만, 성능과 실험 속도도 중요하다. 재무팀은 예산을 통제하고 싶지만, AI 제품의 성장 가능성을 막고 싶지는 않다. 그래서 chargeback은 충분한 신뢰가 쌓인 뒤에 도입하는 것이 좋다.

AI 워크로드의 비용 귀속은 전통적인 클라우드보다 한 단계 더 들어가야 한다. GPU 시간만으로는 부족하다. 같은 GPU 한 시간을 써도 어떤 모델은 백만 토큰을 처리하고, 어떤 모델은 십만 토큰만 처리할 수 있다. 따라서 다음 단위를 함께 기록해야 한다.

  • 모델별 추론 요청 수

  • 입력 토큰 수와 출력 토큰 수

  • 엔드포인트별 평균·p95 TTFT

  • 모델 버전별 tokens-per-dollar

  • 고객 또는 기능 단위의 cost-per-feature

예를 들어 SaaS 제품에서 "문서 요약" 기능과 "AI 검색" 기능이 같은 LLM 추론 클러스터를 쓴다고 하자. GPU 비용을 네임스페이스 기준으로만 나누면 두 기능의 단위 경제성을 알 수 없다. 기능별 요청 수와 토큰 수를 기록해야 "문서 요약 기능은 유료 플랜 가격을 정당화하는가?"라는 비즈니스 질문에 답할 수 있다.

이 지점에서 FinOps는 단순한 인프라 절감 활동이 아니라 제품 전략의 일부가 된다.

비용 대시보드는 사후 보고서가 아니라 운영 화면이다

많은 팀이 FinOps를 월말 보고서로 시작한다. 지난달 클라우드 비용이 얼마였고, 어느 팀이 많이 썼고, 어떤 리소스가 낭비되었는지 정리한다. 이것도 필요하다. 하지만 AI 워크로드에서는 한 달 뒤면 늦다. 잘못 설정한 모델 하나가 며칠 만에 예산을 크게 초과할 수 있다.

GPU 비용 대시보드는 운영 화면이어야 한다. 장애 대시보드 옆에 있어야 한다. p99 latency와 error rate 옆에 cost-per-1K-tokens가 있어야 한다. 새 배포 이후 TTFT가 좋아졌지만 tokens-per-dollar이 절반으로 떨어졌다면, 그것도 성능 회귀다.

대시보드에는 최소한 다음 패널을 둔다.

  • 클러스터 전체 GPU 비용과 사용률

  • 네임스페이스·팀별 GPU 비용

  • 모델·엔드포인트별 tokens-per-dollar

  • GPU utilization과 VRAM utilization의 산점도

  • 큐 길이와 TTFT/ITL 상관관계

  • 유휴 GPU 노드와 scale-down 후보

  • Spot 중단 횟수와 재시도 성공률

특히 GPU utilization과 VRAM utilization을 함께 보는 패널은 유용하다. 네 가지 사분면으로 낭비 유형을 빠르게 구분할 수 있다.

GPU 사용률 높음 + VRAM 사용률 높음 = 정상 포화 또는 용량 부족
GPU 사용률 높음 + VRAM 사용률 낮음 = 연산 중심 작업, 배치/병렬화 검토
GPU 사용률 낮음 + VRAM 사용률 높음 = VRAM 병목, 컨텍스트/캐시/양자화 검토
GPU 사용률 낮음 + VRAM 사용률 낮음 = 유휴 또는 오버프로비저닝

이 단순한 구분만으로도 "GPU를 더 사야 하는지"와 "설정을 고쳐야 하는지"가 달라진다.

배포 전 비용 게이트

성숙한 FinOps는 사후 분석에서 끝나지 않는다. 배포 전에 비용 위험을 잡는다. 새 모델을 배포하거나 리소스 요청을 바꾸는 순간, CI/CD 또는 GitOps 파이프라인이 예상 비용을 계산하고 정책을 확인해야 한다.

예를 들어 개발자가 다음과 같은 변경을 PR로 올렸다고 하자.

resources:
  requests:
    cpu: "16"
    memory: 128Gi
    nvidia.com/gpu: 4
  limits:
    cpu: "32"
    memory: 192Gi
    nvidia.com/gpu: 4

이 변경은 기술적으로는 맞을 수 있다. 하지만 레플리카가 3개이고, HPA 최대치가 10이며, 온디맨드 A100 노드에 올라간다면 월 비용은 크게 늘어난다. 이때 리뷰어가 눈으로 계산하면 실수한다. 파이프라인이 계산해야 한다.

비용 게이트는 다음 항목을 확인한다.

  • 예상 월 GPU 비용

  • GPU 요청량과 팀 쿼터 초과 여부

  • HPA/KEDA maxReplicaCount 기준 최대 비용

  • scale-to-zero 가능 여부

  • 모델별 tokens-per-dollar 목표치

  • Spot 사용 가능 워크로드인지 여부

  • 프로덕션 배포 시 승인 필요 여부

Kyverno나 OPA Gatekeeper로 간단한 정책을 만들 수 있다. 예를 들어 개발 네임스페이스에서 전체 GPU 요청을 막고 MIG 리소스만 허용할 수 있다.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-mig-for-dev-gpu
spec:
  validationFailureAction: Enforce
  rules:
    - name: block-full-gpu-in-dev
      match:
        any:
          - resources:
              kinds:
                - Pod
              namespaces:
                - ai-dev
      validate:
        message: "개발 네임스페이스에서는 전체 GPU 대신 MIG 또는 공유 GPU 풀을 사용해야 합니다."
        deny:
          conditions:
            any:
              - key: "{{ request.object.spec.containers[].resources.limits.\"nvidia.com/gpu\" || `[]` }}"
                operator: AnyIn
                value:
                  - 1
                  - "1"

또 다른 정책은 레플리카와 GPU 요청량을 곱해 팀 한도를 넘는 배포를 차단할 수 있다. 실제 운영에서는 Kyverno만으로 복잡한 비용 계산을 모두 처리하기보다, CI 단계에서 별도 스크립트나 OpenCost/Kubecost API를 호출해 예상 비용을 계산하고, 그 결과를 PR 댓글로 남기는 방식이 좋다.

PR 댓글은 이렇게 나와야 한다.

예상 비용 영향
- 모델: llama-70b-chat
- GPU 요청: 4 GPUs x replicas 2 = 8 GPUs
- HPA 최대 비용: 4 GPUs x replicas 8 = 32 GPUs
- 예상 월 비용: $18,400 ~ $73,600
- team=ai-platform 월 예산의 82% 사용 가능
- 정책: 승인 필요 (월 $10,000 초과)

이 정도 정보가 있으면 승인자는 기술 변경과 비용 영향을 함께 볼 수 있다. "왜 비용이 늘었는지"를 다음 달에 묻는 대신, 배포 전에 대화할 수 있다.

ResourceQuota와 LimitRange는 비용 안전벨트다

FinOps 도구가 비용을 보여준다면, 쿠버네티스 정책은 비용 폭주를 막는다. 가장 기본적인 안전벨트는 ResourceQuota와 LimitRange다.

팀별 네임스페이스에 GPU 쿼터를 걸어두면 실수로 전체 클러스터 GPU를 잡아먹는 일을 막을 수 있다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: ai-search-quota
  namespace: ai-search
spec:
  hard:
    requests.cpu: "128"
    requests.memory: 512Gi
    requests.nvidia.com/gpu: "8"
    limits.nvidia.com/gpu: "8"
    persistentvolumeclaims: "20"

MIG 리소스를 사용한다면 MIG 프로파일별 쿼터도 둘 수 있다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: ai-dev-mig-quota
  namespace: ai-dev
spec:
  hard:
    requests.nvidia.com/mig-1g.10gb: "8"
    requests.nvidia.com/mig-2g.20gb: "4"

LimitRange는 기본 CPU·메모리 요청을 설정해 파드가 무제한으로 자원을 쓰는 일을 막는다. GPU는 보통 명시적으로 요청해야 하므로 LimitRange만으로 충분하지 않지만, CPU와 메모리 오버프로비저닝을 줄이는 데는 유용하다.

apiVersion: v1
kind: LimitRange
metadata:
  name: ai-default-limits
  namespace: ai-dev
spec:
  limits:
    - type: Container
      defaultRequest:
        cpu: "500m"
        memory: 1Gi
      default:
        cpu: "2"
        memory: 4Gi

여기서 중요한 운영 원칙은 "쿼터를 비용 절감 수단으로만 보지 않는 것"이다. 쿼터는 장애 격리 수단이기도 하다. 한 팀의 실험이 전체 클러스터의 온라인 추론을 밀어내지 못하게 만든다. FinOps와 안정성은 같은 방향을 본다.

FinOps 운영 리듬 만들기

도구와 정책만으로 FinOps가 굴러가지는 않는다. 정기적인 운영 리듬이 필요하다. 복잡하게 시작할 필요는 없다. 작은 팀이라면 주간 30분 회의로 충분하다.

회의에서는 다음 질문을 본다.

  • 지난주 GPU 비용이 가장 많이 증가한 모델은 무엇인가?

  • tokens-per-dollar이 악화된 엔드포인트는 어디인가?

  • GPU 사용률은 낮은데 비용이 큰 워크로드는 무엇인가?

  • Spot 중단으로 실패한 작업은 몇 건인가?

  • 다음 주 배포 예정 모델의 예상 비용은 얼마인가?

월간 리뷰에서는 더 큰 결정을 한다. 예약 인스턴스를 늘릴지, 특정 GPU 타입을 줄일지, MIG 프로파일을 바꿀지, 개발 환경의 time-slicing 비율을 조정할지 결정한다. 분기 리뷰에서는 모델 포트폴리오를 본다. 오래된 모델을 내리고, 사용량이 낮은 엔드포인트를 scale-to-zero로 바꾸고, 비용 대비 가치가 낮은 기능을 제품 차원에서 재검토한다.

좋은 FinOps 문화는 엔지니어를 탓하지 않는다. 비용을 숨기지 않고, 판단 가능한 숫자를 제공하고, 좋은 선택을 쉽게 만든다. 개발자가 GPU를 요청할 때 "A100 전체"가 기본값으로 보이면 누구나 그렇게 쓴다. 플랫폼이 "작은 추론", "프로덕션 추론", "배치 학습", "개발 테스트" 같은 선택지를 제공하면 비용 효율적인 선택이 자연스러워진다.

작은 팀을 위한 현실적인 시작 순서

모든 것을 한 번에 도입하려고 하면 실패한다. 특히 1인 개발자나 소규모 팀은 OpenCost, Karpenter, Kueue, MIG, KEDA, Gatekeeper를 동시에 운영할 여력이 없을 수 있다. 시작 순서는 단순해야 한다.

첫째, GPU 비용과 토큰 처리량을 기록한다. 클라우드 청구서와 애플리케이션 로그만 있어도 된다. 모델별 요청 수, 입력 토큰, 출력 토큰을 남기고, GPU 노드 비용과 대략 연결한다. 처음에는 정확도보다 추세가 중요하다.

둘째, 개발·테스트 GPU를 분리한다. 개발 환경이 프로덕션 GPU를 잠식하지 않게 한다. 가능하면 time-slicing이나 작은 GPU를 사용한다. 개발 환경은 야간 자동 종료를 기본값으로 둔다.

셋째, 배치 작업을 Spot으로 옮긴다. 학습, 임베딩, 오프라인 평가처럼 재시도 가능한 작업부터 시작한다. 체크포인트와 큐 재처리를 반드시 넣는다.

넷째, 온라인 추론의 최소 용량과 scale-to-zero 대상을 나눈다. 모든 엔드포인트를 항상 켜두지 않는다. 내부 도구, 낮은 트래픽 모델, 비동기 작업은 scale-to-zero 후보가 된다.

다섯째, 네임스페이스와 레이블 표준을 만든다. 팀이 작을수록 나중에 정리하기 어렵다. team, service, model, environment, cost-center 정도는 처음부터 붙인다.

여섯째, 배포 전 비용 댓글을 만든다. 처음부터 차단하지 않아도 된다. PR에 예상 GPU 비용을 댓글로 보여주는 것만으로도 행동이 바뀐다. 이후 예산 초과 배포만 승인 절차로 넘기면 된다.

비용 최적화의 함정

GPU 비용을 줄이는 과정에서 자주 빠지는 함정이 있다.

첫 번째 함정은 사용률만 보는 것이다. GPU 사용률 90%가 항상 좋은 것은 아니다. 사용률이 높아도 TTFT가 나빠지고 큐가 쌓이면 사용자 경험이 무너진다. 비용 최적화는 SLO 안에서 해야 한다.

두 번째 함정은 Spot을 만능으로 보는 것이다. Spot은 강력하지만 중단된다. 체크포인트가 없는 학습, 재시도 불가능한 배치, 실시간 추론에 무리하게 쓰면 비용은 줄어도 신뢰를 잃는다.

세 번째 함정은 모든 모델을 공유 GPU에 올리는 것이다. Time-slicing은 유용하지만 격리가 약하다. 유료 고객 트래픽이나 지연 시간 민감도가 높은 서비스는 MIG나 전용 GPU가 더 낫다.

네 번째 함정은 비용을 너무 늦게 보는 것이다. 월말 보고서만 보면 이미 돈은 나갔다. 배포 전 비용 게이트와 실시간 대시보드가 필요하다.

다섯 번째 함정은 비용 최적화를 플랫폼 팀만의 책임으로 보는 것이다. 모델 선택, 프롬프트 길이, 캐시 전략, 재시도 로직은 애플리케이션 팀의 결정이다. GPU 노드 풀과 스케줄링은 플랫폼 팀의 결정이다. 예산과 우선순위는 비즈니스의 결정이다. FinOps는 이 셋을 연결하는 운영 방식이다.

GPU FinOps 체크리스트

마지막으로 이 장의 내용을 운영 체크리스트로 정리해보자.

측정부터 시작한다. GPU 시간당 비용, 모델별 토큰 처리량, TTFT, ITL, 큐 길이, GPU/VRAM 사용률을 함께 본다. 비용 지표는 항상 성능 지표와 짝을 이뤄야 한다.

공유 전략을 구분한다. 프로덕션 추론과 SLA가 있는 엔드포인트에는 MIG를 우선 적용한다. 개발·테스트와 간헐적 실험에는 time-slicing을 적용한다. DRA와 DeviceClass를 활용해 개발자가 하드웨어 세부사항을 몰라도 비용 효율적인 GPU 클래스를 선택하게 만든다.

구매 전략을 섞는다. 온라인 추론은 예약·온디맨드 중심으로 운영하고, 학습·배치·임베딩은 Spot 중심으로 보낸다. Spot에는 체크포인트와 재시도 구조를 반드시 넣는다.

시간대별로 GPU를 재사용한다. 낮에는 온라인 추론, 밤에는 학습·파인튜닝·배치 임베딩을 돌린다. Kueue와 PriorityClass로 큐와 우선순위를 관리하고, 온라인 SLO가 악화되면 배치가 양보하게 만든다.

비용을 귀속한다. 네임스페이스, 팀, 서비스, 모델, 엔드포인트, 환경 레이블을 표준화한다. OpenCost나 Kubecost로 showback을 시작하고, 충분히 신뢰가 쌓이면 chargeback으로 확장한다. GPU 시간뿐 아니라 요청 수, 토큰 수, 모델별 처리량을 함께 기록한다.

배포 전에 막는다. 새 모델 배포나 리소스 요청 변경 시 예상 월 비용, 최대 scale-out 비용, GPU 쿼터, tokens-per-dollar 목표를 검증한다. 예산 초과 가능성이 큰 배포는 GitOps/CI 단계에서 차단하거나 승인 절차로 넘긴다.

GPU FinOps의 핵심은 절약이 아니다. 낭비를 줄이고, 필요한 곳에는 과감히 쓰며, 비용과 성능을 같은 화면에서 의사결정하는 것이다. AI 시대의 쿠버네티스 운영자는 이제 파드가 떠 있는지만 보지 않는다. 그 파드가 얼마의 비용으로 몇 개의 토큰을 만들고 있는지 본다. 그 숫자를 볼 수 있을 때, GPU 클러스터는 비용 폭탄이 아니라 제품의 경쟁력이 된다.

참고

1Cast AI 2026 State of Kubernetes Optimization Report

2FinOps Foundation, Scaling Kubernetes for AI/ML Workloads with FinOps

3Google Cloud, GKE에서 GPU를 사용하여 LLM 추론 최적화하기

4NVIDIA Developer Blog, Maximize AI Infrastructure Throughput by Consolidating Underutilized GPU Workloads

5OpenCost 2026 AI Cost Tracking for Kubernetes GPU

책 목록

AI 시대의 쿠버네티스: LLM 추론과 에이전트를 운영하는 인프라 전략

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기