GPUを使える状態で作ったDockerイメージがいつの間にか動かなくなった

作成日: 2023年11月09日(木) 00:00
最終更新日: 2024年07月07日(日) 00:25
カテゴリ: その他
タグ:  その他 環境構築

こんにちは.高山です.
普段実験とかをするときは,Dockerという仮想環境を使うのですが,過去に作ったイメージがいつの間にか動かなくなってしまいました.
今後同じようなことが起きたときのためのメモとして,解決までの手順を書き残しておこうと思います.

1. 環境

  • ホストOS: Windows 11, version 22H2
  • Linux環境: Ubuntu20.04 on WSL2 (1.2.5.0), 5.15.90.1-microsoft-standard-WSL2
  • Docker: Docker Engine - Community, 24.0.7

2. 起きた現象

まずどのような現象が起こったかというと,下記のようにDocker上でGPUを使える設定 (--runtime=nvidia としていますが,これは古いやり方で新しい設定方法は --gpus [GPU番号] になります) で起動しようとしたところエラーになってしまいました.

$ docker run --runtime=nvidia -it --rm image_name bash -l
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error running hook #1: error running hook: exit status 1, stdout: , stderr: Auto-detected mode as 'legacy'
nvidia-container-cli: mount error: file creation failed: /var/lib/docker/overlay2/9b712f84b91def97f8eaafeccbf39dae84a94b6756172e5f1cc467db8f5bb9f2/merged/usr/lib/x86_64-linux-gnu/libcudadebugger.so.1: file exists: unknown.

また,下記のようにGPUを使わない設定 (--runtime=runc) では正常起動します.

$ docker run --runtime=runc -it --rm image_name bash -l
-> No error.

3. 現象の解析・調査

類似のエラー例を調べていくと,どうやらこの現象はGPUを使用した状態でDockerのイメージを作成すると発生するようです.

典型的な状況としては,docker build の処理内でライブラリをソースコードから作成する (要GPU) ケースなどが当てはまります.
よく知られている方法は,公式ドキュメントの第4節に書いてあるように,Dockerのデフォルトランタイムを nvidia とする方法です.
確かに,高山は過去にこの設定を行ったことがありそのままの設定で利用していました.
(なお,今は利用するライブラリが変わったのでDocker内でビルドすることはしていません.)

どうやらこのビルド方法は (WSL2環境だけかもしれません) 副作用があるらしく,本来は存在しないはずのライブラリ (リンク先では "ghost libraries" と呼んでいます) がイメージ内に発生し,それがエラーの原因となっているようです.

具体的には,下記のようなライブラリが生成されます.

$ docker run --runtime=runc -it --rm image_name bash -l
$ ls /usr/lib/x86_64-linux-gnu/ | grep nvidia
libnvidia-encode.so.1
libnvidia-ml.so.1
libnvidia-nvvm.so.4
libnvidia-nvvm.so.520.61.05
libnvidia-opticalflow.so
libnvidia-opticalflow.so.1
libnvidia-ptxjitcompiler.so.1
libnvidia-ptxjitcompiler.so.520.61.05
$ ls /usr/lib/x86_64-linux-gnu/ | grep cuda
libcuda.so
libcuda.so.1
libcuda.so.520.61.05
libcudadebugger.so.1
libcudadebugger.so.520.61.05

上記のライブラリは --runtime=nvidia 指定時にDockerが動的に配置するライブラリと同じらしく,これらがイメージ内に既に存在していることでエラーとなっているようです.
なお,各ライブラリのバージョン番号は環境依存なので,ここでの話とは特に関係ありません.

4. 解決法

単純には,Dockerのデフォルトランタイムを runc として,イメージをビルドし直すとエラーは起きなくなります.
例えば,高山のケースでは公式ドキュメントの第4節の処理を取りやめ (/etc/docker/daemon.json を削除),Dockerを再起動してビルドし直すと,下記の通り正常起動するようになりました.

$ docker run --runtime=runc -it --rm image_name bash -l
$ ls /usr/lib/x86_64-linux-gnu/ | grep nvidia
-> No output.
$ ls /usr/lib/x86_64-linux-gnu/ | grep cuda
-> No output.
$ exit
$ docker run --runtime=nvidia -it --rm image_name bash -l
-> No error.

ただし,この解決法だと元々やりたかった"GPUを使用した状態でDockerのイメージを作成する"ができなくなってしまいます.

他の解決方法としては,

  1. CUDAインストール済みのイメージをベースとして,環境パスを通す (こちらの第2節参照)
  2. GPU依存のビルド処理はイメージ作成後のコンテナ内で行い (docker run で起動したコンテナで行う),docker commit でイメージを上書きする

などがあるようで,これらの方法ならば双方の問題解決が図れるかもしれません.
(高山は最初の方法で解決してしまったので試してません.すみません(^^;))


如何でしたでしょうか?
WSL2,Docker,GPUの組み合わせは色々とハマる場合が多いです.
同じような現象でお困りの方が,少しでも参考になれば幸いです.