Windows10 + WSL2 + DockerDesktop + docker-compose +GPU(Nvidia)+Jupyterlabの環境構築

#はじめに
本記事は私がタイトル通りの環境を構築するために行った事を記したレポートです。
使用する環境のWindows10、Docker Desktop、Docker-composeがプレビュー版となっているため安定しているかは不明です。
※Qiita初投稿のため作法がなっていなかったら申し訳ございません。
※Dockerも勉強期間1ヶ月程度の知識のため、誤っている内容が含まれておりましたら指摘頂けると幸いです。

Qiita内外の情報をかき集めてタイトルの環境を構築できたので、環境構築の手順を記載いたします。

**※2021年1月10日現在の内容です。**
　DockerDesktop、WSL2、GPU(nvidia)周りはアップデートが早いため注意が必要です。

**※2021年1月20日追記**
Docker-composeの1.28.0がリリースされました。
docker-compose.ymlは当記事のままでも問題なく動いております。
[Githubリリースページ](https://github.com/docker/compose/releases/tag/1.28.0)
  
 
※私が見つけられていないだけで既に同様の記事が存在していたら指摘頂けると幸いです。
　直ぐに記事を削除致します。

Qiitaが無ければ環境構築できていませんでした。
参考になる方が居たら幸いです。


#構成
OS:Windows 10 Pro Insider Preview  ビルド:21286.1000
CPU:Ryzen5900X
GPU:GeForce RTX 3070
WSL2:ubuntu 20.04LTS
DockerDesktop:テクニカルプレビュー版
Docker:version 20.10.0
Docker-compose:1.28.0

#構築手順
1.Windows10 Insider Preview Buildのインストール
2.WSL2のインストール
3.Docker Desktopのプレビューバージョンのインストール
4.Nvidiaドライバのベータ版をインストール
5.NGCコンテナイメージをpull
6-1.コンテナを起動して必要なパッケージをインストール後、イメージを作成
6-2.Dockerfileを使って新たなイメージを作成
7.作成したイメージでGPUが有効か確認
8.docker-composeのymlファイルを作成、docker-composeでコンテナを立ち上げ

#手順1：Windows10 Insider Preview Buildのインストール
Windowsの設定から更新とセキュリティ、Windows Insider Programを選択
「Devチャネル/ベータ/リリースプレビュー」の「Devチャネル」を選択
※Microsoft アカウントが必要です。
※インストールに時間がかかります。
![インサイダービルド.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1013917/d099d0cf-10dc-9c37-4083-5f4fd2d19cc2.png)


#手順2:WSL2のインストール
下記の記事を参考にWSL2のインストールを実施する。
※Windows10 proでも問題ないです。
※LinuxのディストリビューションはUbuntu-20.04LTSを選択
[Windows 10 Home で WSL 2 + Docker を使う](https://qiita.com/KoKeCross/items/a6365af2594a102a817b)

Windows Power shellで以下のコマンドを実行した際に

```shell:PowerShell
wsl --set-default-version 2
```

>WSL 2 を実行するには、カーネル コンポーネントの更新が必要です。詳細については https://aka.ms/wsl2kernel を参照してください

と表示された場合は下記の記事の「カーネル コンポーネントの更新」以降を参照する
[WSL2導入｜WinアップデートからWSL2を既定にするまでのスクショ](https://qiita.com/tomokei5634/items/27504849bb4353d8fef8)

#手順3:Docker Desktopのプレビューバージョンのインストール
GPUサポートした旨を記載した[Docker公式ブログ](https://www.docker.com/blog/wsl-2-gpu-support-is-here/)の
>To get started with Docker Desktop with Nvidia GPU support on WSL 2, you will need to download our technical preview build from [here.](https://desktop.docker.com/win/preview/50723/Docker%20Desktop%20Installer.exe)

hereからプレビューバージョンのDockerDesktopをインストールする
※インストール後にWSL2 backendを有効にするのを忘れないようにする

#手順4:Nvidiaドライバのベータ版をインストール
以下のリンクから、NVIDIAドライバのベータ版をインストールします
https://developer.nvidia.com/cuda/wsl/download

![gefo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1013917/fc4ebe40-f444-9f6e-1348-a4a1d246c2d7.png)

>NVIDIA Developer Program Membership Required
>>The file or page you have requested requires membership in the NVIDIA Developer Program. Please either log in or join the program to access this material. Learn more about the benefits of the NVIDIA Developer Program.

と表示されるためNVIDIA Developer Program Membershipに登録している方はログインしてドライバのダウンロード、インストールを行ってください。
※登録していない方は登録の上ダウンロード願います。

#手順5:NGCコンテナイメージをpull
pullするイメージは何でも良いのですが、Nvidia提供のDockerイメージ(NGCコンテナ)があるためそちらをpullします。
フレームワークはTensorflow,Pytorchが選択できます。

以下のリンクから、TensorFlowを選び最新のDockerイメージのtagを探す
https://www.nvidia.com/ja-jp/gpu-cloud/containers/

![NGC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1013917/24a2a755-ebd5-2fbc-59ad-8842a28bf623.png)

![NGCコンテナ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1013917/9738248a-eb33-d0a6-ef56-4b0524f53ed7.png)

最新のイメージを以下のコマンドでpull
※少々時間がかかります。
※最新のイメージは変わると思いますので都度確認願います。

```shell:bash
#TensorFlow1の場合
$docker pull nvcr.io/nvidia/tensorflow:20.12-tf1-py3
#                                     ↑(20.12）←ここがver毎に変わると思います。
#TensorFlow2の場合
$docker pull nvcr.io/nvidia/tensorflow:20.12-tf2-py3
```


#手順6-1:コンテナを起動して必要なパッケージをインストールした後に、Dockerイメージを作成(手順6-2のどちらかを実施)
TensorFlowとKerasはインストール済みですが
pandas、matplotlib、Seabornなどのパッケージがインストールされていないため
一度コンテナを立ち上げ必要なパッケージをインストールした後に、新たなイメージを作成します。
~~※Dockerfileでイメージを作成するのが一般的だと思いますがまだ出来ておらず...~~
2021/1/23：Dockerfileを用いたイメージ作成を追記(後述)


先ほどpullしたイメージのコンテナを、WSL2のターミナルで立ち上げた後に
必要なパッケージをインストールします。

```shell:bash
#WSL2のターミナルで以下コマンドを実施
$docker run -it nvcr.io/nvidia/tensorflow:20.12-tf2-py3 bash
```

```shell:bash
#-------コンテナ内--------------
================
== TensorFlow ==
================

NVIDIA Release 20.12-tf2 (build 18110405)
TensorFlow Version 2.3.1

Container image Copyright (c) 2020, NVIDIA CORPORATION.  All rights reserved.
Copyright 2017-2020 The TensorFlow Authors.  All rights reserved.

#~~~中略~~~~

root@9f1a8350d911:/workspace#pip install 必要なパッケージ(matplotlibとか)
#必要なパッケージがインストール出来たら一旦コンテナを停止
root@9f1a8350d911:/workspace#exit
```
```shell:bash
#-------ここからWSL2のターミナル--------------
#psコマンドでコンテナのIDを取得する
$docker ps -a
CONTAINER ID   IMAGE                                     COMMAND                  CREATED         STATUS                     PORTS     NAMES
5dc0ae8981bf   nvcr.io/nvidia/tensorflow:20.12-tf2-py3   "/usr/local/bin/nvid…"   3 minutes ago   Exited (0) 3 seconds ago             confident_noether

#コンテナIDが確認出来たらcommitコマンドでイメージを作成
#イメージの名前はDockerhub等にpushする場合は、リポジストリと名前を合わせる必要があります。ローカルで使うだけであれば好きな名前で問題ないです）
$docker commit 5dc0ae8981bf　hogehoge:latast 
#問題なくイメージを作成できれば以下のような画面が出力されます。
sha256:8461579f0c2adf2a052b2b30625df0f48d81d3ab523635eb97b360f03096b4

#docker imagesコマンドでイメージを確認
#問題なければ以下のような出力
$docker images
REPOSITORY                  TAG             IMAGE ID       CREATED        SIZE
hogehoge                    latest          9d3ea0900f00   29 hours ago   13.4GB
nvcr.io/nvidia/tensorflow   20.12-tf2-py3   21d1065bfe8f   5 weeks ago    12.2GB
```

#手順6-2:Dockerfileでのイメージ作成(手順6-1のどちらかを実施)
以下のようなDockerfileを作成してイメージを作成

```Dockerfile
#↓ベースイメージを記載(今回はNGCコンテナのイメージ名）
#ベースイメージが無かったらpullしてくれます
FROM nvcr.io/nvidia/tensorflow:20.12-tf2-py3 
#↓aptをアップデートしてから必要なパッケージをインストールするRUNを追記
RUN apt update && apt upgrade -y \ 
	&& yes | pip install -U pip \
	matplotlib \ 
	pandas \
	scikit-learn \
	seaborn
#コンテナ起動時のデフォルトのコマンドを追記
CMD ["/bin/bash"]
```

Dockerfileがあるフォルダに移動して以下コマンドでイメージを作成

```shell:bash
#WSL2のターミナルで以下コマンドを実施
docker build -t <イメージの名前>:<tagの名前> <directory>
#例
docker build -t hogehoge:latest . #←カレントディレクトリを表す「.」

```


#手順7:作成したイメージでコンテナを作成してGPUが有効か確認
**※GPUを有効にする場合はdocker runコマンドのオプションに「--gpus all」が必要です。**

```shell:bash
#WSL2のターミナルで以下コマンドを実施
#--rmはコンテナ停止時に自動で削除するオプションです。
docker run -it --rm --gpus all -p 8888:8888 hogehoge:latest jupyter lab


#---ここからコンテナ内---
================
== TensorFlow ==
================

NVIDIA Release 20.12-tf2 (build 18110405)
TensorFlow Version 2.3.1

#~~~中略~~~
 To access the notebook, open this file in a browser:
        file:///root/.local/share/jupyter/runtime/nbserver-1-open.html
    Or copy and paste one of these URLs:
        http://hostname:8888/?token=[トークン]
     or http://127.0.0.1:8888/?token=[トークン]

#終了する場合はWSLのターミナル上でCtrl+Cを実行
```
上記のように出力されるためWebブラウザ(私はChrome)でhttp\://127.0.0.1:8888/?token=[トークン]にアクセス
Jupyterlabに接続できるので、新しいノートブックで以下のコードを実行
device_type: "GPU"が表示されていればGPUが有効

```python
from tensorflow.python.client import device_lib
device_lib.list_local_devices()

#GPUが有効であれば以下のように出力
[name: "/device:CPU:0"
 device_type: "CPU"
 memory_limit: 268435456
 locality {
 }
 incarnation: 16078152362305136132,
 name: "/device:XLA_CPU:0"
 device_type: "XLA_CPU"
 memory_limit: 17179869184
 locality {
 }
 incarnation: 6904616874393552950
 physical_device_desc: "device: XLA_CPU device",
 name: "/device:XLA_GPU:0"
 device_type: "XLA_GPU"
 memory_limit: 17179869184
 locality {
 }
 incarnation: 13161252575635162092
 physical_device_desc: "device: XLA_GPU device",
 name: "/device:GPU:0"
 device_type: "GPU"
 memory_limit: 5742592000
 locality {
   bus_id: 1
   links {
   }
 }
 incarnation: 2330595400288827072
 physical_device_desc: "device: 0, name: GeForce RTX 3070, pci bus id: 0000:2b:00.0, compute capability: 8.6"]

#--gpuをオプションにつけていない場合や、どこかでうまくいっておらずgpuを認識できていない場合は以下のような出力になる
[name: "/device:CPU:0"
 device_type: "CPU"
 memory_limit: 268435456
 locality {
 }
 incarnation: 68886281224950509,
 name: "/device:XLA_CPU:0"
 device_type: "XLA_CPU"
 memory_limit: 17179869184
 locality {
 }
 incarnation: 13575954317913527773
 physical_device_desc: "device: XLA_CPU device"]
```

#手順8:docker-composeの利用
docker runコマンドでコンテナを作成するのであれば手順7まで問題ないですが
毎回長いコマンドを打つのが大変なのでdocker-composeを利用したいと思います。

~~docker-composeの安定版は--gpus allがサポートされていないため~~
~~runtime:"nvidia"を利用する方法となっております。~~
~~※私はこの方法では、うまくいきませんでした...~~

~~参照記事：~~
~~[docker-composeでgpuを使う方法](https://qiita.com/hgoj/items/1ba050c7d73e0dd5ef90)~~
~~[docker-compose内でGPUを走らせる(2020年2月2日時点)](https://qiita.com/sht47/items/0706f19c67eb30e0954f)~~
~~[githubのissue](https://github.com/NVIDIA/nvidia-docker/issues/1073#issuecomment-532104398)~~
~~[githubのissue](https://github.com/NVIDIA/nvidia-docker/issues/1073#issuecomment-533257434)~~


~~途方に暮れていた所、docker-copmoseのプレビュー版でGPUをサポートしたとのことですのでそちらのやり方で行きます。~~
~~[Githubのリリースページ](https://github.com/docker/compose/releases/tag/1.28.0-rc1)~~
~~↑はverが1.28.0-rc1ですが、一番新しいプレビュー版が1.28.0-rc2のためそちらをインストールします。~~


※2021/1/20追記※
正式版(1.28.0)がリリースされました。
 [リリースページ](https://github.com/docker/compose/releases/tag/1.28.0)

docker-composeのアップデート方法
[docker-composeの公式](https://matsuand.github.io/docs.docker.jp.onthefly/compose/install/)を参照

```shell:bash
#WSLのターミナルで以下を実施
$sudo curl -L "https://github.com/docker/compose/releases/download/1.28.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

#ダウンロード出来たら以下を実施
$sudo chmod +x /usr/local/bin/docker-compose

#docker-composeのverを確認
$docker-compose -version
#問題なければ以下が出力
docker-compose version 1.28.0-rc2, build f1e3c356
```
プレビュー版をインストール出来たらdocker-compose.ymlを作成します。
参照：[Github Enable GPU access with DeviceRequests](https://github.com/docker/compose/pull/7929)
※deploy:のコメントの意味が分かっていません。


``` docker-compose.yml
version: '3.7'
services:
  jupyterlab:
    image: hogehoge:latest #自分が作成したイメージ名を記載
    deploy:
      resources:
        reservations:
          devices:
          - 'driver': 'nvidia'
            'capabilities': ['gpu']
    container_name: jupyterlab
    ports:
      - '8888:8888'
    #↓ホストとコンテナのボリュームをマウントします ホストのボリューム：コンテナのボリュームの表記
    #./がカレントディレクトリのためWSL2のターミナルでdocker-compose.ymlが置いてあるディレクトリに移動してから使う
　　volumes:
      - './ds_python:/workspace' #フォルダ名は適当に変える
    command: jupyter lab
    tty: true
    stdin_open: true
```
docker-compose.ymlが作成出来たら
以下のディレクトリ構成にする

```
└── ./適当なフォルダ
    ├──── docker-compose.yml(適当なフォルダの直下)
        └── ds_python(ds_pythonの下にコンテナに持っていきたいファイルを格納する）
          ├── ~~~~.py 
          ├── ~~~~.ipynb 
```
ディレクトリが構成出来たら、WSLのターミナルで以下を実施
※WSL2でWindows側のディレクトリを表現するにはmnt/c/users~のように記載します。(例はCドライブ)

```shell:bash
#WSLのターミナルで以下を実施
#docker-compose.ymlのあるディレクトリに移動
$cd /mnt/c/~

#ディレクトリを移動できたらdocker-composeでコンテナ起動　-dはバックグラウンドで実施
$docker-compose up -d
#問題なければ以下が出力
Creating network "docker_default" with the default driver
Creating jupyterlab ... done

#jupyter labのアドレスがわからないので以下で確認
$ docker logs jupyterlab
#docker runコマンドの時と同様の内容が出力されるので
#Webブラウザ(私はChrome)でhttp://127.0.0.1:8888/?token=[トークン]にアクセス

#コンテナを削除したい場合は、docker-compose downで終了
$docker-compose down
```

Jupyterlabにアクセス後、docker-runコマンドと同様の手順でGPUデバイスが表示されていれば完了です。




###おわりに
・いろいろ調べたけど、アップデートが早い分野のため構築手順がすぐに陳腐化しそう。


#参照記事
**手順1,3,4の参照記事**
[WSL2 + Docker Desktop + GPU で快適開発生活](https://qiita.com/yamadalter/items/00797cef1d3fe0f38465)
https://www.docker.com/blog/wsl-2-gpu-support-is-here/

**手順2の参照記事**
[Windows 10 Home で WSL 2 + Docker を使う](https://qiita.com/KoKeCross/items/a6365af2594a102a817b)
[WSL2導入｜WinアップデートからWSL2を既定にするまでのスクショ](https://qiita.com/tomokei5634/items/27504849bb4353d8fef8)

**手順5の参照記事**
[NGC（nVIDIA GPU Cloud）を試してみた！(Qiita外)](https://bto.applied.ne.jp/c1189-c2193-pm224.html)

**手順6の参照記事**
[Dockerコマンド よく使うやつ](https://qiita.com/Esfahan/items/52141a2ad741933d7d4c)
[Dockerコンテナの作成、起動〜停止まで](https://qiita.com/kooohei/items/0e788a2ce8c30f9dba53) 
[Dockerの作業済みコンテナからイメージを作って移植を楽にする](https://qiita.com/tubone/items/a3bad04abf4c700cae3d)
[Dockerfileを書いてみる](https://qiita.com/shin-ch13/items/1de829288db2670276e8)

**手順7の参照記事**
[【Docker】3分でjupyterLab(python)環境を作る！](https://qiita.com/hgaiji/items/edf71435d0565257f980)
[Dockerを使って5分でJupyter環境を構築する](https://qiita.com/fuku_tech/items/6752b00770552bf4f46b)
[TensorFlowからGPUが認識できているかを2行コードで確認する(Qiita外)](https://thr3a.hatenablog.com/entry/20180113/1515820265)


**手順8の参照記事**
[docker-composeでgpuを使う方法](https://qiita.com/hgoj/items/1ba050c7d73e0dd5ef90)
[docker-compose内でGPUを走らせる(2020年2月2日時点)](https://qiita.com/sht47/items/0706f19c67eb30e0954f)
[docker-composeの公式](https://matsuand.github.io/docs.docker.jp.onthefly/compose/install/)
[jupyterをdocker-composeで起動1](https://qiita.com/va034600/items/9ceb36821a3a12a34b3a)
[docker-compose.ymlの書き方について解説してみた](https://qiita.com/yuta-ushijima/items/d3d98177e1b28f736f04)
[複数のDockerコンテナを自動で立ち上げる構成管理ツール「Docker Compose」（Dockerの最新機能を使ってみよう：第7回）](https://knowledge.sakura.ad.jp/5736/)