【Docker/環境】NVIDIA Container ToolkitインストールとGPUパススルー設定で「Could not load UVM kernel module」を解決

問題の概要:Dockerコンテナ内でGPUが認識されない

AI開発や機械学習の環境構築において、Dockerコンテナ内でNVIDIA GPUを利用しようとすると、以下のようなエラーに遭遇することがあります。特に、NVIDIA Container Toolkit(旧称:nvidia-docker2)のインストール後や、Dockerコンテナの起動時に発生します。

# 代表的なエラーメッセージ例
docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].
# コンテナ内で`nvidia-smi`を実行した場合のエラー
nvidia-container-cli: initialization error: driver error: failed to process request
# システムログ(`journalctl -xe`や`dmesg`)で確認できるカーネル関連のエラー
Could not load UVM kernel module. Failed to initialize NVML: Unknown Error

これらのエラーが発生すると、コンテナ内からGPUリソースを利用した計算(例:TensorFlow, PyTorchでの学習や推論)が実行できず、AI開発ワークフローが停止してしまいます。本記事では、この問題の原因と、NVIDIA Container Toolkitを正しくインストール・設定してDockerコンテナにGPUをパススルーする方法をステップバイステップで解説します。

原因の解説

DockerコンテナがホストマシンのGPUを認識できない主な原因は、以下の3つに大別されます。

1. NVIDIA Container Toolkitが未インストールまたは設定不備

標準のDockerエンジンは、GPUのような特殊なハードウェアリソースをコンテナに直接割り当てる機能を備えていません。NVIDIA Container Toolkitは、DockerランタイムとNVIDIA GPUドライバーを連携させるためのブリッジ役となるソフトウェア群です。これが正しくインストール・設定されていないと、GPUパススルーは機能しません。

2. ホスト側のNVIDIAドライバーまたはカーネルモジュールの問題

「Could not load UVM kernel module」というエラーは、ホストOS側のNVIDIAドライバーカーネルモジュール(特にUVM – Unified Video Memoryモジュール)が正しくロードされていないことを示しています。ドライバーのバージョン不整合、インストールの失敗、またはカーネルの更新後にモジュールが再ビルドされていない場合に発生します。

3. Dockerデーモンの再起動不足またはランタイム設定の不備

NVIDIA Container Toolkitをインストールした後、Dockerデーモンを再起動しないと、新しい設定が読み込まれません。また、Dockerのデフォルトランタイムが`nvidia`に正しく設定されていないことも原因となります。

解決方法:完全なインストールと設定手順

以下に、Ubuntu 20.04/22.04 LTSを例に、問題を解決するための完全な手順を示します。他のディストリビューションでも概念は同様です。

ステップ1: 前提条件の確認

まず、ホストマシンにNVIDIA GPUドライバーが正しくインストールされていることを確認します。

# NVIDIAドライバーとCUDAツールキットのバージョン確認
nvidia-smi

正常に表示されればOKです。表示されない場合は、まずホストOS向けのNVIDIAドライバーをインストールしてください。

ステップ2: NVIDIA Container Toolkitのインストール

公式リポジトリを追加し、必要なパッケージをインストールします。

# ディストリビューションとアーキテクチャを設定
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
echo "Distribution: $distribution"

# NVIDIA Container Toolkitのリポジトリを追加
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | 
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | 
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# パッケージリストを更新し、ツールキットをインストール
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

ステップ3: Dockerランタイムの設定

インストール後、`nvidia-container-runtime`をDockerの利用可能なランタイムとして設定します。

# nvidia-container-runtimeをDockerの設定に登録
sudo nvidia-ctk runtime configure --runtime=docker

# 設定ファイルの内容を確認(オプション)
sudo cat /etc/docker/daemon.json

このコマンドは、`/etc/docker/daemon.json` ファイルを編集または作成し、以下のような`runtimes`セクションを追加します。

{
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

ステップ4: Dockerデーモンの再起動と設定の適用

設定を有効化するために、Dockerサービスを再起動します。

sudo systemctl restart docker

ステップ5: GPUパススルーの動作確認

最も簡単なテストは、NVIDIAが提供するCUDAコンテナを実行し、`nvidia-smi`がコンテナ内で動作するか確認することです。

# テスト用コンテナの実行
sudo docker run --rm --runtime=nvidia --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi

あるいは、より新しいDockerバージョン(19.03以降)では`–gpus`オプションが利用できます。

sudo docker run --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi

ホストで実行した場合と同様のGPU情報がコンテナ内から表示されれば、設定は成功です。

ステップ6: 「Could not load UVM kernel module」エラーへの対処

上記手順後もこのエラーが発生する場合は、ホスト側のNVIDIAカーネルモジュールに問題がある可能性が高いです。以下の手順でトラブルシューティングします。

# 1. ホスト側でNVIDIAモジュールがロードされているか確認
lsmod | grep nvidia
# nvidia_uvm, nvidia_drm, nvidia_modeset, nvidia などのモジュールが表示されるはず

# 2. 表示されない場合、手動でロードを試みる
sudo modprobe nvidia-uvm

# 3. 永続化のために、モジュールを/etc/modulesに追加
echo "nvidia-uvm" | sudo tee -a /etc/modules

# 4. カーネルを更新した直後の場合、NVIDIAドライバーカーネルモジュールを再ビルド
sudo apt-get install --reinstall linux-headers-$(uname -r)
sudo apt-get install --reinstall nvidia-kernel-$(uname -r | sed 's/-/ /g' | awk '{print $1}') # ディストリビューションにより異なる
# または、NVIDIAドライバーインストーラーを再実行する

コード例・コマンド例:実践的なAI開発環境の構築

設定が完了したら、実際のAIフレームワークが動作するコンテナを起動してみましょう。

# PyTorch公式イメージを使用したインタラクティブな環境の起動
docker run -it --rm --gpus all -p 8888:8888 
  -v $(pwd)/workspace:/workspace 
  pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime 
  bash

# コンテナ内でPythonを起動し、GPUが認識されているか確認
python3 -c "import torch; print(f'PyTorch version: {torch.__version__}'); print(f'CUDA available: {torch.cuda.is_available()}'); print(f'GPU device count: {torch.cuda.device_count()}')"
# TensorFlow公式イメージを使用したJupyter Notebook環境の起動
docker run -it --rm --gpus all -p 8888:8888 
  -v $(pwd)/notebooks:/tf/notebooks 
  tensorflow/tensorflow:latest-gpu-jupyter

# 起動後、ブラウザで http://localhost:8888 にアクセス

まとめ・補足情報

NVIDIA Container Toolkitを用いたDockerコンテナへのGPUパススルー設定は、現代のAI開発における必須のスキルです。本記事で解説した手順に従うことで、ほとんどの環境で問題を解決できるはずです。設定の要点を以下にまとめます。

  • 依存関係の管理: ホストのNVIDIAドライバー、Dockerエンジン、NVIDIA Container Toolkitのバージョン互換性に注意してください。特にCUDAバージョンとフレームワークの要求バージョンを一致させることが重要です。
  • 「–runtime=nvidia」と「–gpus all」の違い: 古いガイドでは`–runtime=nvidia`オプションが使われていますが、Docker 19.03以降ではより直感的な`–gpus all`(または`–gpus 2`のように特定のGPU数を指定)の使用が推奨されます。内部的には同じ機能を呼び出します。
  • マルチGPU環境: 複数GPUを搭載するサーバーでは、`–gpus all`ですべてのGPUがコンテナに渡されます。特定のGPUのみを割り当たい場合は、`–gpus ‘”device=0,2″‘`のようにデバイスIDを指定できます。
  • 永続的な問題が解決しない場合: NVIDIA Container ToolkitのGitHub IssuesやNVIDIA Developer Forumを検索することをお勧めします。特定のディストリビューションやカーネルバージョンに固有の問題が報告されていることがあります。

この設定が完了すれば、再現性が高く、依存関係に悩まされない、ポータブルなGPU機械学習開発環境を手に入れたことになります。プロジェクトごとに異なるCUDAバージョンやライブラリバージョンが必要な場合も、Dockerイメージを切り替えるだけで対応可能となり、開発効率が大幅に向上します。

この記事は役に立ちましたか?