VScodeでdockerの開発環境(python)を使う。

Ubuntu20.04.3LTS
Docker version 20.10.7
docker-compose version 1.29.1
nvidia-docker2(sudo apt-get install -y nvidia-docker2でNvidia Container Toolkitが導入される）
VScode 1.61.1
RTX 3060  Driver Version: 470.74

python機械学習の開発環境をどうするか。
Anaconda:事業展開するときに有償ライセンスが必要かも知れない。
Docker Desktop:事業展開するときに有償ライセンスが必要かも知れない。
という背景から、ライセンスが緩いdockerとdocker-compose を使った方がいい？という動機。
最新の開発環境は、dockerで提供されるケースが増えているという助言も。
CUDAやcuDNN、tensorflowのバージョン合わせに疲れたこともある。

できれば、VScodeを使ってGUIで操作したという動機。

ここでは、VScodeの実行ボタンを押して、コンテナ内のpython上で実行させることを記述。
参考　https://qiita.com/Yuki_Oshima/items/d3b52c553387685460b0

実は、以下の作業全部クロームリモートデスクトップを使って、遠隔でWindowsPCからやっていました。
https://qiita.com/katakaku/items/dafc5bcbebf443f75f9b

## VScodeの拡張機能

VScodeのプロジェクトはフォルダ単位で管理される。ローカル（ホスト）とリモート（コンテナ）を行き来するので、どちらにいるのか気をつけておく。（左下の緑のリモートマークに注目）
以下の拡張機能をホスト側（通常インストールして開いた画面）でインストールする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/1dbcc0be-eeb7-4ded-6b3b-7647ec1dc45e.png)
いつの間にか名前が変わっていた(Remote -> Dev)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/06097623-44fe-ec29-f279-4e969e3a5b00.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/f8641c48-4b4c-4e84-0b0e-38ada1cb7f25.png)
dockerの拡張機能を入れるとタブからコンテナ一覧を見たり、起動・削除ができるようになる。また、ファイルメニューからdockerfileを右クリックするとビルドできるが、今回の流れでは操作しない。コンテナのターミナルを起動することもできるが、混乱するので慣れるまでは使わない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/f6300a6a-bfc1-c289-c377-8f3c14f03048.png)

## プロジェクトフォルダを開く
dockerfileやdocker-compose.ymlがあるフォルダを開く。するとホストのファイルが表示される。dockerfileとdocker-compose.ymlの記載例は下の方にあります。その他、pythonのライブラリを一括でインストールするのに便利なrequirementsファイル、pythonのテスト用のファイルや（今回の流れでは無くても良いですが）学習管理サイトWeights&Biases(wandb)の設定ファイルなどが有ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/9ac108af-0052-efd0-e55c-2df765aba509.png)　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/fda6abfb-f621-be36-f0c4-a7b1c758dafb.png)

## コンテナのなかに入る
左下の「リモートウィンドウを開きます」![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/a8703632-a548-6a32-b96b-54b1104a9055.png)　を押す。
ここから、Reopen in Containerを選ぶ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/f8695477-4053-dcf4-1326-4933963f7704.png)
すると、VScodeがdocker用の設定ファイルを作るベースを聞いてくる。Dockerfileのみの場合は、Dockerfile。docker-compose.ymlがあるならFrom docker-compose.ymlを選ぶ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/0e35d964-f4c8-07f5-c280-6cf9a57c2d46.png)
すると、左下のマークが替わり、コンテナに接続される。コンテナ内（リモート）の状態だ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/e5529e68-e8a5-5369-9362-6cc6ec96d9db.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/a137c9dc-2a6d-118b-c073-dfe0f99c7ec2.png)

ホスト側のプロジェクトフォルダが、コンテナ内の/workspaceフォルダにデフォルトでマウントされる。このフォルダ内のファイルに対する変更や新規作成はホスト側に反映される。

同時に隠しフォルダ.devcontainerが作成されその下にdevcontainer.jsonとdocker-compose.ymlが作成される。docker-compose.ymlは既存のと重複するが、隠しファイル内に作成されたものは、VScodeに必要な記述だけ記載するのが良いと思う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/d97261fe-c340-a06b-1462-8b5278cc290f.png)
## コンテナ内でpythonファイルの実行
エクスプローラーからpythonのファイルを選ぶと実行ボタンがない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/421b87b3-1ee7-6822-d364-8ccc6a512823.png)
メニューの実行からデバッグの開始やデバッグなしで実行を選ぶと、拡張機能がないと表示されるので、pythonの拡張をインストールする。ホストに拡張をインストールしていてもリモートのコンテナにはインストールしていないので必要になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/54ba561a-ca3f-7b6b-bdd9-819dd2932bf9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/3f05cd94-ff13-2b2d-dfc6-e51e5a7e350c.png)
拡張がインストールされると、右上に実行ボタンが表示されるようになった。
ちなみに最近、実行ボタンの色が緑から白に変わったようだ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/98a2cd87-03b0-db2c-5586-542914776387.png)
実行ボタンがうまく機能しない場合はpythonインタプリターの指定を確認する。

左下の![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/9b9c50bb-4713-3b92-a0b5-4de94a2bb153.png)を押すと、pythonのパスが複数表示される。これらは、ホスト側ではなく、リモート（コンテナ）側のパスである。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/a434b749-5180-c25e-44fa-10c9834795d6.png)

端末からwhich pythonを実行して一致するものを選ぶ。（今回の場合pythonとpython3はどちらも一緒）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/4750752a-c6cc-66c7-e6a0-f4364e52e33f.png)

## ホスト側に戻る
左下のボタンを押し、「リモート接続を終了する」を選ぶ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/ac3dbc83-68fb-78f4-39aa-ac9eca3ebd20.png)

VScodeが作業開始待ちの初期状態に戻る。
docker拡張のタブを見てみると、先ほどのコンテナが休止しているのが分かる。pruneボタンを押すと休止中のコンテナの削除ができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/e4707cc2-754f-c1aa-5d43-c81e865c4c92.png)

## コンテナ用の拡張がビルド時にインストールされるように設定する

dockerfileなどを変更して再ビルドを繰り返すとき、拡張が自動で入る方が楽です。この場合、隠しフォルダ内のdevcontainer.jsonに拡張機能の識別子を記述します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/dc74e8e3-7091-18e1-a510-021ef911ad4d.png)
識別子は拡張をクリックした画面の詳細情報のなかに記載がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/7ecc9000-011f-6bea-45bd-20b0bf07a64e.png)

## コンテナ内で一般ユーザーとして実行する
コンテナ内でマウントしたファイルを操作すると、所有者がrootに変わってしまい面倒だが、自分のdockerの使い方が違うのか？下に示すdockerfileの記述で一般ユーザーとしてファイル作成・上書きができる。コンテナ内のユーザー名はホスト側のユーザー名と異なっても大丈夫。コンテナ内で作成したファイルは、ホスト側から見るとホストの所有になる。ls -laで確認する。

## 今回使用したdockerfile
イメージはこちらから選ぶ。

https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tensorflow/tags

tensorflowのイメージでは認証を求められなかったが、pythorchのイメージでは認証を求められイメージのダウンロードが中断した。NGCにログインしてからGenereteAPI Keyを選択。コマンドラインでログインしておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/926a2fb8-15b3-f182-6b88-956a28568884.png)


```dockerfile
# tensorflowがGPU上で使えるイメージ
FROM nvcr.io/nvidia/tensorflow:21.09-tf2-py3

# ユーザーを作成
ARG UID=1000
RUN useradd -m -u ${UID} user

# 作成したユーザーに切り替える
# このユーザーはRUN, CMD, ENTRYPOINT, docker run, exec の実行ユーザ。
USER ${UID}

# プロジェクトフォルダを/codeに追加する。ユーザー権限で扱えるようchownオプションを使う。
# ADDの実行権者はrootなのでオプションが必要。
ADD  --chown=user:user . /code
# 作成したフォルダに移動し、パッケージリストをインストールする。
WORKDIR /code
RUN pip install -r requirements.txt

# 作成時にホームディレクトリに置きたいファイルがあれば
RUN cp .netrc ~

```

ここで呼び出しているrequirements.txt。利用したいパッケージを記載。うまく行っている環境があればpip freezeで取得。

```requirements.txt
matplotlib
jupyter
wandb

```

あらかじめ作成しておいた、docker-compose.ymlの記述。
dockerfileで大枠を記載して、docker-compose.ymlで細かな部分を記述する感じ。docker-compose.ymlだけでも書けるらしい。

```docker-compose.yml
version: '3'# ３．２とかも記載できる。２，３系では命令が少し異なるらしい
services:
  gpu_test:
    build: .  # ドットはdockerfileの記述を使う意味
    deploy:
      resources:
        reservations:
          devices: # GPUを利用するための記述。ホスト側はドライバだけ入れればいい。cudaとか不要。
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    ports:
     - "8888:8888" #ジュピターノートブック用のポート hostname to 0.0.0.0
    volumes:
     - /media/DATA_drive/DATA:/data:cached # データフォルダをマウント
     - .:/code:cached # ソースコードがあるフォルダをマウント
    
    tty: true


```

隠しフォルダに作成されたdocker-compose.yml 抜粋

```docker-compose.yml

    #volumes:
      # Update this to wherever you want VS Code to mount the folder of your project
      #- .:/workspace:cached 上述のdocker-compose.ymlで記述したためコメントアウト

    # Overrides default command so things don't shut down after the process ends.
    # コンテナが自動終了しないようにするらしい
    command: /bin/sh -c "while sleep 1000; do :; done"
```

devcontainer.json抜粋

```devcontainer.json
	// The optional 'workspaceFolder' property is the path VS Code should open by default when
	// connected. This is typically a file mount in .devcontainer/docker-compose.yml
	"workspaceFolder": "/code",# ソースファイルをマウントしたフォルダを指定

	// Add the IDs of extensions you want installed when the container is created.
	"extensions": ["ms-python.python"] # ビルド時に入れたい拡張機能を記述

```

## dockerfileやdocker-compose.ymlを編集した後
ホスト側でdockerの記述を変更して、Reopen in Containerを選ぶと、右下のポップアップでリビルドするか無視するか聞かれるので、Rebuildを選ぶ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/74f91173-e68a-4d37-e54a-a7d1837a9ed8.png)
リモート側で編集して、左下のリモートボタンを押した後Rebuid　Containerを選んでも良い。


## jupyter notebookの起動
コンテナ内のターミナルから、jupyter notebook で起動。docker-compose.ymlでブラウザに接続するポートの設定ずみ。
表示されたURLをホストのブラウザで開く。hostnameの部分は0.0.0.0に変える必要があった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/a2b5d84b-a6fa-e9fe-c0e9-9f1f238b729b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/260f2372-36d7-c488-2457-a523ff850f38.png)

### jupyter notebookのマークダウン文書の翻訳
ブラウザの翻訳機能を使うと、jupyter notebookの計算式部分も翻訳される。これを避けるため、セル単位で翻訳可能な拡張機能（nbTranslate）を使う。これは、nbextensionsの１機能として提供されている。
インストールと設定
https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/install.html
https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/nbextensions/nbTranslate/README.html
を参考に
VS-codeのターミナルを使って

```
pip install jupyter_contrib_nbextensions
jupyter contrib nbextension install --user
```
（うまく行ったら、Dockerfileに記載して永続化する）
jupyter notebookとコマンドをうち、ブラウザでアクセスして、左上のdisableのチェックを外しnbTtranslateにチェックを入れる。
その後、下方のSource言語に英語en ターゲット言語に日本語jaを入れる。（新規の際のデフォルト設定になる）グーグル翻訳エンジンが利用される。（初期設定のファイルはここに作製されるようです。/home/user/.jupyter/nbconfig/notebook.json）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/7691e957-c6b4-ec7f-54da-92a59ad95a07.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1520250/48d7a1d2-1a62-9ef7-a84c-0f884d085fb8.png)

拡張機能では他にもいろいろな事ができそうです。
https://qiita.com/simonritchie/items/88161c806197a0b84174

以下のコマンドで一括変換もできると書いてあるがうまく行かない？
```
jupyter nbconvert --to selectLanguage --NotebookLangExporter.language=ja  FILE.ipynb 
```



## 上述とは異なり、dockerfileだけのプロジェクトフォルダを使うとき
Reopen in Containerを実行すると、隠しフォルダのdevcontainer.jsonのみが作成されコンテナに入る。デフォルトでコンテナ内の/workspace/プロジェクトフォルダ名にフォルダが作成され、ホスト側のプロジェクトフォルダがマウントされる。
コンテナ内でこのフォルダの変更はホスト側に反映される。

## 参考サイト
https://medium.com/nvidiajapan/nvidia-docker-%E3%81%A3%E3%81%A6%E4%BB%8A%E3%81%A9%E3%81%86%E3%81%AA%E3%81%A3%E3%81%A6%E3%82%8B%E3%81%AE-20-09-%E7%89%88-558fae883f44