WSL2でNvidia-Docker使えなかったから、そのままGPU環境作ってみた

# はじめに

**[2021/05/01現在]**

以前まではWSL2上のNvidia-DockerでGPU認識していたのですが、いつの間にか認識しなくなっていたので環境を作り直しました。
新しい環境をつくってみたもののNvidia-Dockerのサンプルがエラーで動かなかったため、WSL2上にGPUが使える環境を作りました。

この記事では、自分が試したことを備忘録的に以下の流れで記載しています
1. [NVIDIAの公式手順](https://docs.nvidia.com/cuda/wsl-user-guide/index.html)を試した
2. Nvidia-Dockerのサンプルが動かない
3. 色々調べてみた
4. Nvidia-Dockerを使わずにWSL2上にGPU環境を構築した



# 環境
- Windows10 Home Insider Preview build:21370.1
    - Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz   3.60 GHz
    - RAM: 32GB
    - RTX2080Ti
- 一応Windows側のCuda周り
  - CUDA 10.1
  - CuDNN 7.6.5


# CUDA on WSLに沿って環境構築してみる
[NVIDIAの公式手順](https://docs.nvidia.com/cuda/wsl-user-guide/index.html)を初めからなぞってみました。
WSL2はUbuntu20.04がインストール済みです。

## 1. CUDA Driveのインストール
[ここ](https://developer.nvidia.com/cuda/wsl)からCUDA on WSL2用のCUDA Driverをダウンロードして、インストール

## 2. WSL2上でCUDA Toolkitをセッティング（sudoが必要かもしれません）
   ```
    $ apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub

    $ sh -c 'echo "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 /" > /etc/apt/sources.list.d/cuda.list'

    $ apt-get update
    
    $ apt-get install -y cuda-toolkit-11-0
   ```
## 3. Dockerのセットアップ
   ```
    $ curl https://get.docker.com | sh

    $ distribution=$(. /etc/os-release;echo $ID$VERSION_ID)

    $ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -

    $ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

    $ curl -s -L https://nvidia.github.io/libnvidia-container/experimental/$distribution/libnvidia-container-experimental.list | sudo tee /etc/apt/sources.list.d/libnvidia-container-experimental.list

    $ sudo apt-get update

    $ sudo apt-get install -y nvidia-docker2
   ```

# Nvidia-Dockerのサンプルを試してみる
公式に記載のあるサンプルを動かしてみる

```
$ sudo docker run --gpus all nvcr.io/nvidia/k8s/cuda-sample:nbody nbody -gpu -benchmark
Unable to find image 'nvcr.io/nvidia/k8s/cuda-sample:nbody' locally
nbody: Pulling from nvidia/k8s/cuda-sample
d519e2592276: Pull complete
d22d2dfcfa9c: Pull complete
b3afe92c540b: Pull complete
b25f8d7adb24: Pull complete
ddb025f124b9: Pull complete
fe72fda9c19e: Pull complete
c6a265e4ffa3: Pull complete
c931a9542ebf: Pull complete
f7eb321dd245: Pull complete
d67fd954fbd5: Pull complete
Digest: sha256:a2117f5b8eb3012076448968fd1790c6b63975c6b094a8bd51411dee0c08440d
Status: Downloaded newer image for nvcr.io/nvidia/k8s/cuda-sample:nbody
docker: Error response from daemon: OCI runtime create failed: container_linux.go:367: starting container process caused: process_linux.go:495: container init
caused: Running hook #0:: error running hook: exit status 1, stdout: , stderr: nvidia-container-cli: initialization error: driver error: failed to process requ
est: unknown.
ERRO[0012] error waiting for container: context canceled

```
nvidia-container-cliとdriverでエラーが出てるっぽい...


# 原因調査
調べてみると、同じような現象が発生している模様。

- https://github.com/NVIDIA/nvidia-docker/issues/1203
- https://forums.developer.nvidia.com/t/stderr-nvidia-container-cli-initialization-error-driver-error-failed-to-process-request-n-unknown/128871/4
- https://github.com/NVIDIA/nvidia-docker/issues/1409

他にも、WSL2自体を初期化してcuda-toolkit-11-0をcuda-toolkit-10-2などにしても動かない...

WSL2のバージョンアップなども試しましたが、結局動かないので諦めました。

# Nvidia-Dockerを諦めて、WSL2上でGPUを使えるようにする

使いたいTensorflowのバージョンの兼ね合いから、CUDA10.1、CuDNN 7.6.5で環境を構築しました。
Python環境にはpipenvを利用します。

## 1. WSL2の初期化
Windowsの「設定」「アプリと機能」からUbuntuをアンインストールし、「Microsoft Store」から再インストール

## 2. WSL2上でCUDA Toolkitをセッティング

   ```
    $ sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub

    $ sudo sh -c 'echo "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 /" > /etc/apt/sources.list.d/cuda.list'

    $ sudo apt-get update

    $ sudo apt-get install -y cuda-toolkit-10-1
   ```

## 3. libcublas.so.10にパスを通す
    
cuda-10.1にはlibcublas.so.10は存在しないので、cuda-10.2の方にパスを通しました。

.bashrcに以下を追記してパスを通す。

```.bashrc
export LD_LIBRARY_PATH=/usr/local/cuda-10.2/lib64:${LD_LIBRARY_PATH}
```
追記した後に以下コマンドを実行して反映する。

```
$ source ~/.bashrc
```


## 4. cuDNNのインストール
   [cuDNN Download](https://developer.nvidia.com/rdp/cudnn-download)から自分のCUDAのバージョンに対応したものをダウンロード・インストールします。

   ```
    $ wget ${ダウンロードURL}
    $ sudo dpkg -i ${ダウンロードしたファイル名}
   ```


## 5. Python環境の構築
pipenvのセッティング

   ```
    $ sudo apt update && sudo apt install -y --no-install-recommends \
        build-essential \
        libffi-dev \
        libssl-dev \
        zlib1g-dev \
        libbz2-dev \
        libreadline-dev \
        libsqlite3-dev \
        git
    
    $ git clone https://github.com/pyenv/pyenv.git ~/.pyenv

    $ touch ~/.bash_profile
    $ echo -e "# pyenv paths" >> ~/.bash_profile
    $ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
    $ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
    $ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
    $ source ~/.bash_profile
    $ pyenv -v

    $ pyenv install 3.7.4
    $ pyenv global 3.7.4

    $ pip install pipenv
   ```

## 6. 試してみる
お試し環境を作成

   ```
   $ mkdir {お試し用ディレクトリ}
   $ cd {お試し用ディレクトリ}
   $ pipenv install tensorflow==2.3
   ```

TensorflowでGPUが認識しているか確認する

```
$ pipenv shell
$ python
>>> from tensorflow.python.client import device_lib
2021-05-01 15:00:32.024423: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
>>> device_lib.list_local_devices()
...｛出力が多いので割愛｝...
[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 155588733005698279
, name: "/device:XLA_CPU:0"
device_type: "XLA_CPU"
memory_limit: 17179869184
locality {
}
incarnation: 11516640570978184874
physical_device_desc: "device: XLA_CPU device"
, name: "/device:XLA_GPU:0"
device_type: "XLA_GPU"
memory_limit: 17179869184
locality {
}
incarnation: 3037529886568359493
physical_device_desc: "device: XLA_GPU device"
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 9849037120
locality {
bus_id: 1
links {
}
}
incarnation: 2702533884710410519
physical_device_desc: "device: 0, name: NVIDIA GeForce RTX 2080 Ti, pci bus id: 0000:01:00.0, compute capability: 7.5"
]

```

これで一応Tensorflow側からGPUが認識できていることを確認することができました。

# おわりに
今回は、Nvidia-Dockerを使った環境構築を諦め、WSL2上にそのままGPU環境を作りました。

本当はちゃんとDockerを使って色々試したかったのですが、何か解決策が見つかるまでこれで我慢しようと思います。