WSL2 + Dockerに入門してJupterLabの開発環境を作る

# 誰向けの記事？
- WSL2 + DockerをでJupterLabを用いたPythonの開発環境を作りたい。
- WSL2って何？Dockerって何？状態なのでそこら辺の入門をしたい。
- WSL2 + DockerからGPUを使った開発環境(PyTorch)を作りたい。

筆者自身が全てに当てはまっていたので、やったことのメモ的な側面もあります。

# WSL2 + Dockerの環境を作る

## `INTEL VIRTUALIZATION TECHNOLOGY`を有効化する
BIOSを立ち上げて、`INTEL VIRTULIZATION TECHNOLOGY`を有効化します。\
これをサボったことにより後でエラーが出て調べる羽目になりました。必ず確認しておくと良いでしょう！


## WSL2インストール
### 1. インストール
　PowerShellで以下のコマンドを打つだけ。簡単。
```sh:PowerShell
wsl --install
```
    
「**Windowsの機能の有効化または無効化**」の設定が必要。自動で設定されるらしいという情報もあったが、結局手動で以下をチェックした。
- Linux用Windowsサブシステム（wsl2インストール前は無かった気がする）
- Windowsハイパーバイザープラットフォーム
- 仮想マシンプラットフォーム


### 2. 利用可能なディストリビューションの一覧の確認
```sh:PowerShell
wsl -l -o
```
　ubuntu 20.04を使うことにする。。

### 3. Ubuntu20.04で環境構築
```sh:PowerShell
wsl --install -d Ubuntu-20.04
```
　これで`Ubuntu`のターミナルがスタートメニューに追加される。
  Ubuntu(WSL)に入りたければ、スタートメニューから叩けば入れる。この時点でVirtual Boxより圧倒的に使いやすくて感動。

　とりあえずでaptのアップデートをしておく＠WSL2
```sh:WSL
sudo apt update
sudo apt upgrade
```

## Docker Desktopのインストール
[公式](https://www.docker.com/)からインストーラー入手してインストール

ライセンスが気になる場合は「Docker **Engine**」をWSL2に入れる。なお、この先の手順には互換性があるはず。


## 開発用コンテナのイメージをビルドする
### 1. `Dockerfile`の作成
　WSL2上の`/home/user/Dockerfile/Python-Base`に`Dockerfile`を作成する。（パスは好みでOK）
    
``` Dockerfile:Dockerfile
FROM python:3.9-slim-buster

RUN pip install --upgrade --no-cache-dir pip && \
  pip install --upgrade --no-cache-dir \
  jupyterlab

CMD ["jupyter", "lab", "--ip=0.0.0.0", "--allow-root", "--LabApp.token=''"]
```

これは
- Docker Hub上にある`python:3.9-slim-buster`というイメージをベースにして
- `pip install`で必要なモジュールをインストールして
- jupyter labを立ち上げる
   
ということが書かれている。環境の構築手順が明示されているので、誰かに環境を渡すときに便利。
    
    
    
### 2. コンテナをビルドする。
``` sh:WSL
cd /home/user/Dockerfile/Python-Base
docker image build --tag jupyter:test .
```
   
　dockerコマンドの意味は以下
|コマンド|意味|
|---|---|
|`docker image build`|イメージをビルドするよ|
|`--tag jupyter:test`|イメージ名 `jupyter`が名前, `test`がバージョン名　だよ|
|`.`|Dockerfileがあるディレクトリはここだよ|


## コンテナを起動し、ホストPCのブラウザからjupyterに接続する
　以下に留意してコンテナを起動する。
- jupyterはwebサーバーが立ち上がるので、ホストPCからコンテナ上のwebサーバーにアクセスする必要がある。
- 開発したソースコードはホストPCとコンテナ双方から見える位置に置いておきたい。

### 1. WSL2上に開発したソースコードを入れるディレクトリを作成する。
　`/home/usr/test/src`とする。
　ちなみに、WSL2上のディレクトリはWindows上の`\\wsl$`という場所に入っている。ネットワークドライブに割り当てておくと便利！

### 2. 以下のコマンドでコンテナを起動する。
```sh:WSL
docker container run \
--name jupyter \
--rm \
--detach \
--publish 18888:8888 \
--volume /home/usr/test/src:/mnt/host_src \
jupyter:test
```
|コマンド|意味|
|---|---|
|`docker container run`| コンテナを起動するよ|
|`--name jupyter`| コンテナの名前はjupyterだよ|
|`--rm`|stopと同時にコンテナは消去されるよ。|
|`--detach`| デタッチするよ<br>（じゃないと、WSLのターミナルが固まる）|
|`--publish 18888:8888`| ホスト側の18888ポートとコンテナ側の8888ポートを接続するよ。<br>（jupyterはデフォルトで8888ポートを使う）|
|`--volume /home/usr/test/src:/mnt/host_src`| ホスト側の`/home/usr/test/src`を<br>コンテナ側の`/mnt/host_src`にマウントするよ|
|`jupyter:test`|起動するイメージは`jupyter:test`だよ|

### 3. ホストPC（Windows）のブラウザでアクセスする。
　`localhost:18888`にアクセスすると、JupyterLabが開く。ディレクトリ構成をみるとコンテナの世界だということはわかるはず。

### 4. いったんコンテナを止める
```sh:WSL
docker container stop jupyter
```
　別にこのままでも良いのですが、docker-composeを使ってお手軽に実行したいので。

## イメージビルド～コンテナ起動をdocker-composeで簡略化する。
### 1. 以下のファイルを作る。Dockerfileと同じ場所でOK。
```yml:docker-compose.yml
version: '3.8'

services:
  jupyter:
     container_name: jupyter
     build:
        context: .
     ports:
        - '18888:8888'
     volumes:
        - type: bind
          source: /home/user/test/src
          target: /mnt/host_src
```

|キー|意味|
|---|---|
|`version`|[公式サイト](https://docs.docker.jp/compose/compose-file/compose-file-v3.html)からDocker-Engineに対応したバージョンを書く|
|`services`|各サービスごとに定義していく。複数のコンテナを立ち上げることも可能|
|`jupyter`|サービスの名前|
|`container_name`|コンテナとしての名前|
|`build`|ビルドの情報。`context`は`docker image build`のときの最後の"`.`"|
|`ports`|ホスト側18888とコンテナ側8888を繋げる|
|`volumes`| ホスト側の`source`をコンテナ側の`target`にマウント(`bind`)する|

### 2. `docker compose up`する 
 `docker-compose.yml`がある場所で以下のコマンドを打つだけ。
```sh:WSL
docker compose up
```
　初回は`docker image build` ⇒　`docker container run`相当の動きをします。次からは`docker container run`から始まります。長いコマンドが簡単になって嬉しいです。
　docker-composeは**複数のコンテナをまとめて**buildしたりrunしたり出来るところがメリットです。実は必要なかったのですが、入門ということでやってみました。
    
　コンテナを停止させるときは`docker-compose.yml`がある場所で
```shell-session:WSL
docker compose down
```

# GPUを使った開発環境の構築
　更にホストPC上のGPUリソースにDockerからアクセスできるようにカスタムをしていく。
- `PyTorch`使う。
- ホストPCはWin11

## ドライバのインストール
　[nVIDEA DEVELOPPER](https://developer.nvidia.com/cuda/wsl)からGPUにあったドライバーを選ぶ。SDインストール済みだと思ってもやっておきましょう。Download Typeは"SD"にしました。
　インストール後、wslやDockerDesktop（DockerEngine）を再起動(起動していたら)

## WSL2にCUDA Toolkit　を入れる。
　[nVIDEA CUDA Toolkit](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#getting-started-with-cuda-on-wsl)を手順通りに進めていく。コマンドを写経すればOK。WSL2の環境は作成済みなので、"3. CUDA Support for WSL 2"からやれば良いはず。

動作確認コマンド。WSL上からGPUが認識されてればOKです。
``` sh:WSL
/usr/local/cuda/bin/nvcc --version
```

## `Dockerfile`にpytorchのpipを追加
   [PyTorch公式](https://pytorch.org/get-started/locally/)でpipのコマンドを教えてくれる。Windowsじゃなくて、Linux版のコマンドを参照して、`Dockerfile`を以下のように変更する。
```Dockerfile:Dockerfile
FROM python:3.9-slim-buster

RUN pip install --upgrade --no-cache-dir pip && \
  pip install --upgrade --no-cache-dir \
  jupyterlab \
  torch \
  torchvision \
  torchaudio

CMD ["jupyter", "lab", "--ip=0.0.0.0", "--allow-root", "--LabApp.token=''"]
```

## `docker-compose.yml`にgpuの割り当てを記載する
　`deploy`以下が追記した部分。（サービス名やコンテナ名も変更してる）
```yml:docker-compose.yml
version: '3.8'

services:
  pytorch:
    container_name: pytorch

    build:
      context: .

    ports:
      - '18888:8888'
    volumes:
      - type: bind
        source: /home/user/test/src
        target: /mnt/host_src

    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
```

## コンテナを立ち上げて動作確認する。
### 1. コンテナの立ち上げ（再掲）
```sh:WSL
docker compose up
```

### 2. コンテナのjupyterに接続する。（再掲）
 ホストPC(Windows)のブラウザで`localhost:18888`にアクセス。

### 3. 適当なnotebookを作成して以下を実行
```py:test.ipynb
import torch
torch.cuda.is_avalilable()
```
>True

これでDocker上でPyTorchをGPUで動かす準備ができた！

# 参考リンク
- [実践 Docker - ソフトウェアエンジニアの「Docker よくわからない」を終わりにする本](https://zenn.dev/suzuki_hoge/books/2022-03-docker-practice-8ae36c33424b59)
- [Windowでデータ分析環境を構築する方法](https://gure-it-memo.com/windows-environment/)
- [Windows11 + WSL2 + Docker DesktopでGPU環境を作る](https://zenn.dev/okz/articles/83e6f899150b1e)
- [PyTorch公式](https://pytorch.org/get-started/locally/)
- [nVIDEA CUDA Toolkit](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#getting-started-with-cuda-on-wsl)