VSCodeリモートコンテナで、簡単にPythonの環境構築をする方法

# 1. 目的
この記事の目的は、**VSCodeのリモートコンテナでPython環境を構築し、楽に開発・学習を行えるようにすること**です。

この記事を参照するメリットは以下の点があります。
* ローカルPCの環境を汚さない、仮装環境を簡単に構築できる
* VSCodeの拡張機能を仮想環境内で使うことができる

# 2. 事前準備
必要な事前準備は以下のようになります。

## 2-1. VSCodeと拡張機能のインストール
以下のページを参考に、VSCodeと拡張機能をインストールしてください。

https://code.visualstudio.com

必要な拡張機能
* [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)

## 2-2. Docker Desktopのインストール
以下のページを参考にDocker Desktopをインストールしてください。

* [Macの方](https://docs.docker.com/desktop/install/mac-install/)
* [Windowsの方](https://docs.docker.com/desktop/install/windows-install/)
:::note warn
Windouwの方への注意
ドキュメント内のSystem requirementsに記述されているように、 [以前のバージョンの WSL の手動インストール手順](https://learn.microsoft.com/ja-jp/windows/wsl/install-manual#step-4---download-the-linux-kernel-update-package)に従って、WSL2を用意してから、Docker Desktopをインストールしてみてください。
:::
* [Linuxの方](https://docs.docker.com/desktop/install/linux-install/)


# 3. ディレクトリとファイルの作成
以下では、VSCodeのリモートコンテナでPython環境を構築するために必要なディレクトリ、ファイルを作成します。

私のGitHubで、今回作成するファイルを公開しています。もしよろしければ参考にしてください。

https://github.com/Mochi-Moch1/vscode-remote-container-python

cloneで複製していただいても大丈夫です。
```
$ git clone https://github.com/Mochi-Moch1/vscode-remote-container-python.git
```


## 3-1. ディレクトリの作成
以下のようなディレクトリ構造を作成していきます。
親となるディレクトリ(vscode-remote-container-python)は好きな場所に作っていただいて大丈夫です。
```
vscode-remote-container-python
┝ .devcontainer 
│   └ devcontainer.json
┝ Dockerfile
┝ reqirements.txt
┝ docker-compose.yml
```

## 3-2. devcontainer.json ファイルの作成
.devcontainer の中に以下のようなファイルを作成します。
```devcontainer.json
{
	"name": "remote python3",
    "dockerComposeFile": "../docker-compose.yml",
    "service": "python3", 
	"workspaceFolder": "/workspace",
    "shutdownAction": "stopCompose",
    "extensions": ["ms-python.python"]
}
```
devcontainer.json はコンテナへのアクセス方法を伝え、VSCodeとコンテナを繋ぐ役目があります。


各項目を説明します。
1. `"name": "remote python3"`
    * 今回作るコンテナの名前です。

2. `"dockerComposeFile": "../docker-compose.yml"`
    * 参照するDocker Composeファイルへのパスを示します。パスは``devcontainer.json``からの相対パスで与えます。
    * Docker Composeを使う場合には必須です。


3. `"service": "python3"`
    * `devcontainer.json`で扱うDocker Compose内のサービスを与えます。
    * 基本は1つの`devcontainer.json`で一つのサービスを扱います。
    * Docker Composeを使う場合には必須です。

4. `"workspaceFolder": "/workspace"`
    * リモートコンテナに接続したときにデフォルトで開く、ファイルのパスを与えます。

5. `"shutdownAction": "stopCompose"`
    * リモートコンテナのウィンドウを閉じた時の処理を与えます。
    * `stopCompose`では、composeごと停止します。
6. `"extensions": ["ms-python.python"]`
    * リモートコンテナ内でダウンロードしたいVSCodeの拡張機能を与えます。
    * **ローカルコンピュータのVSCodeの拡張機能には、影響を与えません。**

## 3-3. Dockerfileの作成
以下に今回作成するコンテナのDockerfileを示します。
```Dockerfile
FROM python:3.10.6
# Latest version in 2022.9.1

RUN python -m pip install --upgrade pip

WORKDIR /workspace

COPY requirements.txt /workspace/

RUN pip install -r requirements.txt

ENV JUPYTER_ENABLE_LAB = yes

```
Dockerfileは1つのコンテナを作成するために使うファイルです。

各項目を説明します。

1. `FROM python:3.10.6`
    * コンテナの元となるimageを指定します
    * 使いたいPythonのバージョンがある方は、こちらを変更してください


1. `RUN python -m pip install --upgrade pip`
    * pipのインストールとアップデートを行います

1. `WORKDIR /workspace`
    * Dockerfileが扱う作業ディレクトリを`/workspace`に移動します

1. `COPY requirements.txt /workspace/`
    * requirements.txtをコンテナ内の`/workspace/`直下にコピーします

1. `RUN pip install -r requirements.txt`
    * ライブラリのインストールを行います
    * インストールしたいものは、後に説明するrequirements.txtに記述します


1. `ENV JUPYTER_ENABLE_LAB = yes`
    * Jupyter labを使うために、環境変数を変更します


### requirements.txtの作成
以下に今回作成するrequirements.txtを示します。
```requirements.txt
NumPy==1.23.2 
jupyterlab==3.4.5 
```
このファイルには、コンテナ内にインストールしたいライブラリを記述しておきます。(@shun198 さんに教えていただきました)

バージョンの指定は必須ではありません。ただし、指定しておく事で環境の統一化がしやすくなります。

## 3-4. docker-compose.ymlの作成
```docker-compose.yml
version: '3'
services:
  python3:
    build:
      context: .
      dockerfile: Dockerfile
    tty: true
    volumes:
    - workspace:/workspace
    ports:
      - '1000:8888'

volumes:
  workspace:
    #  driver_opts:
    #        type: none
    #        device: absolute path
    #        o: bind
```
docker-compose.ymlは複数のコンテナを管理し、構築、停止、削除などを行う役割を持ちます。

各項目を説明します。

1. `version: '3'`
    * Compose fileのバージョンを与えます


2. services:
  python3:
    * python3という名前のサービスを作成します
        * サービスは一つのコンテナのようなイメージです

1. build:
      context: .
      dockerfile: Dockerfile
    
    * コンテナを作成するためのDockerfileへのパスを指定します

1. `tty: true`
    * コンテナ側に仮想端末を置くことで、コンテナを正常終了させず、持続させます

1.  volumes:
    -workspace:/workspace
    * workspaceという名前のvolumeに場所`/workspace`を与えます
        * volumeはデータを保存しておく場所のようなイメージです
        * ウィンドウを閉じてもデータが保持されます

1.   ports:
     -'1000:8888'
    * ポートを指定します
        * 左(1000)はホストのポート
        * 右(8888)はコンテナのポート

1. volumes:
  workspace:
    * 先ほど指定したvolumeのworkspaceを再度指定します
        * volumeに名前をつけ、管理を楽にするために必要です 
:::note info
追加情報
自身のパソコンのフォルダやファイルをコンテナ内とつなげたい場合には、コメントアウト(#)を外してください。

そして、`device: absolute path` の`absolute path`の代わりにつなげたいフォルダなどの絶対パスを指定してください。


これをDockerでは[バインドマウント](https://matsuand.github.io/docs.docker.jp.onthefly/storage/bind-mounts/)と呼びます 
:::



# 4. リモート環境を組み立てる
いよいよVSCodeリモートコンテナを利用して、環境を立ち上げていきます！
## 4-1. フォルダをコンテナで開ける
1. VSCodeで先ほど作成したフォルダ`vscode-remote-container-python`を開いてください。

2. 左下の緑の部分をクリックしてください

3. Open Folder in Container をクリックしてください

4. Openでフォルダ`vscode-remote-container-pyhton`を開いてください

![スクリーンショット 2022-09-19 22.03.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/457b261f-c87f-a8a7-32ad-b1ad8d62b879.png)

### エラーが出た場合

* ポートに関するエラーが出た場合は、一度作成されたリモートコンテナを削除し、docker-compose.yml内のポート番号(1000)を1001などに変更してください。

* 修正してうまくいかない場合などは、F1キーを押して`Remote Containers : Rebuild and Reopen in Container`を試してみてください。

## 4-2. カレントディレクトリと拡張機能の確認
まずはカレントディレクトリを確認してみましょう。

以下のコマンドをVSCodeのターミナルに入力してください。
```
$ pwd
```
`/workspace` という出力があるかと思います。



これにより今、workspaceというdevcontainer.json内の`"workspaceFolder": "/workspace"`で指定したディレクトリにいることが確認できます。

また、ディレクトリの構成からローカル環境ではなく、仮装環境にいることも確認できます。


---
次に**拡張機能**を確認してみましょう。

左の拡張機能のタブから、ローカルのVSCode(LOCAL)とは別で、コンテナ内のVSCode(DEV CONTAINER:...)に独自の拡張機能がインストールされていることを確認できます。
![スクリーンショット 2022-09-19 22.18.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/be44d5e8-21f9-703c-00bf-4778043083ea.png)

これにより、この方法では仮装環境の拡張機能もカスタマイズできます。


## 4-3. Pythonファイルの作成と実行
簡単なpythonファイルを作成し、実行してみましょう。

1. ファイルを作成します

2. 名前にhello.pyと入力します
    これでPythonのファイルを作成できます。
![スクリーンショット 2022-09-20 10.16.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/601c77cb-83e8-b33e-f1b8-cc14b70e4bc1.png)


1. ファイルの中に、以下のコードを書きます
   この時、以下の2つを確認できます。
   * VSCodeの補完機能が働いている
   * ライブラリ(numpy)がインストールされている
    ```python
    import numpy as np
    print("hello remote container")

    print(np.zeros(5))
    ```
1. VSCodeのターミナルに以下のコマンドを入力する
    先ほど作成したPythonファイルを実行します。
    ```
    python3 hello.py
    ```
    以下のような結果が返ってくるかと思います。
    ```
    hello remote container
    [0. 0. 0. 0. 0.]
     ```


これでPythonファイルの作成と実行は完了です！

補完機能が働くなど、非常に便利に使えます！

## 4-4. Jupyter Notebookファイルの作成と実行
次にJupyter Notebookのファイルを作成し、実行していきます。

普段Notebook形式を用いている方は、こちらの方が慣れているかもしれません。

1. ファイルを作成します

2. 名前にsample.ipynbと入力します
    これでJupyter Notebookのファイルを作成できます。
![スクリーンショット 2022-09-20 10.45.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/f78d3165-b632-da17-b1fb-af3219598670.png)

1. Pythonのバージョンを変更します
    右上のPython 3..のようなボタンを押し、選択することで、Notebook内で用いるPythonのバージョンを変更できます。

    ここでは右端にGlobal Envと示されているPython 3.10.6を選択してみましょう。   
    ![スクリーンショット 2022-09-20 10.52.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/76e42a93-4591-2bae-cb9e-2793e6e07b11.png)

1. コードセルの中に、以下のコードを書きます
   Pythonファイルと同様に、以下の2つを確認できます。
   * VSCodeの補完機能が働いている
   * ライブラリ(numpy)がインストールされている
    ```python
    import numpy as np
    print("hello jupyter")
    
    np.ones(5)
    ```
    ![スクリーンショット 2022-09-20 10.59.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/5808eac4-22d9-bfdf-375f-0c1f6b5b2867.png)

1. 実行ボタンを押す
    先ほど作成したコードセルを実行します。
![スクリーンショット 2022-09-20 11.01.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/9a445fb7-f74a-2ad3-bbe0-532fb377c6c8.png)
画像のように、次の出力がされます。
```
hello jupyter
array([1., 1., 1., 1., 1.])
```
これでJupyter Notebookファイルの作成と実行は完了です！

こちらでも補完機能が働くなど、非常に便利に使えます！
# 5. リモートコンテナの終了と再開
次に、リモートコンテナを終了したのち、再開する動きを確認してみましょう。

普段使う際に、重要な部分です。
## 5-1. リモートコンテナを閉じる
主に2つの方法があります。
devcontainer.jsonのshutdownActionで指定したように、自動的にComposeを停止しますが、保存されているデータは消えません。
* VSCodeをそのまま閉じたい場合
    ウィンドウを閉じるボタンを押してください。   
---
* リモートコンテナだけを閉じ、そのままVSCodeを使いたい場合
    1. 左下の緑色のボタンを選択します
    2. Reopen Folder Locallyを選択します
    ![スクリーンショット 2022-09-20 11.51.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/359346f8-aee8-e47d-91da-5466565991c6.png)
    これで無事にローカルのVSCodeに戻れます。


    
## 5-2. リモートコンテナを再開する
再開する方法は主に2つあります。
中に作成したファイルが残っていることも確認してみてください。
1. 開きたいフォルダが指定してある場合
   最初に作成した`vscode-remote-container-python`フォルダをVSCodeで開いている場合は
    1. 左下の緑のボタンを押す
    1. Reopen in Containerを選択する
    ![スクリーンショット 2022-09-20 11.57.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/7c6568f3-5456-9459-7e15-a3dc6f0f2978.png)
    この流れで、リモートコンテナを再開できます。Dockerコンテナの仕組みにより、一度作成した仮装環境は、高速で立ち上げることができます。

2. フォルダを指定していない場合
    1. 左のRemote Explorerを選択します
    2. CONTAINERS > Dev Containers > workspace vscode-remote-container-python...を右クリックします
    2. Open Folder in Container を選択します
![スクリーンショット 2022-09-20 12.39.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/41c5a687-e542-c2fc-9cba-3b04e2d6bd1d.png)
:::note info
ショートカット
VSCodeを開いた時に表示される、Get Started > Recentの中でも選ぶことができます！
:::

## 5-3. リモートコンテナを削除する
最後にリモートコンテナを削除する方法を説明します。

もう今回作成した仮装環境を使わない場合に、簡単に削除できます。また、パソコンの容量の削減にもつながります。

1. ローカルのVSCodeを開きます

2. 左のRemote Explorerを選択します
2. CONTAINERS > Dev Containers > workspace vscode-remote-container-python...を右クリックします
2. Remove Container を選択します
 
 
![スクリーンショット 2022-09-20 12.39.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/15ba8f7a-c4d8-9b8b-4043-2ff7d7fb940b.png)

:::note warn
注意
この状態ではまだ、内部にあったデータ自体は削除されていません。
:::

完全に内部のデータを消すためには、DEVVOLUMESを削除する必要があります。バインドマウントを用いていない場合は、完全に消えるので気を付けてください。

1. 左のRemote Explorerを選択します
2. DEVVOLUMES > workspace vscode-remote-container-python...を右クリックします
2. Removeを選択します

![スクリーンショット 2022-09-20 14.22.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/900476/763e6a01-e07c-37b0-0436-8596582878e2.png)


以上でPythonの環境構築は終了です！お疲れ様でした！
快適なコーディングの手助けになれば嬉しいです。

## 次のステップ
https://qiita.com/Yu_Mochi/items/4fb5b87acdb3003e68db
* 同様の手順でPythonのWebアプリケーションフレームワーク Flaskの環境構築方法も説明しています。Webアプリに興味のある方は、ぜひご覧ください。

# 参考文献
以下を参考にさせていただきました。

* [devcontainer.json reference](https://containers.dev/implementors/json_reference/)
* [Dockerfile reference](https://docs.docker.com/engine/reference/builder/)
* [Compose file version 3 reference](https://docs.docker.com/compose/compose-file/compose-file-v3/)
* [Bind your Python image](https://docs.docker.com/language/python/build-images/)
* [VSCode のリモートコンテナ機能を用いて、あるリポジトリ専用の環境を開発者間で統一する](https://zenn.dev/yuchiki/articles/vscode-remote-container)
* [Docker、ボリューム(Volume)について真面目に調べた](https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32)
* [Docker+Django+Nginx +MySQL +Gunicornで環境構築~M1Mac対応](https://qiita.com/shun198/items/f6864ef381ed658b5aba)

#### 読んでいただき、ありがとうございました！