TT Lab
はじめる
学ぶ 学習パス コース

GPU Operatorとタイムスライシング

ClusterPolicy・RuntimeClass・資源のアドバタイズ

TT Labで続きを見る

一言でいうと

GPU Podが起動するには、互いにまったく関係のない2つのゲートを通過する必要があります。スケジューラーが見るnvidia.com/gpuリソースと、ノードのランタイムが見るRuntimeClassハンドラーです。片方だけを整えても、Podは、どちらで止まったのかを教えてくれません。

なぜこの区別が必要なのか

「GPU Podが起動しない」という報告を受けたら、症状はちょうど2つのうちのどちらかです。

この2つを切り分けられないと、device pluginのログを調べながら、実はcontainerdの問題だったものに何時間も時間を取られます。逆も同じです。2つのゲートがなぜ分かれているのかを知っていれば、最初の3分で切り分けを決められます。

どう動くのか

リソース側。GPUは、cpuやmemoryのようにkubeletが自分で数えるリソースではありません。device pluginがノード上でデバイスを開いて確認し、kubeletにその一覧を渡すと、kubeletがその個数を、ノードオブジェクトのstatus.capacityとstatus.allocatableに、拡張リソースとして書き込みます。スケジューラーが見るのはデバイスではなく、その数字です。そのため、数字だけがあってデバイスがなくてもスケジューリングは成立し(それがこのコースのラボの立ち位置です)、逆にデバイスが正常でもpluginが止まっていれば、スケジューラーはそのノードにGPUがないと判断します。

拡張リソースには、もう1つ規則があります。requestsとlimitsを別々の値にはできません。limitsに書いた値がそのままrequestsになり、整数でなければなりません。GPUを0.5枚だけ渡すことはできないからです。

ランタイム側。RuntimeClassは、「このPodをどのランタイムハンドラーで作るか」を選ぶ、クラスタースコープのオブジェクトです。

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: nvidia
handler: nvidia          # 이 문자열이 containerd 설정의 runtimes.<이름> 과 맞아야 한다

kubeletはPodを作るとき、このhandler文字列をCRIリクエストに載せて送り、containerdは自分の設定のruntimesテーブルから同じ名前を探します。なければ、コンテナの作成が拒否されます。ここで重要なのは、APIサーバーがこの名前を検証しないという点です。RuntimeClassはkubectl applyでいくらでも作成でき、存在しないハンドラーを指していても、誰も警告しません。そのずれは、Podを作る瞬間、しかもそのノードでだけ表面化します。

2つのゲートの関係を1行でまとめると、次のとおりです。

스케줄러  : nvidia.com/gpu 숫자를 보고 "어느 노드로 보낼까" 를 정한다
kubelet   : RuntimeClass handler 를 containerd 에 넘겨 "어떻게 만들까" 를 정한다

現場での姿

第一に、runtimeClassNameを書かなくてよいクラスターが多くあります。toolkitがcontainerdのdefault_runtime_nameをそもそもnvidiaに書き換えておく構成が、一般的だからです。便利ですが、代償があります。GPUを使わないPodまでnvidiaランタイムを通ることになり、その設定が壊れると、GPU PodだけでなくクラスターのすべてのPodが起動できなくなります。事故の影響範囲が変わります。

第二に、CDIへの移行が進んでいます。Container Device Interfaceは、「デバイスをコンテナに入れる方法」を、ランタイムの種類に依存しないJSON/YAMLの仕様として標準化したものです。CDIを使うと、nvidia専用のランタイムバイナリがなくても、nvidia.com/gpu=allのようなデバイス名で注入できます。ただし、containerd側のCDIサポートは、バージョンごとに有効にする方法が異なるため、現場では今、2つの方式が混在しています。

第三に、Pendingの原因はリソースだけではありません。GPUノードにはたいていnvidia.com/gpu=present:NoScheduleのようなテイントが付いていて、一般のワークロードが高価なノードを占有しないようにしています。GPU Podにトレラレーションがないと、リソースが余っていてもスケジュールされません。kubectl describe podの最後のイベントが、常に最初に読むべき一文である理由です。

次のラボですること

すぐ後のラボで、2つのゲートのうちリソース側を、8つのステップで最後まで進みます。ノードのstatusに拡張リソースを書き込んでスケジューラーに判定させ、requestsとlimitsの規則がapiserverで拒否されるのを確認し、同じPendingでも原因がテイントなのかリソースなのかを、メッセージで切り分けて読みます。ここで切り分けを正確にできれば、あとに出てくる障害の話がずっと早く読めます。