Mayıs 16 2025

Kubernetes Küme Yönetiminde GPU İsrafını Önleme ve Maliyet Optimizasyonu

Biz SRE’lerin kabusu genelde bellek sızıntıları veya çöken DNS servisleridir sanıyorduk. Yanılmışız. Asıl kabus, şirketin yapay zeka trenine binmesiyle başlayan ve ay sonunda CFO’nun masasında patlayan o meşhur bulut faturasıymış. Evet, kubernetes gpu yönetimi ve beraberinde gelen astronomik maliyetler, modern cloud devops dünyasının yeni canavarı. Veri bilimcilerin “Deney yapıyorum” diyerek rezerve ettiği ama günün %95’inde boş yatan A100’leri görünce saç baş yoluyorsanız, doğru yerdesiniz. Bu yazıda, gpu monitoring ve k8s cost optimization tekniklerini kullanarak GPU israfını nasıl önleyeceğimizi, teoriyi geçip doğrudan üretim ortamında uygulayabileceğiniz konfigürasyonlarla inceleyeceğiz.

Göremediğin Şeyi Optimize Edemezsin: GPU İzleme altyapısı

Kubernetes’in native metrik sunucusu (metrics-server), CPU ve bellek konusunda harikalar yaratsa da GPU dünyasından tamamen habersizdir. Bir podun GPU talep etmesi ve Kubernetes scheduler tarafından o poda bir GPU atanması, o GPU’nun gerçekten aktif kullanıldığı anlamına gelmez. Bizim için kritik olan iki metrik var: GPU Duty Cycle (hesaplama gücü kullanımı) ve Framebuffer (VRAM) kullanımı.

Bu metrikleri toplamak için NVIDIA’nın DCGM Exporter aracını kümemize kurmak zorundayız. Prometheus ile entegre çalışan bu ajan, bize pod seviyesinde GPU kullanım detaylarını verir.

DCGM Exporter Kurulumu ve Prometheus Entegrasyonu

Aşağıdaki Helm komutuyla DCGM Exporter’ı kümenize hızlıca dahil edebilirsiniz. Burada kritik nokta, servis izleme (ServiceMonitor) özelliğini aktif etmektir:

helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

helm install --namespace gpu-operator \
  --create-namespace \
  --set dcgmExporter.serviceMonitor.enabled=true \
  gpu-operator nvidia/gpu-operator

Kurulum tamamlandıktan sonra Prometheus üzerinde şu sorguyu (PromQL) çalıştırarak, rezerve edilmiş ama kullanılmayan (idle) GPU’ları anında tespit edebilirsiniz:

# Son 1 saat içinde ortalama GPU kullanımı %5'in altında olan ve pod tarafından rezerve edilmiş GPU'lar
avg_over_time(DCGM_FI_DEV_GPU_UTIL[1h]) < 5 and on(pod) kube_pod_container_resource_requests{resource="nvidia.com/gpu"} > 0

Neden bu sorgu? Çünkü sadece DCGM_FI_DEV_GPU_UTIL metriğine bakmak yanıltıcı olabilir; sistemde başıboş duran, hiçbir podun istemediği GPU’ları da listeler. Bizim amacımız, bir geliştirici tarafından rezerve edilip kilitlenmiş ama kullanılmayan “israf” kaynakları yakalamak.

GPU Bölümleme Teknolojileri: MIG ve Time-Slicing

Her podun koca bir A100 veya L4 GPU’ya ihtiyacı yoktur. Basit bir model çıkarımı (inference) veya hafif bir Jupyter Notebook hücresi için koca bir donanımı kapatmak tam bir kaynak israfıdır. Kubernetes dünyasında GPU’ları bölmenin iki popüler yolu vardır: donanımsal MIG (Multi-Instance GPU) ve yazılımsal Time-Slicing.

1. Donanımsal Bölümleme: NVIDIA MIG

MIG, fiziksel bir GPU’yu tamamen izole edilmiş mini GPU’lara (instance) böler. Bellek ve hata toleransı donanım seviyesinde izole edilir; yani bir podun çökmesi diğer “slice”ı etkilemez. A100 ve H100 gibi enterprise kartlarda desteklenir.

GPU Operator yüklü bir kümede MIG profilini aktif etmek için node üzerindeki etiketi değiştirmemiz yeterlidir:

kubectl label nodes <node-adi> nvidia.com/mig.config=all-1g.10gb --overwrite

Bu komut, uyumlu bir GPU’yu her biri 10GB VRAM’e sahip bağımsız parçalara böler. Podlarınız artık nvidia.com/gpu yerine doğrudan bu profilleri isteyebilir:

resources:
  limits:
    nvidia.com/mig-1g.10gb: 1

2. Yazılımsal Bölümleme: GPU Time-Slicing

Eğer elinizde MIG desteklemeyen (örneğin T4, L4 veya RTX serisi) kartlar varsa, kurtarıcınız Time-Slicing’dir. Bu yöntem, GPU üzerinde zaman paylaşımlı (interleaved) çalışmayı sağlar. Bellek izolasyonu yoktur (biri hata yaparsa OOM olur), ancak dev/staging ortamları için can kurtarır.

Time-slicing yapılandırması için aşağıdaki ConfigMap’i oluşturup GPU Operator’e bildirmemiz gerekir:

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

Bu konfigürasyon, Kubernetes scheduler’a fiziksel olarak 1 adet olan GPU’yu sanal olarak 4 adetmiş gibi gösterir. Böylece 4 farklı pod aynı GPU’yu paylaşarak çalışabilir.

Karpenter ve Dynamic Autoscaling ile Scale-to-Zero

GPU makineleri pahalıdır. Gecenin bir yarısı çalışmayan bir eğitim (training) podu için AWS veya GCP üzerinde bir GPU makinesinin açık kalması, sabaha kadar dolar yakmak demektir. Kubernetes Cluster Autoscaler yerine Karpenter kullanmak, GPU maliyet optimizasyonunda çağ atlatır.

Neden Karpenter? Çünkü Karpenter, podun taints/tolerations ve kaynak isteklerine doğrudan bakarak milisaniyeler içinde doğru boyuttaki GPU makinesini (Spot veya On-Demand) ayağa kaldırabilir ve iş bittiğinde node’u anında yok edebilir (scale-to-zero).

Karpenter NodePool GPU Konfigürasyonu

GPU iş yüklerinizi Spot instance’lar üzerinde koşturmak, faturanızı %70’e varan oranda düşürür. İşte Karpenter için optimize edilmiş bir GPU NodePool tanımı:

apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: gpu-spot-pool
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot"]
        - key: karpenter.k8s.aws/instance-gpu-manufacturer
          operator: In
          values: ["nvidia"]
        - key: node.kubernetes.io/instance-type
          operator: In
          values: ["g4dn.xlarge", "g5.xlarge"]
      taints:
        - key: nvidia.com/gpu
          value: "true"
          effect: NoSchedule
      disruption:
        consolidationPolicy: WhenEmpty
        consolidateAfter: 30s

Buradaki ince ayar disruption.consolidationPolicy: WhenEmpty ve consolidateAfter: 30s parametreleridir. GPU üzerindeki iş bittiği ve pod silindiği anda, Karpenter 30 saniye içinde o pahalı makineyi kapatır. Geleneksel autoscaler’lardaki 10 dakikalık varsayılan bekleme süresini bypass etmiş oluruz.

Uygulama Seviyesinde Optimizasyon: MPS (Multi-Process Service)

Eğer yoğun bir model çıkarım (inference) API’si çalıştırıyorsanız ve kuyrukta bekleyen binlerce istek varsa, Time-Slicing’in yarattığı bağlam geçişi (context switching) yükü CPU’yu ve GPU’yu yorar. NVIDIA MPS, tek bir GPU üzerinde birden fazla CUDA işleminin eşzamanlı ve sıfıra yakın overhead ile çalışmasını sağlar.

Kubernetes üzerinde MPS kullanmak için pod konfigürasyonunuza NVIDIA amblemli şu ortam değişkenlerini eklemeniz yeterlidir:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mps-inference-deployment
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: model-server
        image: my-inference-model:v1
        env:
        - name: CUDA_MPS_ACTIVE_THREAD_PERCENTAGE
          value: "33" # Donanım gücünün %33'ünü sınırla
        resources:
          limits:
            nvidia.com/gpu: 1

Bu sayede, aynı GPU’yu kullanan 3 pod, donanım kaynaklarını birbirini bloklamadan, sanki paralel donanımları varmış gibi yüksek performansla kullanır.

Özet ve “Ne Yapmalı?” Listesi

Kubernetes üzerinde GPU optimizasyonu yapmak tek bir hamleyle çözülecek bir iş değildir. Katmanlı bir yaklaşım gerektirir:

  • İzleme: DCGM Exporter kurun ve boşta yatan (idle) GPU’ları alarm sisteminize (Alertmanager) bağlayın.
  • Paylaşım: Geliştirme ortamlarında Time-Slicing, üretim ortamlarında ise izolasyon için MIG tercih edin.
  • Autoscaling: Karpenter ile scale-to-zero uygulayın. İş bittiğinde sunucunun anında kapandığından emin olun.
  • Satın Alma: Model eğitimi gibi kesintiye dayanıklı işlerde kesinlikle Spot/Preemptible instance kullanın.

Günün sonunda, doğru konfigüre edilmiş bir Kubernetes kümesi, AI projelerinizin hızından ödün vermeden bulut faturalarınızı yönetilebilir seviyelerde tutmanın tek yoludur.

Category: Genel | LEAVE A COMMENT