---
title: "eBPF ile GPU FinOps"
description: "GPU harcaması cgroup ve cAdvisor metriklerine neden görünmez, eBPF onu ekiplere nasıl atfeder — ve DCGM'in kapatması gereken CUDA sınırı."
url: https://sade.dev/tr/systems/ebpf-ile-gpu-finops/
lang: tr
author: "Muhammet Şafak"
published: 2026-06-20
updated: 2026-09-06
section: System
tags: ["ebpf","gpu","finops","observability","kubernetes"]
summary: "Bütün bir GPU için faturalandırılıyorsunuz ve onun %3’ünü kullanıyorsunuz — ve monitoring’iniz size o %3’ün kimin olduğunu söyleyemiyor. eBPF, syscall sınırını izleyerek GPU işini bir pod’a atfeder; DCGM ise silikonun ne kadar iyi koştuğunu ölçer. Farklı sorulara cevap verirler ve gerçek bir chargeback her ikisine de ihtiyaç duyar."
---

# eBPF ile GPU FinOps

> Bütün bir GPU için faturalandırılıyorsunuz ve onun %3’ünü kullanıyorsunuz — ve monitoring’iniz size o %3’ün kimin olduğunu söyleyemiyor. eBPF, syscall sınırını izleyerek GPU işini bir pod’a atfeder; DCGM ise silikonun ne kadar iyi koştuğunu ölçer. Farklı sorulara cevap verirler ve gerçek bir chargeback her ikisine de ihtiyaç duyar.

Bir GPU node’u, bir raf dolusu CPU makinesinden saatte daha fazlaya mal olur ve finansın onun hakkında sorduğu soru, her şey hakkında sorduğu soruyla aynıdır: **bunu kim kullandı ve ne kadar?** CPU, bellek ve disk için bunu cevaplayabilirsiniz — cgroup’lar bunu process başına hesaplar, cAdvisor pod başına raporlar, Prometheus ekip başına toplar. GPU içinse çoğu kümede dürüst cevap, kimsenin bilmediğidir. Filodaki en pahalı kaynağın parasını ödüyorsunuz ve onun tek bir kuruşunu bile atfedemiyorsunuz.

Bu, doldurmayı unuttuğunuz bir araç boşluğu değil. Yapısaldır: GPU, **yığınınızın geri kalanının dayandığı muhasebe düzleminin dışındadır.** Bu yazı, bunun nedeni ve atfetmeyi geri kazanmak için eBPF kullanımı ile ilgili — ve eBPF’in ne görebildiği ve ne göremediğine dair sert bir çizgiyle birlikte, çünkü buradaki en pahalı hata, bir aracın fiziksel olarak cevaplayamadığı bir soruyu cevapladığına inanmaktır.

## GPU mevcut metriklerinize neden görünmez?

Üç ayrı körlük üst üste yığılır.

**cgroup’lar GPU’yu hesaplamaz.** Bir cgroup, CPU, bellek, block I/O ve PID’leri hesaplayan ve sınırlayan bir kernel yapısıdır. GPU bunların hiçbiri değil. Compute ve framebuffer (VRAM), NVIDIA kernel driver’ı ve userspace CUDA runtime’ı tarafından yönetilir ve cgroup hesaplamasına opaktır. Bir `device` cgroup controller’ı var, ama o yalnızca device node’larına (`/dev/nvidia0` ve dostları) *erişimi geçitler* — izin ver ya da reddet. Tek bir saniye GPU zamanını ya da bir megabayt VRAM’i hesaplamaz. Yani size pod başına CPU ve bellek veren tüm cAdvisor → Prometheus boru hattının, kuruluş gereği, **GPU hakkında söyleyecek hiçbir şeyi yoktur.**

**`nvidia.com/gpu` bir tahsis sayısıdır, bir kullanım sinyali değil.** Kubernetes GPU’ları, onları kubelet’e extended resource `nvidia.com/gpu` olarak kaydeden NVIDIA device plugin’i üzerinden öğrenir. Pod’lar bunu bir integer olarak ister — bütün GPU’lar. Scheduler istenen *sayıyı* mevcut *sayıyla* eşler. Tahsis katmanının bildiği tek şey budur. **Bir GPU istemek, bir GPU kullanmak değildir.** Bir pod bütün bir cihazı tutabilir — ve bütün cihazın faturasını ödeyebilir — onu %3’te sürerken. Üzerinden chargeback yapabileceğiniz tahsis sayısı, tam da tüketim hakkında size hiçbir şey söylemeyen sayıdır.

**`nvidia-smi`’nin kullanım sayısı bile size yalan söyler.** `nvidia-smi`’ye uzanırsınız ve `utilization.gpu` alanı kurtuluş gibi görünür. Sandığınız şey değil. NVML onu şöyle tanımlar: *örnekleme periyodu boyunca bir ya da daha fazla kernel’in çalıştığı zamanın yüzdesi.* **Zamansal varlığı — GPU hiç meşgul muydu — ölçer, ne kadarının meşgul olduğunu değil.** Onlarca SM’den birini işgal eden tek thread’lik bir kernel %100’e yakın raporlayabilir. Microsoft, A100’ler üzerinde 8B parametreli bir modeli servis ederken bellek-bağımlı decode fazında %10’un altında compute kullanımı raporlamıştır — naif bir “kullanım” okuması o GPU’ları dolu sayacakken. `utilization.gpu` üzerinden chargeback yaparsanız, silikon neredeyse boştayken “meşgul” diyen bir sayı üzerinden faturalandırıyorsunuzdur.

Yani: tahsis katmanı kullanımı-değil-sayıyı bilir, cgroup’lar hiçbir şey bilmez ve tek kolay yüzde yanlış şeyi ölçer. Boşluk budur.

## İki soru ve neden farklı araçlar gerektirdikleri

Herhangi bir araçtan önce iki soruyu ayırın, çünkü onları karıştırmak temel hatadır — [bir aracın çözdüğü problemi eldeki problemle karıştırmakla](/tr/systems/veri-yogunluklu-sistemler-kirilma-noktalari) aynı şekil.

- **Atfetme — GPU işini *kim* yaptı ve *ne kadar*?** Bu FinOps sorusudur. Pod başına, ekip başına GPU aktivitesi hesabı gerektirir: hangi workload kernel başlattı, VRAM tahsis etti, veri taşıdı ve ne kadar süreyle. Chargeback bunun üzerinden koşar.
- **Verimlilik — silikon *ne kadar iyi* kullanıldı?** SM’ler gerçekten doluydu mu, tensor core’lar aktif miydi, darboğaz bellek bant genişliği miydi? Bu performans sorusudur. Bir ekibin harcamasının *haklı* olup olmadığını söyler, ama *kimin* harcaması olduğunu söyleyemez.

Bunlar iki farklı ölçüm düzlemine eşlenir ve aşağıdaki her şeyi örgütleyen kural şu: **eBPF atfetmeyi cevaplar; GPU’nun kendi sayaçları verimliliği cevaplar. Hiçbiri diğerinin yerine geçmez.**

## eBPF’in görebildiği: kontrol düzlemi

eBPF kernel’de çalışır ve kprobe’lara, uprobe’lara, tracepoint’lere ve syscall’lara takılır. Okunacak bir GPU-kullanımı tracepoint’i yok — ama GPU *işinin talep edildiği* iki yüzey var ve ikisi de kernel’den görünür:

- **Driver ioctl sınırı.** Her parça GPU işi — kernel gönderimi, bellek tahsisi, senkronizasyon — sonunda `/dev/nvidiactl`’a ve `/dev/nvidia0…N`’e bir `ioctl()` olur. Driver’ın giriş noktalarındaki (`nvidia_unlocked_ioctl`, `nvidia_open`) bir kprobe o trafiği görür. (Kapalı driver tarihsel olarak yalnızca tek bir tracepoint, donanım hata olayları için `nvidia:nvidia_dev_xid`, sunar; geri kalan her şey kprobe’lanır.)
- **CUDA kütüphane sınırı.** `libcuda.so` / `libcudart.so` üzerindeki bir uprobe API’nin kendisini izler: `cuLaunchKernel`, `cuMemAlloc`, `cuMemcpyHtoD`, `cuStreamSynchronize` ve dostları. Bir giriş uprobe’unu bir dönüş uretprobe’uyla eşlemek her çağrının süresini ölçer.

Bunun *atfetme* olmasının nedeni, eBPF’in her hook noktasında çağıran PID/TGID’yi ve cgroup’u native olarak okumasıdır — `bpf_get_current_pid_tgid` ve cgroup id tam orada. Bu size **PID → cgroup → pod → namespace → ekip** zincirini verir, uygulama değişikliği olmadan ve NVIDIA’nın işbirliği olmadan. Pod başına şunları alırsınız: kernel-başlatma sayıları, başlatma boyutları, VRAM tahsis boyutları, memcpy hacmi ve yönü, çağrı zamanlaması. Bu, tamamen sınırın kernel tarafından türetilen, gerçek ve savunulabilir bir chargeback sinyalidir.

Buradaki ekosistem genç ama gerçek. Primitifler bugün işliyor — CUDA kütüphanelerini uprobe’layan ve nvidia ioctl yolunu kprobe’layan çalışan yazılar ve eğitimler var ve `bpftime` eBPF mantığını GPU olaylarına bağlamayı keşfediyor. Tetragon (Cilium), Kubernetes pod kimliğiyle jenerik `process_uprobe`, `process_kprobe` ve ioctl izleme sevk eder, yani CUDA sembollerine ya da ioctl yoluna *yöneltilebilir* — ama bunun sevk edilen bir “GPU FinOps” özelliği değil, yazdığınız bir `TracingPolicy` olduğunu anlayın. Henüz baskın, anahtar-teslim bir eBPF chargeback ürünü yok. Size “tak-çalıştır eBPF GPU FinOps” satan biri, ekosistemin şu an ulaştığından öteye satıyordur.

## eBPF’in göremediği: silikon

Dürüst limit bu ve bir sürümde düzeltilecek bir olgunluk problemi değil — verinin nerede yaşadığının fiziği.

eBPF **kontrol düzlemini** görür: API çağrıları, ioctl’lar, tahsis boyutları, başlatma sayıları ve izleyebildiğiniz yerde gönderim-tamamlanma zamanlaması. GPU’nun *içini* **görmez**. SM occupancy’sini, tensor-core kullanımını ya da erişilen bellek bant genişliğini okuyamaz, çünkü bunlar *cihazda* yaşayan ve yalnızca NVML / DCGM / CUPTI üzerinden açığa çıkan donanım performans sayaçlarıdır. eBPF’in onlara giden bir yolu yoktur. Bir pod’un on bin kernel başlattığını ve 40 GB VRAM tahsis ettiğini söyleyebilir; o kernel’lerin SM’leri doyurup doyurmadığını ya da onları %90 boş bırakıp bırakmadığını söyleyemez.

İkinci, daha incelikli bir limit var. eBPF’in takılabildiği ioctl sınırında bile, *payload’lar* büyük ölçüde opaktır. Driver’ın komut yapıları (`NV_ESC_*` Resource Manager API’si) karmaşık ve fiilen proprietary. Bir ioctl’ın *olduğunu* görebilirsiniz — komut numarasını, çağıran PID’yi, zamanlamayı — ama keyfi bir RM payload’unun semantik içeriğini decode etmek pratik dışı ve kırılgandır. İşin gerçeğini ve atfetmesini alırsınız, anlamının bedava bir okumasını değil.

Cazip bir kapıyı kapatan bir not: NVIDIA’nın açık kernel modülleri (`open-gpu-kernel-modules`, Turing ve sonrası) **kernel arayüz katmanını** açar — modül init’ini, ioctl giriş noktalarını, `NV_ESC_*` komut yüzeyini. Bu, *neyi* hook’layacağınızı anlamanıza gerçekten yardımcı olur. Ama GPU’nun beyni kapalı kalır: Turing+’ta yönetimin çoğu GPU üzerindeki GSP firmware’inde koşar ve o firmware — modüllerin ihtiyaç duyduğu user-mode driver bileşenleriyle birlikte — hâlâ kapalı olarak dağıtılır. Kernel modüllerini açmak, eBPF’e donanım performans sayaçlarını **açmaz**. Cihaz-içi limit değişmemiştir. Açık modüllerin eBPF’e SM occupancy okuttuğunu iddia eden biri, mevcut kanıtlara göre yanılıyordur.

## Hâlâ ihtiyaç duyduğunuz userspace yolu: DCGM

eBPF silikonu göremediği için, cevabın verimlilik yarısı userspace’ten gelir ve standart araç, Prometheus için **dcgm-exporter** ile NVIDIA **DCGM** (Data Center GPU Manager)’dır. DCGM, GPU’nun donanım sayaçlarını driver üzerinden okur ve eBPF’in erişemediği alanları açığa çıkarır:

- `DCGM_FI_PROF_SM_ACTIVE` — bir multiprocessor’da en az bir warp’ın aktif olduğu zamanın oranı, hepsinin ortalaması.
- `DCGM_FI_PROF_SM_OCCUPANCY` — resident warp’ların desteklenen maksimuma oranı: *gerçek* occupancy.
- `DCGM_FI_PROF_PIPE_TENSOR_ACTIVE` — tensor pipe’ın aktif olduğu döngülerin oranı.
- `DCGM_FI_PROF_DRAM_ACTIVE` — bir bellek-bant-genişliği vekili.
- `DCGM_FI_DEV_FB_USED` — gerçekten kullanımda olan framebuffer (VRAM).

Bunlar size bir ekibin pahalı tahsisinin ekmeğini kazanıp kazanmadığını söyleyen sayılardır. DCGM onları pod’lara, her GPU UUID’yi onu tutan pod’a eşleyen kubelet **Pod Resources API**’si (`/var/lib/kubelet/pod-resources` adresindeki bir gRPC servisi) üzerinden atfeder.

Ama DCGM’in atfetmesinin, tam da eBPF’in yerini hak ettiği yerde sert bir kenarı var: **DCGM cihaz-seviyesi metrikler üretir, bu yüzden tek bir fiziksel GPU’yu paylaşan tüketicileri ayırt edemez.** Time-slicing ya da MPS (Multi-Process Service) altında, bir GPU’yu paylaşan tüm pod’lar *aynı, kopyalanmış* cihaz-seviyesi değerleri alır — `DCGM_FI_DEV_FB_USED` dahil. NVIDIA’nın kendi exporter’ı, time-slicing etkinken metrikleri container’larla ilişkilendirmediğini belgeler; `--kubernetes-virtual-gpus=true` ile her paylaşan pod tüm fiziksel GPU’nun durumunu aynalar. MIG altında atfetme, keyfi pod’lara değil GPU-instance seviyesine kayar. Yani tam da paylaşılan-GPU durumunda — ki bu durum, bütün-GPU tahsisi para israf ettiği *için* var olur — DCGM size kimin ne tükettiğini söyleyemez. eBPF’in PID-seviyesi izlemesi söyleyebilir. İki araç, her birinin en zayıf olduğu dikişte birbirini tamamlar.

(En derin verimlilik profilleme için **CUPTI**, CUDA Profiling Tools Interface var; DCGM’in açığa çıkarmadığı sayaçları okuyabilir — ama Nsight Compute gibi metrik-replay profilleme kernel’leri birden çok kez yeniden çalıştırır ve ağır overhead taşır. Bu bir profilleme-oturumu aracıdır, her zaman açık tenant başına telemetri değil. DCGM düşük-overhead’li, örnekleme-bazlı sürekli yoldur; CUPTI ağır, derin olandır.)

## Kernel-vs-userspace takası, açıkça

| Düzlem | Araç | Görür | Göremez |
| --- | --- | --- | --- |
| Kernel (kontrol) | eBPF | PID→pod atfetme, başlatma sayıları, tahsis boyutları, memcpy hacmi, çağrı zamanlaması — time-slicing altında pod başına *dahil* | cihaz-içi SM/tensor occupancy, bellek bant genişliği, ioctl payload içleri |
| Userspace (cihaz) | DCGM | gerçek SM occupancy, tensor aktivitesi, DRAM aktivitesi, kullanılan VRAM — donanım sayaçlarından | paylaşılan GPU altında (time-slicing/MPS) pod başına atfetme: tüm paylaşanlar aynı değerleri alır |

eBPF düşük-overhead’lidir, uygulama değişikliği gerektirmez, vendor işbirliği gerektirmez ve PID/cgroup üzerinden native atfeder — *kim ve ne kadar* için doğru araç odur. DCGM silikonu okur — *ne kadar iyi* için doğru araç odur. Maliyetler de dürüst: uprobe’lar gerçek overhead taşır ve CUDA sembolleri **versiyonludur** (`cuMemAlloc@CUDA_11.0` vs `@CUDA_12.0`), yani probe’lar dinamik sembol çözümü gerektirir ve driver yükseltmeleri boyunca kırılır; CUDA API çağrıları saniyede on binden fazla atabilir, yani handler’lar ucuz kalmalı yoksa ölçtükleri workload’u yavaşlatırlar. DCGM kendi örnekleme overhead’i olan userspace polling’dir ve yalnızca belirli sayaç gruplarının birlikte okunabildiği bir donanım kısıtı taşır.

## İkisine de neden, hangi sırayla ihtiyacınız var

Yanlış dönüş, bir aracı seçip ona diğer aracın sorusunu sormaktır — ekipleri DCGM’in cihaz-seviyesi sayıları üzerinden faturalandırıp bir GPU’yu paylaşan herkesi sessizce fazla yüklemek, ya da eBPF’in başlatma sayılarına verimlilik vekili olarak güvenip meşgul görünen bir workload’un iyi kullanıldığı sonucuna varmak. Gerçekten işleyen sıra:

```
1. eBPF: GPU işini pod/ekibe atfet           (kim, ne kadar — fatura)
2. DCGM: cihaz-içi verimliliği ölç           (ne kadar iyi — haklı mıydı)
3. Onları pod kimliğinde birleştir            (ekibin harcaması VE verimliliği)
```

Atfetme önce, çünkü finansın gerçekten sorduğu soru o ve mevcut yığınınızın hiç cevaplayamadığı o. Verimlilik sonra, çünkü faturayı bir karara dönüştürür — pahalı bir tahsisi %4 occupancy’de tutan bir ekip, artık *görebileceğiniz* bir [ileride-lazım-olur kapasite vergisi](/tr/journal/ileride-lazim-olur-kodunun-faturasi) ödüyordur; oysa önceden GPU, kimsenin açamadığı düz bir kalemdi. Pod kimliğinde birleşince, nihayet her diğer kaynak için sahip olduğunuz şeye sahip olursunuz: atfedilmiş harcama, üzerinde harekete geçecek verimlilik bağlamıyla. İki düzlem gerektirmesinin nedeni, görünmez olmasının nedeniyle aynıdır — silikon muhasebe düzleminde hiç yaşamadı ve tek bir probe bile sınırı kat etmiyor.

---

*İlgili:* [DCGM exporter belgeleri](https://docs.nvidia.com/datacenter/dcgm/latest/installation/install-dcgm-exporter.html) pod-atfetme yolunu, [DCGM özellik genel bakışı](https://docs.nvidia.com/datacenter/dcgm/latest/user-guide/feature-overview.html) ise profilleme alanlarını tanımlar; bunun yaslandığı eBPF primitifleri [ebpf.io](https://ebpf.io/what-is-ebpf/) ve [Tetragon](https://tetragon.io/)’da belgelenmiştir.

## Sık sorulanlar

**cgroup'lar ve cAdvisor GPU kullanımını neden atfedemiyor?**

Bir cgroup CPU, bellek, block I/O ve PID hesaplar. GPU compute ve framebuffer ise NVIDIA kernel driver'ı ile userspace CUDA runtime'ı tarafından yönetilir ve cgroup'a opaktır. device cgroup controller'ı yalnızca device node'larına erişimi geçitler; tek bir saniye GPU zamanı hesaplamaz. Dolayısıyla cAdvisor'dan Prometheus'a giden boru hattının raporlayacak hiçbir şeyi yoktur.

**nvidia-smi'nin utilization.gpu değeri chargeback için kullanılabilir mi?**

Hayır. NVML onu, örnekleme periyodu boyunca bir ya da daha fazla kernel'in çalıştığı zamanın yüzdesi olarak tanımlar. Bu zamansal varlıktır, cihazın ne kadarının meşgul olduğu değil — onlarca multiprocessor'dan birini işgal eden tek thread'lik bir kernel %100'e yakın raporlayabilir. Bunun üzerinden faturalandırmak, neredeyse boş bir silikonun parasını kesmektir.

**DCGM gerçek donanım sayaçlarını okuyorsa eBPF'e neden hâlâ ihtiyaç var?**

DCGM cihaz-seviyesi metrik üretir, bu yüzden tek bir fiziksel GPU'yu paylaşan tüketicileri ayırt edemez. Time-slicing ya da MPS altında paylaşan her pod aynı kopyalanmış değerleri alır ve NVIDIA, time-slicing etkinken exporter'ının metrikleri container'larla ilişkilendirmediğini belgeler. eBPF ise PID başına atfeder; yani tam da DCGM'in kapatamadığı durumu kapatır.


## Kaynaklar

- [NVML: nvmlUtilization_t.gpu is the percent of time one or more kernels was executing](https://docs.nvidia.com/deploy/nvml-api/structnvmlUtilization__t.html) — NVIDIA
- [DCGM feature overview: SM active, SM occupancy, tensor and DRAM activity, and the counter grouping limit](https://docs.nvidia.com/datacenter/dcgm/latest/user-guide/feature-overview.html) — NVIDIA
- [GPU Operator time-slicing: DCGM-Exporter does not associate metrics to containers when time-slicing is enabled](https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/gpu-sharing.html) — NVIDIA
- [Kubernetes device plugins: extended resources are integer-only and devices cannot be shared between containers](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/) — Kubernetes
