2026年2月22日
インフラ
CUDA実行環境の選定指針|ベアメタル・Docker・VMの構造比較とユースケース別の最適解
GPUを用いたLLM推論基盤を構築する際、CUDAとDockerの組み合わせはCPUアプリとは異なる固有の課題を抱えます。本稿では、ベアメタル・Docker・VMの構造的な違いと、よくあるハマりどころを整理し、規模・用途別の最適な実行環境の選定指針をDigitalBaseの実務知見からまとめます。

はじめに
GPUを用いたAI推論やローカルLLMの稼働環境を構築する際、「とりあえずDockerで」という前提から検討を始める例は少なくありません。しかし、CUDAとDockerの組み合わせは、CPUベースのアプリケーションとは根本的に異なる課題を抱えています。
本稿では、CUDA環境をDockerで構築する際にハマりやすいポイントを整理したうえで、ベアメタル・Docker・VMそれぞれの構造的な違いと、規模・用途別の最適解を解説します。DigitalBaseが社内AI基盤の構築支援で蓄積した知見を前提に、過剰な抽象化レイヤーを避けつつ運用しやすい構成を選ぶための判断材料を提供します。
DockerとCUDAの相性が問題になる理由
Dockerの設計思想は「ハードウェアを抽象化し、どこでも同じように動かす」ことにあります。一方でCUDAは「特定のNVIDIA GPUハードウェアに密結合し、ドライバのバージョンまで一致を要求する」性質を持ちます。この2つの方向性は本質的に相反します。
Dockerのポータビリティが GPU で崩れる
通常のDockerコンテナでは、アプリケーションの依存関係をすべてイメージ内に閉じ込めることで環境差異をなくします。しかしGPUを使う場合、コンテナ内のCUDAランタイムがホストOSのNVIDIAドライバに依存するため、この「閉じ込め」が成立しません。
通常の Docker: コンテナ内に全依存関係 → どの環境でも動く GPU Docker: コンテナ内の CUDA ← ホストの NVIDIA ドライバに依存 → 環境ごとに動作が変わる
結果として、ホスト側のドライバとコンテナ内のCUDAバージョンの互換性を常に意識する必要が生じ、Dockerの最大の利点であるポータビリティが大きく損なわれます。
CUDA × Docker でハマりやすい5つのポイント
1. nvidia-container-toolkit の設定
DockerコンテナからGPUにアクセスするには、nvidia-container-toolkit(旧 nvidia-docker2)が必要です。この「橋渡し」ツールを別途導入しなければならない時点で、Docker単体ではGPUを扱えないことを意味します。
// daemon.json に runtime を登録する { "runtimes": { "nvidia": { "path": "/usr/bin/nvidia-container-runtime", "runtimeArgs": [] } } }
この設定を忘れると could not select device driver エラーが発生します。GPU Dockerの導入初期に最も多く遭遇するトラブルの一つです。
2. CUDA バージョンとドライバの互換性
NVIDIAドライバには「Forward Compatibility」という仕組みがありますが、すべての組み合わせで動作するわけではありません。
| ホストドライバ | 対応 CUDA | よくあるトラブル |
|---|---|---|
| 535.x | CUDA 12.2 まで | CUDA 12.3 以降のイメージが動かない |
| 550.x | CUDA 12.4 まで | 新しい PyTorch イメージでエラー |
| 560.x+ | CUDA 12.6+ | 比較的安定 |
ドライバをアップデートすれば解決する場合が多いものの、カーネルモジュールの再ビルドを伴うことがあり、本番環境では気軽に実施できません。
3. cgroup v2 問題
Ubuntu 22.04以降はデフォルトでcgroup v2を使用します。古いバージョンのDockerやnvidia-container-toolkitはcgroup v2のデバイス管理に対応しておらず、コンテナからGPUが見えないという問題が発生します。
# コンテナ内で nvidia-smi が動かない $ docker run --gpus all nvidia/cuda:12.4-base nvidia-smi Error: could not select device driver "" with capabilities: [[gpu]]
解決にはDockerとtoolkitの双方を対応バージョンへアップデートする必要があります。
4. 権限とセキュリティ
コンテナからGPUデバイス(/dev/nvidia*)にアクセスするには、適切な権限設定が必要です。--privileged フラグを使えば動作しますが、コンテナのセキュリティ分離が事実上無効化されてしまいます。
# 動作するが、セキュリティ的に避けるべき docker run --privileged nvidia/cuda:12.4-base nvidia-smi # 推奨される方法 docker run --gpus all nvidia/cuda:12.4-base nvidia-smi
権限設定の不備により「sudoでしかGPUにアクセスできない」といった状態に陥るケースは、現在でもしばしば報告されています。
5. パフォーマンスオーバーヘッド
Docker自体のオーバーヘッドは通常小さいものの、GPUパススルーの場合はデバイスマッピングのレイヤーが加わります。特に次のケースで影響が出やすくなります。
- メモリ管理: コンテナのmemory limitとGPUメモリの管理が別系統になる
- I/O: モデルファイルの読み込みで、ボリュームマウント経由のレイテンシが加わる
- マルチGPU: 複数GPUの割り当てとスケジューリングが複雑化する
ベアメタル・Docker・VM の構造比較
3つの実行環境がどのようにGPUへアクセスするのか、構造的な違いを整理します。
ベアメタル(物理サーバーでの直接実行)
┌─────────────────────┐ │ アプリケーション │ │ (CUDA / PyTorch) │ ├─────────────────────┤ │ CUDA Toolkit │ ├─────────────────────┤ │ NVIDIA ドライバ │ ├─────────────────────┤ │ OS (Linux) │ ├─────────────────────┤ │ GPU ハードウェア │ └─────────────────────┘
特徴:
- GPUへのアクセスが最短経路で、追加のオーバーヘッドが発生しない
- CUDAとドライバの組み合わせをホストで一度設定すれば安定して稼働する
- 環境の再現性は構成管理ツール(Ansible等)で担保する
Docker(コンテナ実行)
┌─────────────────────┐ │ アプリケーション │ │ (CUDA / PyTorch) │ ├─────────────────────┤ │ CUDA Toolkit │ ← コンテナ内 │ (コンテナイメージ) │ ├─────────────────────┤ │ nvidia-container- │ ← 橋渡しレイヤー │ toolkit │ ├─────────────────────┤ │ Docker Engine │ ├─────────────────────┤ │ NVIDIA ドライバ │ ← ホスト側に依存 ├─────────────────────┤ │ OS (Linux) │ ├─────────────────────┤ │ GPU ハードウェア │ └─────────────────────┘
特徴:
- nvidia-container-toolkitという追加レイヤーが必須となる
- コンテナ内のCUDAとホストのドライバの互換性を継続的に管理する必要がある
- 複数のCUDAバージョンを共存させたい場合には有効
VM(仮想マシン)
┌─────────────────────┐ │ アプリケーション │ │ (CUDA / PyTorch) │ ├─────────────────────┤ │ CUDA Toolkit │ ├─────────────────────┤ │ NVIDIA ドライバ │ ← ゲスト OS 用ドライバ ├─────────────────────┤ │ ゲスト OS │ ├─────────────────────┤ │ ハイパーバイザー │ ← 仮想化レイヤー │ (GPU パススルー │ │ / vGPU) │ ├─────────────────────┤ │ ホスト OS │ ├─────────────────────┤ │ GPU ハードウェア │ └─────────────────────┘
特徴:
- GPUパススルー(SR-IOV / vGPU)の設定が複雑になる
- ハイパーバイザーによるオーバーヘッドが最も大きい
- マルチテナントでGPUを分割共有する場合に採用される(クラウド事業者向け)
ユースケース別の最適解
単一 GPU × 1台のマシン → ベアメタルを推奨
小型PCやワークステーション1台でLLM推論を動かすケースでは、Dockerによる抽象化の恩恵がほとんどありません。
| 観点 | ベアメタル | Docker |
|---|---|---|
| GPU 性能 | 100%(オーバーヘッドなし) | 95〜99%(デバイスマッピング経由) |
| セットアップ | CUDA インストールのみ | CUDA + Docker + nvidia-container-toolkit |
| トラブル時 | 原因の特定箇所が1つ | ホスト/コンテナの切り分けが必要 |
| 運用コスト | OS のアップデートのみ | Docker + toolkit のバージョン管理も必要 |
たとえばNVIDIA GB10搭載機や小型ワークステーションにOllamaを導入して社内AIを稼働させる構成では、ベアメタルなら curl -fsSL https://ollama.com/install.sh | sh の一行でCUDAが自動検出されます。Docker経由ではnvidia-container-toolkitのセットアップから始める必要があり、得られる利点に対して手間が見合いません。
複数バージョンの CUDA を使い分けたい → Docker が有効
研究開発でPyTorchの異なるバージョン(CUDA 11.8 / 12.1 / 12.4 など)を切り替えたい場合は、Dockerに明確な価値があります。
# CUDA 11.8 環境 docker run --gpus all pytorch/pytorch:2.0.1-cuda11.8-cudnn8-runtime # CUDA 12.4 環境 docker run --gpus all pytorch/pytorch:2.5.0-cuda12.4-cudnn9-runtime
ただしこれは開発・検証環境に限った話であり、本番の推論サーバーでは単一のCUDAバージョンに固定するのが一般的です。
複数ノードでスケールアウト → Kubernetes(K8s)
GPUサーバーが複数台あり、ワークロードを動的に振り分けたい場合は、Kubernetes + NVIDIA GPU Operator が事実上の標準です。
# GPU リソースのリクエスト例 resources: limits: nvidia.com/gpu: 1
ただし、K8sの運用には相応のインフラ運用体制が前提となります。
| 規模 | 最適解 | 必要なスキル |
|---|---|---|
| GPU 1台 | ベアメタル | Linux 基礎 |
| GPU 数台(同一用途) | ベアメタル + Ansible | Linux + 構成管理 |
| GPU 数台〜数十台(混在ワークロード) | K8s + GPU Operator | K8s + MLOps |
| 大規模クラスタ(100台以上) | Slurm / Ray | HPC 専門知識 |
ベアメタルでの構築手順: Ollama 編
小型PC × 単一GPUでLLMを稼働させる、最もシンプルな構成を示します。
1. NVIDIA ドライバのインストール
# Ubuntu の場合 sudo apt update sudo apt install -y nvidia-driver-560 sudo reboot # 確認 nvidia-smi
2. Ollama のインストール
curl -fsSL https://ollama.com/install.sh | sh
OllamaはCUDAを自動検出するため、追加の設定は不要です。
3. モデルの取得と起動
# 7B クラスのモデル ollama pull llama3.1 # 70B クラスのモデル(大容量メモリ環境向け) ollama pull llama3.1:70b # API サーバーとして起動(デフォルト: localhost:11434) ollama serve
4. LAN 内からのアクセス設定
# 外部からのアクセスを許可 OLLAMA_HOST=0.0.0.0 ollama serve
これだけで社内ネットワークからAI推論APIへアクセスできます。Docker構成では docker-compose.yml の作成、ボリュームマウント、ポートマッピング、nvidia-runtimeの指定と、必要な手順が大幅に増えます。
ベアメタルでの構築手順: vLLM 編(uv 使用)
Ollamaはシンプルさが強みですが、高スループットの推論サーバーが必要な場合はvLLMが有力な選択肢になります。OpenAI互換のAPIサーバーを高い並列性能で動かせる点が特徴です。
ここではPythonのパッケージマネージャ uv を用いたセットアップ手順を示します。pip + venv よりも高速で、依存関係の解決も安定しています。
1. uv のインストール
curl -LsSf https://astral.sh/uv/install.sh | sh
2. vLLM プロジェクトの作成
# プロジェクトディレクトリを作成 mkdir vllm-server && cd vllm-server # Python 3.11 で仮想環境を作成(vLLM の推奨バージョン) uv venv --python 3.11 source .venv/bin/activate # vLLM をインストール(CUDA 対応版が自動選択される) uv pip install vllm
uvはpipと比べて大幅に高速にパッケージを解決・インストールします。vLLMのように依存関係が多いパッケージで特に効果が顕著です。
3. モデルのダウンロードと起動
# OpenAI 互換 API サーバーとして起動 vllm serve meta-llama/Llama-3.1-8B-Instruct \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 4096
Hugging Faceからモデルが自動ダウンロードされ、CUDAを検出してGPU上で推論が開始されます。
4. API の動作確認
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "meta-llama/Llama-3.1-8B-Instruct", "messages": [{"role": "user", "content": "こんにちは"}], "max_tokens": 256 }'
OpenAI SDKからそのまま接続できるため、既存アプリケーションの接続先を変更するだけでローカルLLMへ切り替えられます。
Ollama と vLLM の使い分け
| 観点 | Ollama | vLLM |
|---|---|---|
| セットアップ | ワンライナー | uv + 数コマンド |
| 対応モデル | Ollama ライブラリから選択 | Hugging Face 上の幅広いモデル |
| API 互換性 | 独自 + OpenAI 互換 | OpenAI 完全互換 |
| スループット | 単一リクエスト向き | 高並列処理(Continuous Batching) |
| メモリ効率 | 標準 | PagedAttention で最適化 |
| 適性 | 個人〜小規模チーム | 高負荷の本番推論サーバー |
小規模かつ手軽に始める場合はOllama、高スループットが求められる本番環境ではvLLMという使い分けが現実的です。いずれもベアメタルで動かすことで、Docker特有のオーバーヘッドとトラブルを回避できます。
まとめ
| 判断基準 | ベアメタル | Docker | K8s |
|---|---|---|---|
| GPU 1台で推論 | ◎ | △ | ✕(過剰) |
| CUDA バージョン切替 | △ | ◎ | ◎ |
| 本番推論サーバー | ◎ | ○ | ○ |
| マルチノード管理 | △ | ○ | ◎ |
| 非エンジニアの運用 | ◎ | ✕ | ✕ |
| トラブル時の切り分け | 容易 | 中程度 | 複雑 |
GPU環境において「とりあえずDocker」は必ずしも最適解ではありません。単一マシンでLLMを動かすのであれば、ベアメタルが最もシンプルで、パフォーマンス面でも有利です。Dockerが本当に必要になるのは、複数のCUDAバージョンを切り替える開発環境か、K8sでオーケストレーションするマルチノード構成が中心です。自社のGPU環境が1台で完結するのであれば、余計な抽象化レイヤーを挟まずベアメタルで動かす構成を推奨します。
