Skip to main content
BlogDevOps

Kubernetes DRA GPU Fallback: Prioritized List di 1.36

Minta H100, kalau tidak ada pakai A100, dalam satu ResourceClaim. Saya membaca post DRA 1.36, docs terbaru dan halaman driver NVIDIA untuk melihat apa yang stable dan apa yang masih berisiko.

Matthews Wong

Software Engineer

Oktober 202612 menit baca
Kubernetes DRA GPU Fallback: Prioritized List di 1.36

Ringkasan Utama

Kubernetes 1.36 menjadikan prioritized list di Dynamic Resource Allocation stable: ResourceClaim berisi subrequest firstAvailable, misalnya satu H100 dulu lalu dua A100, dan scheduler mengambil yang pertama bisa dialokasikan. Partitionable devices seperti MIG slice masih beta di 1.36, jadi adopsi dengan sadar.

Permintaan yang sering saya lihat di cluster GPU sebenarnya bukan untuk satu model kartu. Yang dibutuhkan adalah memori cukup untuk memuat model, dengan preferensi ke kartu cepat dan toleransi ke kartu yang lebih lambat. Cara meminta ala device plugin, jumlah tetap dari satu resource bernama pada container, tidak bisa menyatakan itu, sehingga tim akhirnya punya dua Deployment, dua set manifest, dan manusia yang memutuskan mana yang di-apply.

Kubernetes 1.36 menutup celah itu lewat prioritized list di Dynamic Resource Allocation (DRA) yang sudah stable. Post ini adalah hasil membaca, bukan benchmark. Saya membaca post v1.36 dari tim DRA Kubernetes, halaman concept dan task terbaru, serta dokumentasi driver DRA NVIDIA, dan saya tidak menjalankan apa pun di cluster GPU. Setiap nama field di bawah berasal dari halaman yang saya baca, dan bagian yang saya rakit sendiri saya tandai.

Kenapa claim, bukan sekadar jumlah

DRA bekerja mirip dynamic volume provisioning. Driver mempublikasikan ResourceSlice yang menjelaskan device, admin atau driver mendefinisikan DeviceClass sebagai kategori, dan workload meminta device lewat ResourceClaim atau ResourceClaimTemplate yang memfilter dengan ekspresi CEL pada atribut device. Docs menyebut keunggulan dibanding device plugin: filter CEL yang fleksibel, sharing device antar container dan Pod, konfigurasi per workload, dan kategori terpusat, sementara device plugin butuh request per container dan tanpa sharing atau filter ekspresi. Fitur intinya stable sejak Kubernetes 1.35 dan pertama muncul di 1.30.

Claim adalah objek yang bisa diperiksa, dibagi dan dijadikan template, dan itulah yang membuat fallback mungkin. Jumlah pada container hanyalah angka yang dipenuhi scheduler atau tidak. Claim adalah query kecil, dan query bisa punya pilihan kedua. Harganya adalah objek baru yang harus dipelajari: ResourceSlice, DeviceClass, ResourceClaim, ResourceClaimTemplate.

Prioritized list dalam satu manifest

Post Kubernetes merangkumnya dalam satu kalimat: beri saya H100, tetapi kalau tidak ada, fallback ke A100. Di API ini adalah field firstAvailable pada sebuah request, berisi daftar subrequest berurutan. Halaman API concepts menandainya Stable since 1.36, aktif secara default, dan pertama tersedia di 1.33. Scheduler memilih subrequest pertama yang bisa dialokasikan, dan tiap subrequest bisa membawa selector dan count sendiri.

Di bawah ini claim template dengan bentuk contoh di docs, disesuaikan untuk driver NVIDIA. Atribut productName dan DeviceClass gpu.nvidia.com berasal dari referensi ResourceSlice driver, yang mencantumkan nilai seperti NVIDIA A100-PCIE-40GB. String produk H100 yang persis tidak ada di halaman itu, jadi saya memakai substring match dan Anda sebaiknya memastikan string aslinya dengan kubectl get resourceslices -o yaml di node Anda sendiri.

apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: h100-else-a100
spec:
  spec:
    devices:
      requests:
      - name: gpu
        # firstAvailable replaces "exactly": the scheduler walks this list
        # in order and takes the first entry it can allocate.
        firstAvailable:
        - name: h100
          deviceClassName: gpu.nvidia.com
          selectors:
          - cel:
              expression: |-
                device.attributes["gpu.nvidia.com"].productName.contains("H100")
        - name: a100-pair
          deviceClassName: gpu.nvidia.com
          count: 2   # a fallback may ask for a different shape, not just a different card
          selectors:
          - cel:
              expression: |-
                device.attributes["gpu.nvidia.com"].productName.contains("A100")

Perhatikan fallback di sini tidak sepadan: satu H100 dulu, dua A100 kemudian. Itulah gunanya count per subrequest. Itu juga jebakannya, karena workload yang jalan harus bisa berjalan di kedua bentuk, dan claim tidak bisa menjamin itu untuk Anda.

Menyambungkan ke Pod dan membaca hasilnya

Pod merujuk template di spec.resourceClaims lalu menyebut nama claim di resources.claims milik container, dua langkah yang sama dengan contoh Job di halaman task. ResourceClaimTemplate memberi tiap Pod claim hasil generate sendiri, dengan lifetime terikat pada Pod. Pakai ResourceClaim tulisan tangan bila beberapa Pod harus berbagi device yang sama.

apiVersion: v1
kind: Pod
metadata:
  name: inference
spec:
  containers:
  - name: server
    image: ubuntu:24.04
    command: ["sleep", "9999"]
    resources:
      claims:
      - name: accelerator          # must match the entry below
  resourceClaims:
  - name: accelerator
    resourceClaimTemplateName: h100-else-a100   # one generated claim per Pod

# Then, per the task page, drill down when it does not start:
#   kubectl describe pod inference
#   kubectl get resourceclaims
#   kubectl describe resourceclaim <generated-name>

Scheduler juga memakai posisi subrequest yang terpilih sebagai salah satu input saat menilai node, sehingga node yang bisa memenuhi subrequest berperingkat lebih tinggi lebih mungkin dipilih. Setelah scheduling, saran troubleshooting di docs berlaku: telusuri dari Job ke Pod ke ResourceClaim dan jalankan kubectl describe di tiap level, lihat status dan event.

Beri nama subrequest sesuai maknanya, misalnya h100 dan a100-pair, bukan first dan second. Nama itulah yang Anda cari saat membaca kembali hasil alokasi, dan itu yang dibaca tim on-call tengah malam.

Partitionable devices: MIG tanpa membohongi scheduler

Partitionable devices beta di 1.36 dan docs terbaru menulis Beta since 1.36, aktif secara default, di balik feature gate DRAPartitionableDevices pada kube-apiserver dan kube-scheduler. Modelnya adalah counter. ResourceSlice mendeklarasikan counter set bernama yang menggambarkan isi device fisik, dan tiap device logis mendeklarasikan apa yang ia konsumsi. Pada contoh docs, dua device masing-masing mengonsumsi 6Gi dari satu counter berisi 8Gi, sehingga hanya satu yang bisa dialokasikan pada satu waktu, dan API ResourceClaim di sisi konsumen tidak berubah.

# From the DRA features page: two devices that each consume 6Gi of a shared
# 8Gi counter. Both are advertised, only one can ever be allocated.
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
  name: resourceslice-with-countersets
spec:
  nodeName: worker-1
  pool: {name: pool, generation: 1, resourceSliceCount: 2}
  driver: dra.example.com
  sharedCounters:                 # counter sets live in their own slice...
  - name: gpu-1-counters
    counters:
      memory:
        value: 8Gi
---
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
  name: resourceslice-with-devices
spec:
  nodeName: worker-1
  pool: {name: pool, generation: 1, resourceSliceCount: 2}
  driver: dra.example.com
  devices:                        # ...and the devices that draw on them in another
  - name: device-1
    consumesCounters:
    - counterSet: gpu-1-counters
      counters:
        memory:
          value: 6Gi
  - name: device-2
    consumesCounters:
    - counterSet: gpu-1-counters
      counters:
        memory:
          value: 6Gi

Itulah representasi jujur untuk GPU berkemampuan MIG: device logis yang saling tumpang tindih dan tidak bisa ada semuanya sekaligus. CounterSet harus dipublikasikan di ResourceSlice terpisah dari device yang mengonsumsinya, dan device hanya bisa mengambil dari counter set di pool yang sama. Dengan driver NVIDIA, MIG slice muncul di DeviceClass mig.nvidia.com dengan atribut profile seperti 1g.5gb dan atribut parentUUID, dan panduan driver menunjukkan constraint matchAttribute pada parentUUID untuk memaksa dua slice berada di satu GPU fisik.

# Assembled by me from the NVIDIA MIG guide's description, not copied.
# Two MIG slices that must sit on the same physical GPU.
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: two-mig-slices
spec:
  spec:
    devices:
      requests:
      - name: small
        exactly:
          deviceClassName: mig.nvidia.com
          selectors:
          - cel:
              expression: device.attributes['gpu.nvidia.com'].profile == '1g.5gb'
      - name: medium
        exactly:
          deviceClassName: mig.nvidia.com
          selectors:
          - cel:
              expression: device.attributes['gpu.nvidia.com'].profile == '2g.10gb'
      constraints:
      - requests: ["small", "medium"]
        matchAttribute: "gpu.nvidia.com/parentUUID"

Claim kedua di bawah saya rakit dari deskripsi panduan itu dan bentuk API DRA, bukan salinan dari satu halaman, jadi cocokkan dengan panduan sebelum dipakai. Nama profile bergantung pada model GPU.

Device taint: mencabut GPU rusak tanpa drain node

Device taint bekerja seperti node taint, dengan key, value dan effect. NoSchedule membuat device bertaint tidak terlihat oleh alokasi, NoExecute juga mengeluarkan Pod yang sudah memakainya lewat device taint eviction controller, dan None diabaikan scheduler serta dipakai untuk memberi sinyal. Claim bisa menoleransi taint, dan eviction bisa ditunda dengan toleransi selama durasi tertentu. Admin membuat taint lewat DeviceTaintRule yang memilih device berdasarkan DeviceClass, driver, pool atau nama device, dan rule tanpa selector sengaja tidak menaint apa pun, supaya selector yang terlupa tidak mengusir semuanya.

# Dry run first: effect None changes nothing, but the controller reports
# how many devices it selects and how many Pods NoExecute would evict.
apiVersion: resource.k8s.io/v1
kind: DeviceTaintRule
metadata:
  name: example
spec:
  deviceSelector:
    driver: dra.example.com      # no selector at all would taint nothing, on purpose
  taint:
    key: dra.example.com/unhealthy
    value: Broken
    effect: None                 # read the status message, then change to NoExecute

# kubectl describe devicetaintrules
# kubectl wait --for=condition=EvictionInProgress=false DeviceTaintRule/example

Effect None memberi Anda dry run. Docs menjelaskan cara membuat rule dengan effect None, membaca pesan tentang berapa device terpilih dan berapa Pod yang akan diusir, lalu mengubahnya ke NoExecute. Satu catatan jujur: post 1.36 mencantumkan device taint sebagai beta, sedangkan docs terbaru menandai taint dan DeviceTaintRule sebagai Stable since 1.37. Baca statusnya untuk versi cluster Anda, bukan untuk tanggal post ini.

Apa yang stable di 1.36 dan apa kata docs sekarang

Soal tingkat kematangan, post DRA paling cepat usang, jadi berikut post 1.36 dibandingkan halaman concept terbaru yang saya baca. Jika keduanya berbeda, halaman yang lebih baru yang benar, dan versi cluster Anda yang menentukan mana yang berlaku.

FiturDi post 1.36Docs terbaru
Prioritized listStableStable since 1.36
Extended resource supportBetaStable since 1.37
Partitionable devicesBetaBeta since 1.36, aktif default
Device taints dan tolerationsBetaStable since 1.37
Device binding conditionsBetaBeta since 1.36, aktif default
ResourceClaim untuk PodGroupAlphaBeta since 1.37, nonaktif default
Node allocatable resources (CPU, memory)AlphaAlpha since 1.36, nonaktif default

Extended resource support adalah yang paling pelan tapi penting untuk adopsi. DeviceClass bisa membawa extendedResourceName, sehingga Pod yang meminta example.com/gpu: 2 dengan cara lama dipenuhi oleh device DRA di sebagian node dan oleh device plugin di node lain. Operator bisa memigrasikan cluster tanpa memaksa setiap tim aplikasi menulis ulang manifest lebih dulu.

# From the DRA API page: a DeviceClass that answers old-style requests.
# A Pod asking for "example.com/gpu: 2" can now land on DRA-managed nodes.
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
  name: gpu.example.com
spec:
  selectors:
  - cel:
      expression: device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'
  extendedResourceName: example.com/gpu

Yang berisiko: trade-off yang harus disebut

Batas pertama tertulis jelas di docs: scheduler tidak mendukung preemption untuk resource DRA. Pod berprioritas tinggi yang butuh device DRA tidak bisa mengusir Pod berjalan yang memegangnya, dan tetap Pending sampai device dibebaskan. Daftar fallback membuat ini lebih jarang menyakitkan karena ada lebih banyak cara untuk terpenuhi, tetapi tidak menghilangkannya.

Yang kedua, prioritized list diputuskan per Pod. Docs menyebut bila Pod bagian dari ReplicaSet atau grup serupa, Anda tidak bisa mengandalkan semua anggota memilih subrequest yang sama, jadi workload harus bisa menampungnya. Delapan replika bisa naik sebagai campuran satu H100 dan dua A100. Inference server dengan ukuran tensor parallel tetap atau anggaran memori tetap harus menoleransi itu atau dipecah ke claim terpisah.

Driver NVIDIA adalah catatan lain. README-nya menyatakan fitur alokasi GPU masih dalam pengembangan dan belum didukung resmi, dengan ComputeDomain sebagai bagian yang didukung resmi, dan halaman instalasi mewajibkan gpuResourcesEnabledOverride=true untuk alokasi GPU. Dynamic MIG adalah gate Alpha terpisah yang menghancurkan partisi MIG yang tidak ia buat saat kubelet plugin mulai. Jangan aktifkan di node yang partisinya ingin Anda pertahankan.

Dua catatan lebih kecil dari panduan MIG: pada kartu Ampere seperti A100, MIG mode harus diaktifkan sebelum driver mulai karena mengubahnya butuh reset GPU, dan bila nonaktif driver kembali ke alokasi GPU penuh dan tidak mengiklankan partisi. Claim yang meminta profile MIG lalu tidak menemukan apa pun, yang terlihat seperti masalah scheduling padahal bukan.

Yang dikerjakan hari Senin

Mulai dengan memastikan apa yang benar-benar didukung cluster Anda, lalu tulis claim fallback terkecil yang masih bisa Anda terima. Perintah pertama hanya membaca.

  1. Cek versi server. Halaman task mensyaratkan v1.34 atau lebih baru, dan prioritized list stable sejak 1.36.
  2. Pastikan DRA menjawab: kubectl get deviceclasses harus mengembalikan daftar atau No resources found, bukan error resource type tidak ada.
  3. Dump ResourceSlice Anda dan baca nama atribut serta string produk yang sebenarnya sebelum menulis CEL apa pun.
  4. Tulis claim firstAvailable dengan dua entri yang pilihan keduanya benar-benar bisa dijalankan workload Anda, lalu apply ke namespace uji.
  5. Sebelum menyentuh GPU produksi, buat DeviceTaintRule dengan effect None dan baca pesannya untuk melihat apa yang akan diusir NoExecute.
# 1. Server version: task pages need v1.34+, prioritized list is stable from 1.36
kubectl version

# 2. Is DRA answering? "No resources found" is fine; "doesn't have a resource type" is not
kubectl get deviceclasses

# 3. What do the drivers actually publish? Read real attribute names and product strings
kubectl get resourceslices
kubectl get resourceslice <name> -o yaml

# 4. Apply the fallback claim to a test namespace, then drill down if it hangs
kubectl apply -n dra-test -f h100-else-a100.yaml
kubectl describe resourceclaims -n dra-test

# 5. Rehearse an eviction before it is real (effect: None, then read the message)
kubectl apply -f taint-dry-run.yaml
kubectl describe devicetaintrules

Jangan menambahkan DRA ke workload yang tidak butuh pilihan. Jika armada Anda satu model kartu dan jumlah sudah cukup, device plugin masih bekerja, dan docs menjelaskan jalur migrasi sebagai bertahap, dengan tujuan yang dinyatakan maintainer yaitu memindahkan pengguna dari device plugin ke DRA seiring waktu.

Aturan yang saya bawa: minta kapabilitas dengan fallback berurutan, pastikan workload di balik setiap entri memang valid, dan cek kematangan tiap fitur terhadap versi cluster Anda sendiri, bukan terhadap tanggal blog tempat Anda membacanya.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apa itu prioritized list di Kubernetes DRA?

Ini cara memberi ResourceClaim daftar device yang bisa diterima secara berurutan lewat field firstAvailable. Scheduler memilih subrequest pertama yang bisa dialokasikan. Docs Kubernetes menandainya Stable since 1.36 dan pertama tersedia di 1.33.

Bagaimana cara meminta H100 lalu fallback ke A100 di Kubernetes?

Letakkan dua subrequest di bawah firstAvailable pada ResourceClaim atau ResourceClaimTemplate, selector H100 dulu lalu A100, masing-masing dengan selector CEL dan count opsional. Anda butuh driver DRA yang mempublikasikan atribut produk untuk dicocokkan; driver NVIDIA mempublikasikan productName. Cek dulu string produk yang persis di ResourceSlice Anda sendiri.

Apakah semua replika Deployment mendapat GPU yang sama dengan prioritized list?

Tidak. Docs menyebut pilihan dibuat per Pod, jadi anggota ReplicaSet atau grup serupa bisa mendapat subrequest yang berbeda. Workload Anda harus berjalan benar di setiap entri dalam daftar, atau dipecah ke claim terpisah.

Apakah partisi MIG sudah stable di Kubernetes DRA?

Partitionable devices, fitur DRA yang memodelkan slice ala MIG, beta di 1.36, dan docs terbaru mencantumkannya Beta since 1.36, aktif secara default. Gate dynamic MIG di driver NVIDIA adalah fitur Alpha yang terpisah. Static MIG aktif default di driver itu dan butuh partisi dibuat sebelum driver mulai.

Apakah saya harus menulis ulang Pod untuk pindah dari device plugin ke DRA?

Belum tentu. DeviceClass bisa mengatur extendedResourceName, sehingga Pod yang meminta extended resource seperti example.com/gpu bisa dipenuhi oleh device DRA. Docs menandai extended resource allocation Stable since 1.37 dan menyebutnya cara memigrasikan cluster sebelum tim aplikasi mengadopsi ResourceClaim.