【Docker + WSL2 + CUDA】docker composeで開発環境を構築

# はじめに
WSL2上でNVIDIA Container Toolkitをインストールし、Dockerコンテナ内でCUDAを使用する手順をまとめました。

# 手順

### 1\. WSL2のインストール

[WSLでLinux環境を構築](https://qiita.com/insilicomab/items/7cdd29196fb611ce2b88)

### 2\. NVIDIAドライバをインストール

[NVIDIA Driverをインストールしよう](https://qiita.com/insilicomab/items/5f1097e0b6d97b8ee28c#%E3%81%BE%E3%81%9A%E3%81%AFnvidia-driver%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%97%E3%82%88%E3%81%86)

### 3\. Docker Desktopのインストール

Docker Desktopをインストールし、設定でWSL2を有効化します。

- Docker Desktopを開き、設定 > Resources > WSL integration を選択します
- WSL2ディストリビューションを有効化します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/471633/06aa9e3c-f255-7996-6c7e-60cda713737f.png)

### 4\. NVIDIA Container Toolkitをインストール

[公式ドキュメント](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installing-with-apt)

以下はUbuntuの場合を記載します。

- WSL2上で以下のコマンドを実行して、リポジトリとGPGキーをセットアップします

```sh
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/stable/deb/nvidia-container-toolkit.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
```

- パッケージを更新します

```sh
sudo apt-get update
```

- NVIDIA Container Toolkit パッケージをインストールします

```sh
sudo apt-get install -y nvidia-container-toolkit
```

- 以下のコマンドを使用してコンテナーランタイムを構成します

```sh
sudo nvidia-ctk runtime configure --runtime=docker
```

- 以下のコマンドを実行するか、Docker Desktopで「Restart」でDocker デーモンを再起動します

```sh
sudo systemctl restart docker
```

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/471633/5f3697bf-9b7a-dfae-2203-ad9fff6b2790.png">

### 5\. サンプル CUDA コンテナを実行し、GPUが認識されるか確認

以下のコマンドを実行します。

`sudo docker run --rm --gpus all nvidia/cuda:<TAG> nvidia-smi`

`<TAG>`は以下から選択します。
https://hub.docker.com/r/nvidia/cuda/tags

例）
```sh
sudo docker run --rm --gpus all nvidia/cuda:12.3.2-base-ubuntu22.04 nvidia-smi
```

成功した場合、出力は以下のようになります。

```sh
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 535.86.10    Driver Version: 535.86.10    CUDA Version: 12.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla T4            On   | 00000000:00:1E.0 Off |                    0 |
| N/A   34C    P8     9W /  70W |      0MiB / 15109MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
```

# Docker ComposeでGPUアクセスを有効化

[公式ドキュメント](https://docs.docker.jp/compose/gpu-support.html#gpu)

以下のような**compose.yaml**ファイルを作成し、`docker compose up`を実行します。
成功した場合、上記の`nvidia-smi`コマンドの出力結果が表示されます。

```compose.yaml
services:
   test:
     image: nvidia/cuda:12.3.2-base-ubuntu22.04
     command: nvidia-smi
     deploy:
       resources:
         reservations:
           devices:
             - driver: nvidia
               count: 1
               capabilities: [gpu]
```

- `capabilities`：`capabilities: [gpu]`の用に文字列のリストとして値を指定します
- `count`：予約した GPU デバイス（ホストが保持している GPU 数）を整数値として指定するか `all` で指定します
- `device_ids`：ホスト上で予約する GPU デバイスの ID を表す文字列を値として指定します。デバイス ID はホスト上で `nvidia-smi `の出力で確認できます。例、`['0', '3']`
- `driver`：文字列として指定します。例、`driver: 'nvidia'`
- `options`：ドライバ固有のオプションを表す キーバリューの組み合わせ