ステータス: ドラフト (2026-10-05)、実測値を反映。 「実測例」セクションの値は、1台の GPU サーバから読み取り専用コマンドで直接確認したものであり、デバイス ID などの識別情報はプレースホルダーに置き換えています。原理の説明はカーネルと KubeVirt のドキュメントの内容に基づき、この記事の執筆時点ではドキュメントを再び開いて照合することはしていません(未確認)。メモリのピン留めとクラッシュループの関係のような、検証できなかった推測はそのように明記しています。 作成ルール: 実測データを基本とし、測定できなかったものは「未実測」と表示します。
目次
- 1. なぜパススルーを使うのか
- 2. 原理: 4つのパーツ
- 3. 実測例: 6GB 級 GPU を渡した1台のサーバ
- 4. 同じデバイスで nvidia-smi が失敗する理由
- 5. KubeVirt がデバイスを VM に接続する経路
- 6. よくある罠: メモリのピン留めとホストの OOM
- 7. コンテナ用 GPU に戻す(手順の例)
- 8. 面接で出そうな質問
- 9. よくある誤解
- 外部の根拠: パススルーのパフォーマンスオーバーヘッド
- 確認できなかったこと
- 参考文献
- 確認したバージョンと日付
1. なぜパススルーを使うのか
GPU をワークロードに提供する方法は大きく分けて4つあります。
| 方式 | 単位 | 分離 | この記事との関係 |
|---|---|---|---|
| コンテナ(device plugin) | GPU 1枚または時分割 | プロセスレベル、ホストカーネルとドライバを共有 | 第3回 |
| MIG | GPU をハードウェア的に分割(対応モデルのみ) | ハードウェアレベル | 第3回 |
| vGPU | ドライバが作成した仮想 GPU | ハイパーバイザーが管理、ライセンスが必要 | 範囲外 |
| パススルー | 物理 GPU 1枚全体 | VM がデバイスを独占 | この記事 |
パススルーが必要になるケースは3つに絞られます。ゲストでホストとは異なるカーネルやドライバのバージョンを使用する必要があるとき、ゲスト OS が Linux ではないとき、そしてコンテナでは満たせない分離を求められるときです。その代わり、 その GPU はホストとコンテナで同時に使用することはできません。 この記事の後半で、同じデバイスが一方では nvidia-smi が失敗する様子を実際に確認します。
2. 原理: 4つのパーツ
パススルーとは、デバイスの所有権をホストカーネルの汎用ドライバからユーザー空間プロセス(QEMU)へ移すことです。そのために4つのパーツが協力して動作します。
2.1 ドライバのバインディング
PCI デバイスはベンダー ID とデバイス ID(例の GPU は 10de:XXXX)を持ち、カーネルはその ID に適合する1つのドライバをアタッチします。通常は nvidia ドライバがアタッチされ、それによりホストで nvidia-smi が動作します。パススルーでは代わりに vfio-pci をアタッチします。 vfio-pci はデバイスを使用するためのドライバではなく、デバイスをユーザー空間に引き渡すための仲介用ドライバです。このドライバがアタッチされたデバイスは、ホストカーネルは使用しません。
アタッチする方法は2つあります。実行中に driver_override と bind/unbind で切り替える方法と、起動時に vfio-pci に該当 ID をあらかじめ取得させる方法です。例のサーバは後者です(第3節)。
2.2 IOMMU: デバイスのメモリへのアクセスを閉じ込める装置
GPU は DMA によってシステムメモリを直接読み書きします。ゲストにデバイスを渡すと、ゲストが使用するメモリのアドレスがホストの実際のアドレスとは異なるため、デバイスが予期せぬホストメモリに触れてしまう可能性があります。 IOMMU はデバイスが生成する DMA アドレスを変換し、許可された範囲を制限することでこの問題を防止します。ゲストのメモリ領域のみをデバイスに開放する仕組みです。
分離の最小単位は IOMMU グループです。同じグループに属するデバイス間では互いの DMA を分離できないため、グループ内のデバイスは すべて同じ所有者に引き渡す必要が あります。そのため、GPU のオーディオ機能(例の 01:00.1)も一緒に vfio-pci にまとめます。
2.3 VFIO インターフェース
VFIO はデバイスをユーザー空間に安全に露出するためのカーネル機能です。ユーザー空間プロセスは /dev/vfio/vfio(コンテナ)と /dev/vfio/<그룹 번호> を開いて次の処理を行います。デバイスの BAR(メモリ領域)を自身のアドレス空間にマッピングし、ゲストメモリを IOMMU に登録し、デバイスの割り込みをイベントファイルディスクリプタとして受け取ってゲストに注入します。QEMU がこの処理を行います。
ゲストメモリは DMA の対象となるため、ホストが回収したりスワップしたりできないように固定(pin)される必要があります。 12GiB メモリの VM は起動時にホストメモリ 12GiB を固定することを意味します(ドキュメントに基づく記述であり、直接測定はしていません)。
2.4 リセット
VM が終了したら、次のユーザーが安全に使えるようにデバイスをクリーンな状態に戻す必要があります。最も望ましい方式はデバイスがサポートする FLR(Function Level Reset)であり、サポートしていない場合はバスリセットを使用します。リセットが正常に行われない GPU は、VM を一度使用した後にホストを再起動しなければならないという問題を引き起こします。
3. 実測例: 6GB 級 GPU を渡した1台のサーバ
すべて読み取り専用コマンドで確認した値であり、ハードウェアごとに異なる 例となる値です。ホストのカーネルは 7.0.0-34-generic です。デバイス ID は XXXX(GPU)と YYYY(オーディオ)にマスクしています。
| 項目 | 値 | 意味 |
|---|---|---|
lspci -nnk -d 10de: |
01:00.0 GPU(10de:XXXX)と 01:00.1 オーディオ(10de:YYYY)のどちらも Kernel driver in use: vfio-pci |
両方の機能とも vfio-pci が所有 |
| IOMMU グループ | グループ 16、メンバーは 01:00.0 と 01:00.1 のみ |
グループがクリーンなため、この GPU のみを個別に渡すことができる |
| カーネルコマンドライン | iommu 関連の引数なし、ただし IOMMU グループは 20 個 |
このカーネルでは IOMMU がデフォルトで有効になっている |
| ロードされたモジュール | vfio_pci, vfio_pci_core, vfio_iommu_type1, vfio, kvm_intel |
VFIO スタックがロードされている |
/dev/vfio/ |
16(グループデバイス)と vfio(コンテナ) |
ユーザー空間に引き渡す準備が整った状態 |
reset_method |
flr bus |
FLR をサポートし、そうでない場合はバスリセット |
| BAR 領域 | 領域 0 が 16M、領域 1 が 8G(prefetchable)、領域 3 が 32M | GPU のメモリウィンドウをゲストにマッピングすることになる |
| PCIe リンク | 能力 16GT/s x16、現在 16GT/s x8 (downgraded) |
リンク幅が半分に低下している |
リンク幅が x8 に低下した原因は確認できていません。ボードの配線のせいである可能性もあれば、電源状態のせいである可能性もあるため、断定はしません。
3.1 設定ファイル
この例でパススルーを作成した設定は2つのファイルです。
# /etc/modprobe.d/vfio-gpu.conf
options vfio-pci ids=10de:XXXX,10de:YYYY
softdep nvidia pre: vfio-pci
softdep nouveau pre: vfio-pci
softdep snd_hda_intel pre: vfio-pci
# /etc/modules-load.d/vfio-pci.conf
vfio-pci
options vfio-pci ids=…はvfio-pciにこれら2つの ID を自分のものとして取得させます。softdep … pre: vfio-pciはnvidia,nouveau,snd_hda_intelがロードされる前にvfio-pciを先にロードし、それらのドライバがデバイスを先取りするのを防ぎます。modules-load.dは起動時にvfio-pciモジュール自体をロードします。
例のサーバのブートイメージ(initramfs)には vfio の項目が1行もありませんでした。そのため、この設定はイメージの再生成なしでファイルだけで動作し、元に戻すときも同じ理由でファイルを削除すれば済みます(ただし再起動後に実際に元通りになるかはまだテストしていません)。
4. 同じデバイスで nvidia-smi が失敗する理由
このホストで nvidia-smi を実行すると、「NVIDIA driver と通信できません」というメッセージが表示されます。これはドライバがインストールされていないという意味ではありません。ホストには nvidia-driver-595-open(595.91.07)がインストールされており、現在のカーネル用のモジュールパッケージもあります。
カーネルログがその原因をそのまま示しています。
NVRM: GPU 0000:01:00.0 is already bound to vfio-pci.
nvidia モジュールがデバイスを取得しようと試みるたびに、すでに vfio-pci が所有しているというメッセージが繰り返し出力されていました。デバイスにアタッチされているドライバは1つだけであるため、所有権が VFIO にある間はホストの nvidia-smi には通信相手が存在しません。故障ではなく 設定通りの正常な動作です。
Kubernetes 側の状態も同様の状況を示しています。
| 場所 | 値 |
|---|---|
ノード nvidia.com/gpu |
0 (コンテナに渡す GPU はなし) |
ノード nvidia.com/<모델별 자원 이름> |
1 (VM に渡すデバイスあり) |
ノードラベル nvidia.com/gpu.deploy.operands |
false (GPU Operator のコンポーネントが無効) |
GPU Operator driver.enabled |
false (ホストにインストールされたドライバを使用する設定) |
5. KubeVirt がデバイスを VM に接続する経路
- KubeVirt の設定の
permittedHostDevicesにpciVendorSelector: 10DE:XXXXとresourceName: nvidia.com/<모델별 자원 이름>が登録されており、機能ゲートHostDevicesが有効になっています。 - ノードの
virt-handlerがその ID のデバイスを見つけて、Kubernetes の拡張リソースとして広告します。その結果が上の表のnvidia.com/<모델별 자원 이름>: 1です。 - VM 定義の
devices.gpusにdeviceName: nvidia.com/<모델별 자원 이름>を記述します。スケジューラはこのリソースが存在するノードにのみ VM を配置します。 - VM が起動すると、QEMU が VFIO を介して該当する IOMMU グループを開き、デバイスをゲストに接続します。
ここで重要な点は、 リソース名に nvidia.com/gpu ではなく別の名前が使用されるということです。コンテナ用 GPU と VM 用 GPU は異なるリソースであり、1枚の GPU はそのどちらか一方としてのみ広告されます。
6. よくある罠: メモリのピン留めとホストの OOM
GPU を渡した VM は、2.3節のようにゲストメモリをホストにピン留めします。ピン留めされたメモリはスワップによって退避させることができないため、VM が起動した瞬間にゲストのサイズ分のホストメモリが一度に必要になります。同じホスト上で他のワークロードやノードdaemon が一緒に稼働している場合、合計が物理メモリを超過しやすくなります。
ホスト全体のメモリが不足すると、カーネルの OOM killer が動作します。このとき、カーネルログにはコンテナの制限超過(Memory cgroup out of memory)ではなく、ホスト全体のメモリ不足(Out of memory: Killed process)として記録されます。犠牲になるのは VM プロセス(qemu-kvm)だけでなく、ノードのシステムデーモンである可能性もあります。VM が終了してから再起動すると同じことが繰り返されるクラッシュループになり、その間にデーモンも継続して一緒に終了します。
筆者の実験環境で観測したホストの OOM ログは、この流れと一致していました。ただし、VM が起動するときに実際にピン留めしたメモリのサイズを測定していないため、この解釈は 推測です(未実測)。教訓は2つあります。ホストのメモリに余裕がないサーバには GPU VM を複数載せない方が安全であり、VM を起動する前にゲストメモリの合計とホストですでに使用しているメモリを事前に足し合わせる必要があります。
7. コンテナ用 GPU に戻す(手順の例)
パススルーを解除し、デバイスをコンテナ用に戻す最も単純な方法は、ノードを空にして(cordon、drain)、設定ファイルを削除した後に再起動することです。ただし、そのノードで他のサービスが稼働している場合、再起動はすべてを停止させる大掛かりな作業になります。以下は 再起動なしでデバイスのみを渡す 例です。可能であると判断した根拠は、すべて読み取り専用で事前に確認しました。
| 事前確認 | 結果 |
|---|---|
/dev/vfio/<그룹 번호> を掴んでいるプロセス |
なし |
| GPU を使用している VM | なし |
| デバイスの状態 | アイドル、リセット方式 flr bus サポート |
nvidia モジュール |
595.91.07、現在のカーネル用、署名済み |
まず2つの設定ファイルをバックアップしてから削除し、以下の2つのステップを実行しました。
# 1) 이 장치는 nvidia 드라이버로만 붙도록 지정한다.
echo nvidia | sudo tee /sys/bus/pci/devices/0000:01:00.0/driver_override
# 2) vfio-pci 에서 푼다. 풀리는 즉시 nvidia 가 장치를 가져간다.
echo 0000:01:00.0 | sudo tee /sys/bus/pci/drivers/vfio-pci/unbind
結果。 unbind の直後、デバイスのドライバが nvidia に変わり、 nvidia-smi が動作しました。
| 項目 | 値 |
|---|---|
| GPU | 6GB 級ノートPC 用 GPU、6,144MiB |
| ドライバ | 595.91.07 |
lspci |
01:00.0 の Kernel driver in use: nvidia |
| ホスト | 稼働時間を維持、負荷の変化なし、他のサービスへの影響なし |
あわせて確認すべき点。
- オーディオ機能(
01:00.1)はvfio-pciにそのまま残っています。 コンテナ用のGPU使用には影響がないと考えていますが、確認はしていません。 - カーネルログに
NVRM: … NV_ERR_INVALID_DATA(PlatformRequest) の警告が2行出力され、power.drawが約752Wと読み取られました。 このGPUの電力値としてはあり得ない数値であり、センサーやプラットフォーム情報の読み取りに問題があると考えられますが、原因は特定できていません。切り替え直後の電力値は信用しないでください。 - 再起動後も
nvidiaが自動的にアタッチされるかはテストしていません。 設定ファイルを削除したためアタッチされると予想されますが、まだ推測の域を出ません。 - Kubernetes側はそのままです。 ノードの
nvidia.com/gpuは依然として0、KubeVirtのnvidia.com/<모델별 자원 이름>も1のまま残っています(virt-handlerが再読み込みするまでの古い値だと思われます)。コンテナがGPUを使用するには、ノードラベルnvidia.com/gpu.deploy.operandsをtrueに変更してGPU Operatorのコンポーネントを有効化する必要があります。
元に戻すには、 driver_override を vfio-pci に変更し、 nvidia を解除すればよいです。この切り替えの後は、GPU VMはこのGPUを使用できなくなります。 1枚のGPUをコンテナとVMで交互に使用するにはこの切り替えを毎回行う必要があるため、枚数が少ない環境では用途を一つに決める必要があります。
8. 面接で出そうな質問
- パススルーとvGPU、MIGの違いは何か? 単位(デバイス全体、ソフトウェア分割、ハードウェア分割)、ライセンス、サポートされるハードウェアによって区別します。
- IOMMUグループがなぜ重要なのか? 同じグループのデバイスはDMAを相互に隔離できないため、一緒に渡す必要があります。例示サーバーのグループはGPUとオーディオの2つだけでクリーンでした。
- VMの起動時にホストメモリがなぜピン留めされるのか? デバイスがゲストメモリに対してDMAを行うため、その領域が移動したりスワップされたりしてはならないためです。
- ホストで
nvidia-smiができないが、ドライバーはインストールされている場合は? デバイスにアタッチされているドライバーをまず確認します(lspci -nnk).vfio-pciがアタッチされていれば設定どおりの動作です)。 - このGPUをコンテナとVMで同時に使用できるか? パススルー方式ではできません。時分割やMIG、vGPUを検討する必要があります。
9. よくある誤解
- 「ドライバーが壊れた」:所有権が別のドライバーにある可能性があります。
- 「パススルーすれば性能損失がない」:デバイス自体の性能はほぼそのままですが、リンク幅、リセット、メモリピン留めなどの運用コストが伴います。例示サーバーでは性能損失を測定していません(未測定、サービスが稼働中のサーバーであるためGPUを再びパススルーに戻してテストすることはしませんでした)。外部の測定結果は下記の「外部の根拠」にまとめています。
- 「IOMMUは個別に有効化する必要がある」:例示カーネルでは引数なしでも有効になっていました。機器やカーネルの設定によって異なるため、グループが表示されるかをまず確認します。
外部の根拠:パススルーの性能オーバーヘッド
パススルーVMの性能を直接測定できなかったため、同様の疑問を測定した外部資料を確認しました。閲覧できた範囲は、アブストラクト、ベンダーの記事、一部の原文です。
| 資料 | 確認した内容と条件 | 信頼性と限界 |
|---|---|---|
| Walters他、「GPU Passthrough Performance: A Comparison of KVM, Xen, VMWare ESXi, and LXC for CUDA and OpenCL Applications」、IEEE CLOUD 2014 | アブストラクト:KVMパススルーがベアメタルの98~100%、XenとVMwareが96~99%(2世代のGPU、2つのプロセッサマイクロアーキテクチャ) | 査読付き論文や本文を閲覧できず、IOMMU、ラージページ、NUMA、リンク幅の条件は不明。2014年のGPU |
| Steininger他、「GPU Passthrough Across Virtualization Platforms for LLM Inference」、Computers(MDPI) 第15巻、2026 | アブストラクト:ワークステーション級GPU 1枚をProxmox VM・LXC、KVM、OpenStack、Podmanでパススルーし、vLLM小規模モデルのスループットやトークン間レイテンシがいずれのプラットフォームでもベアメタルの2%以内(統計的に同等) | 著者が一般的な主張ではないと述べている。デバイスを飽和させない領域。本文へのアクセス拒否によりアブストラクトのみ確認 |
| OpenNebulaベンダーブログ(2026-03-18) | GB200 NVL4パススルー cuBLAS GEMM 71,891対71,738 GFLOP/s(0.21%の差)、vLLMスループット ±4%以内 | ベンダーの記事、NUMA固定・ラージページ・IOMMUの設定が明記されていない |
| VMware性能調査(2023-10) | vGPU(全体プロファイル H100)でのGPT-J 6Bがベアメタルの97.9~99.8%(MLPerf Inference v3.1) | パススルーではなくvGPU。MLCommonsの検証を受けていない結果 |
| Samani, Amini Salehi, arXiv 2112.09780, 2021 | ベアメタル、VM(パススルー)、コンテナの比較。一貫した単一のオーバーヘッドはなく、エンドツーエンドの推論時間がCPUの前処理・後処理やストレージI/Oに支配されるため、GPU部分の影響は小さい。ホストにアイドルコアがないとVMのオーバーヘッドが大きくなり、CPUピン留めで軽減される | Tesla K40、2ソケット80コア、HDD。古いGPU |
| NVIDIA CUDA Programming Guide 3.4.2.5, NCCL GPU troubleshooting | ベアメタルではIOMMUを無効化するよう求め、VMパススルーではIOMMUを有効化してVFIOを使用するよう求めている。ACSはP2PをCPUルートコンプレックス経由で迂回させるため帯域幅が大幅に低下するが、VMではACSを無効化できず、VM内で最大性能を出すにはネットワークアダプターのATSが必要 | 公式ドキュメント。単一GPUサーバーではP2Pは争点にならない |
- 判定:限定的な裏付け。 「デバイス自体の性能はほぼそのまま」という点は、外部測定が報告している0〜5%の範囲と一致します。ただし、確認された数値はアブストラクト、ベンダーの記事、デバイスを飽和させない領域のものであり、 我々のGPUと同じ条件での数値は得られていません。 リンク幅、ラージページ、IOMMUの設定を変数として測定した1次資料は見つかりませんでした。
- 「IOMMUは個別に有効化する必要がある」については、KubeVirtのドキュメントがカーネル引数(
intel_iommu=on,amd_iommu=on)を有効化するよう案内している点とディストリビューションのデフォルト値が異なることを踏まえると、本記事の「グループが表示されるかをまず確認」という表現が安全な記述と言えます。
確認できなかったこと
- GPU VMの起動時に実際にピン留めされたメモリサイズ(第6節の解釈はログと一致する推測)。
- PCIeリンクがx8に低下した原因。
- パススルーVM内でのGPU性能(コンテナと比較したスループットは未測定。本番サービスが同じサーバーで稼働している環境であるため、GPUをVMに再び割り当てるテストは行わず、外部の測定結果で代用しました)。
- 再起動後も
nvidiaが自動的にアタッチされるか(再起動せずに切り替えたため、再起動はテストしていない)。 - 本記事の原理説明で引用しているカーネルのVFIOドキュメントとKubeVirtのホストデバイスドキュメントを、今回改めて突き合わせて照合することはしませんでした。
参考資料
- カーネルVFIOドキュメント:https://www.kernel.org/doc/html/latest/driver-api/vfio.html
- KubeVirtホストデバイスユーザーガイド:https://kubevirt.io/user-guide/compute/host-devices/
確認したバージョンと日付
2026-10-05時点。ホストカーネル 7.0.0-34-generic、NVIDIAドライバーパッケージ 595.91.07(ホストにインストール)、KubeVirt HostDevices 機能ゲートを使用。