WSL2+Docker+VSCodeのGPU使用可能な環境構築

## はじめに
　こんにちは。まっちゃ。:tea:です。
　今回は、開発を進めていくうえで欠かせない**環境構築**について、まとめていきます。環境として、WSL2+Docker+VSCodeを導入し、GPUが使用可能であるコンテナを構築していきます。

## 環境
　表に、今回導入を行ったPCの環境を記載します。
|No. |項目 |詳細 | 
| -: | --- | --- |
| 1 | OS|Windows 11 Home|
| 2 | CPU |Intel(R) Core(TM) i7-13700HX|
| 3 | GPU |NVIDIA GeForce RTX 4050 Laptop|

## 目次
- WSL2の準備
- Dockerの導入
- GPUの使用設定
- Visual Studio Code (VSCode)のインストール
- コンテナ起動の準備
- VSCodeにおけるWSL2への接続とコンテナの起動

## WSL2の準備
- 概要
　Windows Subsystem for Linux 2の略称で、Windows上でLinux環境を動かすための機能です。

</br>

- 導入
　以下、コマンド内のC:\Users\user>におけるuserは、環境によって読み換えてください。

① ディストリビューション一覧を確認
　PowerShellを管理者で実行し、以下のコマンドを実行してインストール可能なディストリビューション一覧を取得します。
```
C:\Users\user> wsl --list --online
```

② ディストリビューションを明示し、インストール
　本記事では、Ubuntu-22.04をインストールします。以下のコマンドを実行してインストールを開始します。
　インストール完了まで時間が掛かります。自分の環境の場合は、大体25分でインストールが完了しました。20分経過しても進行率が0.0%のままでしたが、ウインドウの最小化と最大化を繰り返したところ、進行率が更新してインストールが完了しました。
```
C:\Users\user> wsl --install -d Ubuntu-22.04
```

③ Ubuntuの初期設定
　PowerShellを管理者で実行し、以下のコマンドを実行してWSL2を起動します。初回起動時にユーザー名とパスワードを設定します。
 
:::note warn
　ユーザー名とパスワードは、sudoコマンド使用時に必要となります。パスワードは入力時に表示されないので、入力間違いに注意してください。
:::

```
C:\Users\user> wsl
```

④ 既定のディストリビューションを変更
　以下のコマンドを実行し、起動時のディストリビューションを変更します。
```
C:\Users\user> wsl -s Ubuntu-22.04
```

　変更後、以下のコマンドを実行してリストを表示し、ディストリビューションが変更されていることを確認します。Ubuntu-22.04の横に*がついていれば、ここまでのセットアップは問題なく完了しています。
```
C:\Users\user> wsl -l -v
```

## Dockerの導入
- 概要
　Dockerは、コンテナ型仮想環境の作成、配布、実行を行うプラットフォームです。コンテナを共有することで、同一の環境を容易に構築することができます。
　詳細については、以下のドキュメントを参照ください。
 
https://docs.docker.jp/get-started/overview.html

</br>

- 導入
　以下のマニュアルを参考に、WSL2上にDocker Engineをインストールしていきます。
 
https://matsuand.github.io/docs.docker.jp.onthefly/engine/install/ubuntu/

① WSL2の起動
　PowerShellを管理者で実行し、以下のコマンドを実行してWSL2を起動します。
```
C:\Users\user> wsl
```

② Docker Engineのインストール
　以下のコマンドを実行し、Docker Engineをインストールします。

:::note warn
　sudoコマンドの初回使用時には、「Ubuntuの初期設定」で設定したユーザー名とパスワードを入力する必要があります。
:::

```
# aptパッケージの更新
$ sudo apt-get update
$ sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

# DockerのGPG鍵を追加
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# リポジトリをセットアップ
$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Docker Engineをインストール
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
```

　Docker Engineインストール後、rootユーザー以外でDockerを管理出来るよう、以下のコマンドを実行します。
```
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
```

## GPUの使用設定
- 導入
　今回は、Dockerで構築したコンテナの内部においてGPUを使用するために、表に示す2つを導入します。

|No. |対象 | 
| -: | --- |
| 1 | NVIDIAドライバー|
| 2 | NVIDIA Container Toolkit |

　WSL2上でGPUを使用する場合には、NVIDIAドライバーの他にcuda, cuDNNが別途必要になります。cuda, cuDNNのインストール方法は、下記を参考にしてください。cuda, cuDNN, Pytorch (Tensorflow)のバージョンを合わせる必要があり、合っていない場合はエラーが発生します。

https://qiita.com/pocokhc/items/0a759130941090b1d231

① NVIDIAドライバーのインストール
　図に、以下のページに表示されるドライバーの手動検索画面を示します。手動検索において、自分の環境に合わせた選択を行い、ドライバーをインストールします。OSは、ローカルPCの環境を選択してください。ここでは、Windows用のドライバーを選択します。

https://www.nvidia.co.jp/Download/index.aspx?lang=jp

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3673789/c15293ce-8536-04d4-c49d-d4a22b3d0c40.png" width=50%>

　インストール後、下記のコマンドを実行してGPUが認識されていることを確認してください。
```
$ nvidia-smi
```
② NVIDIA Container Toolkitの導入
　以下のマニュアルを参考に、NVIDIA Container Toolkitを導入していきます。

https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html

　以下のコマンドを実行して、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/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 

# aptパッケージの更新
$ sudo apt update

# NVIDIA Container Toolkitをインストール
$ sudo apt install -y nvidia-container-toolkit

# Dockerデーモンを再起動
$ sudo systemctl restart docker
```

## Visual Studio Code (VSCode)のインストール
- 概要
　Microsoftが提供するコードエディタです。使いやすさや様々な拡張機能といった特徴から定番のエディタとして使用されています。

</br>

- 導入

① インストーラをダウンロード
　以下のページからOSに合わせたVSCodeのインストーラをダウンロードし、実行して下さい。

https://code.visualstudio.com/download

② 拡張機能を追加
　VSCodeのインストールが完了したら、次に開発環境に必要な拡張機能を追加していきます。拡張機能の追加により、自分が使いやすいエディタにすることができます。
　まず、VSCodeを起動します。図に示すアクティビティバーが、左側に表示されていることを確認して、図中の赤枠のアイコンを選択します。(図はすでにRemote DevelopmentとDockerの拡張機能を追加した状態です。)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3673789/5a6f6664-ecc4-1e2c-a0b5-2a61d03061ef.png" width=8%>

　その後、表に示す拡張機能をそれぞれ追加していきます。
 
:::note warn
　No.3の「Docker」はWSL2上で追加します。そのため、WSL2にリモート接続した後で追加を行ってください。リモート接続の方法については、「VSCodeにおけるWSL2への接続とコンテナの起動」の項目に記述しています。
:::

|No. |拡張機能名 |内容 | 追加先
| ---: | -------- | --- | --- |
| 1| WSL | WSLへの接続 |ローカルPC上 |
| 2| Remote Development | リモート接続 | ローカルPC上 |
| 3| Docker | コンテナの構築 |WSL2上|
| 4| Japanese Language Pack for Visual Studio Code | 日本語化 |どちらでも |

## コンテナ起動の準備
- 概要
　Dockerfileを用いることで、DockerHubから取得したイメージをカスタマイズしてコンテナを構築することができます。
　また、下記のファイルを共有することにより、同一のコンテナを簡単に構築することができます。

</br>

- 導入
　以下の記事を参考に、コンテナを構築するためのファイルを準備していきます。

https://qiita.com/KiYuRo/items/c016eaf5066c6cedaa11

　VSCodeにてDockerfileを用いたコンテナを構築する場合、表に示すファイルが必要となります。ここで、「docker-compose.yml」はDocker Composeの設定ファイルであり、「devcontainer.json」は構築するコンテナ内におけるVSCodeの設定ファイルとなります。
　コンテナを構築するだけの場合、上記2つのファイルは不要です。今回は、ファイルを使用して、構築するコンテナの詳細な設定を行います。

 :::note warn
　下記で示す各ファイルは一例であり、自分が構築したい環境によって、記述方法が異なります。その際は、ほかの方の記事を参考にして、適宜変更してください。
:::

|No. |ファイル名 |内容 |
| ---: | -------- | --- |
| 1| Dockerfile | コンテナの構築 |
| 2| docker-compose.yml | コンテナの設定 |
| 3| devcontainer.json | VSCodeの設定 |

　以下に示す各ファイルは、次に示す階層における記述方法です。ここで、「requirements.txt」には、インストールするモジュール名を記述します。また、「.env」には使用する環境変数名を記述します。

```
├ .devcontainer/
│   ├ docker-compose.yml
│   └ devcontainer.json
│
├ Dockerfile
│
├ requirements.txt
│
├.env
│
└ app/
```

① Dockerfile

```
# DockerHubのベースとなるイメージを指定
FROM nvidia/cuda:11.8.0-devel-ubuntu22.04

# 作業ディレクトリを変更
WORKDIR /app

# aptパッケージの更新
RUN apt-get update -y && apt-get install -y \
        curl \
        git \
        python3　\
        python3-pip \
        wget\
    && apt-get autoremove -y \
    && apt-get clean \ 
    && rm -rf /var/lib/apt/lists/*

# ローカルPCのファイルをコンテナのカレントディレクトリにコピー
COPY ./requirements.txt ./

# pipのアップデート
RUN pip install --no-cache-dir --upgrade  pip

# pytorch(gpu用)をインストール
RUN  pip --no-cache-dir install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# pythonパッケージをインストール
RUN pip install -r requirements.txt
```

② docker-compose.yml
```
version: '3'
services:
  python:                           # pythonという名前でサービスを定義
    build:                          
      context: ..                   # Dockerfileを相対パスで指定
      dockerfile: Dockerfile
    env_file:                       # 環境変数の割り当て
      - ../.env
    shm_size: "8gb"                 # 割り当てるメモリ量を記述　※不足している場合はモデルの学習中にエラーが発生
    ulimits:                        # 以下は、コンテナにおいてGPUを使うための設定
        memlock: -1
        stack: -1
    deploy:
        resources:
          reservations:
            devices:
              - driver: nvidia
                capabilities: [ gpu ]
    tty: true                       # コンテナを継続して起動
    volumes:
      - ../app:/app                 # コンテナ停止後もデータをローカルPCに残すようにマウント　※記述方法は、「ローカルPCの相対パス:コンテナ内の作業ディレクトリ」
```

③ devcontainer.json
　コンテナ構築時に拡張機能を導入する際は、各機能の識別子をファイルに記載します。例として、図に、VSCodeにおけるPythonの拡張機能画面を示します。識別子は、図中の赤枠で囲った箇所を参照します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3673789/b7136005-7564-dd22-7223-95111677c023.png" width=85%>

```
{
    "name": "Test",                             // VSCodeがコンテナに接続したときのタイトルを自由に設定
    "dockerComposeFile": "docker-compose.yml",  // docker-conpose.ymlファイルを指定
    "service": "python",                        // docker-conpose.ymlファイル内に記載したサービス名を指定
    "workspaceFolder": "/app",                  // VSCode接続時に開くフォルダを指定
    "customizations": {
        "VSCode": {
            "extensions": [                     // コンテナ構築時にインストールする拡張機能を記載　
                "ms-python.python",             // python拡張機能
                "ms-toolsai.jupyter",           // jupyter拡張機能
                "ms-python.VSCode-pylance"      // 入力補助拡張機能  
            ],
            "settings": {                       // VSCodeの設定を記載
                "files.autoSave": "afterDelay"  // ファイルのオートセーブ
            }
        }
    }
}
```

④ requirements.txt
```
jupyter
```

## VSCodeにおけるWSL2への接続とコンテナの起動
WSL2への接続
　図に、WSL2へ接続するための方法を示します。
　① VSCodeを立ち上げ、左下の青いマークを選択
　② 上に表示されるオプションにて、「WSL への接続」を選択

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3673789/87d51c88-709c-9009-b3b9-abdeea4b3bf1.png" width=85%>

　VSCodeの画面が切り替わり、図に示すように、左下の表記が「WSL: Ubuntu-22.04」となれば、接続は完了です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3673789/e03dddb8-44a2-c72c-1056-e256c9f3abfa.png" width=25%>

<br>
<br>

コンテナの起動
　図に、コンテナを起動するための方法を示します。
　① アクティビティバーからDocker拡張機能を選択
　② 「CONTAINERS」にカーソルをもっていき、「+」を押下
　③ 上に表示されるアクションの選択にて、「コンテナーでフォルダを開く...」を選択し、
　 「.devcontainer」があるフォルダを選択

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3673789/960c59a5-c208-5192-94e0-251517799814.png" width=95%>

　VSCodeの画面が切り替わり、コンテナの構築が始まります。問題がなければコンテナの構築完了後、「devcontainer.json」で記述したワークスペースが開かれた状態となります。
　構築中にエラーが発生した場合には、ログを確認して原因を調査していきます。イメージの構築後にエラーが発生した場合の多くは、イメージの重複が原因となります。その際は、上図の「IMAGES」に追加されたイメージを一度"Remove"で削除して、再度コンテナを構築してください。

<br>

GPU接続の確認
　コンテナ内のVSCodeにて、「ファイル」-「新しいファイル」-「Jupyter Notebook」を選択し、新規のNotebookを開きます。
　図に、Jupyter Notebookを新規で開いた際の画面を示します。開いたファイルにおいて、図中の赤枠で囲った「カーネルの選択」から、Pythonの環境フォルダを指定します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3673789/1bf9af97-67b2-e85b-9042-f82077d535c4.png" width=100%>

　その後、セルに下記コマンドを入力して実行し、"True"が返されたら、構築した環境においてGPUの使用が可能な状態となっています。
```
import torch

print(torch.cuda.is_available())
```

　以上で、WSL2+Docker+VSCodeの環境内でGPUが使用できる環境の構築は完了です。お疲れさまでした！🎉

## まとめ
　ここまでで、GPUの使用が可能な、WSL2+Docker+VSCodeの環境構築をまとめてきました。自分が環境構築を行う際、様々な記事を参考にしており、構築の最低限を1つのページにまとめておくことを目標としています。
　また、Dockerfileでコンテナを構築しエラーが際、エラーの解決に1週間近く格闘したことがあるので、一助になれば幸いです。
　他の環境構築については、ほかの方の記事にまとまっていますので、自分の構築したい開発環境にあわせて、適宜参考にしてください。