Dockerコンテナを利用したPython,Jupyterの環境構築とVScodeとのリモート接続


授業やSIGNATEのコンペでPythonを使うので、環境構築でDockerを使ってみようという試みです。  
ここ半年間はPyCharmをローカル環境で利用しましたが、動作が少し重いと感じていました。また、機械学習する際に実機のリソースをあまり消費したくありませんでした。そこで、より軽量で、カスタムしやすい環境を目指しました。  
さらに、補完能力や機動性が高く、拡張機能の多いVScodeをリモートの接続先でも利用できるようにし、コーディングできるようにしました。
内容としてはn番煎じかもしれませんが、どこかで役に立てたら嬉しいです。

【追記】
2023/9/28
「【Python】Qiita 週間いいね数ランキング」にランクインしました！ありがとうございます。

https://qiita.com/koki_develop/items/9d7f2ffeafb36cf59a77#_reference-eb3308def93a62a9e65c

# 参考用レポジトリ

https://github.com/sumiyoshi01/PyLearning

# 目標
ローカル環境を汚さずに、コンテナ上で環境構築を完結させる。  
VScodeとコンテナをリモート接続し、コンテナ上でVScodeを利用する。  

# 動作環境
* OS:Windows11  
* Docker Desktop:4.23.0
* Docker:24.0.6
* VScode:1.82.2


# 下準備
以下の順番に従って下準備を進めていきます。
1. Docker Desktopのインストール  
2. Anaconda環境のイメージをダウンロードする
3. Jupyterが直接アクセスするディレクトリ(またプロジェクトファイル)を作成

※VScodeは既にインストールしているものとします。

## 1.Docker Desktopのインストール
[Docker Desktop](https://www.docker.com/products/docker-desktop/)のページからインストールする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/f04fff65-dfb5-ac98-2aef-5470785db2bd.png)
Download for (該当OS)のボタンからインストーラーのダウンロードを開始し、インストーラーを開き、案内に従ってインストールを進めます。
私の環境では、なにか特別に設定をカスタムすることはありませんでした。

## 2.Anaconda環境のイメージをダウンロード(pull)する
今回はAnaconda公式のDockerイメージをダウンロードする。
[continuumio/anaconda3 - Docker Hub](https://hub.docker.com/r/continuumio/anaconda3)
ダウンロードするときは以下のコマンドで行うことができる。
`docker pull continuumio/anaconda3`
もちろんPullボタンからダウンロードすることもできる。(おそらく最新版をダウンロードする)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/00a6556e-c51e-e9ac-4e0f-3913d1543c78.png)

ダウンロードするとImagesに追加される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/f527fe01-e959-0394-07d5-7cf3606b7a81.png)


## 3.Jupyterが直接アクセスするディレクトリ(またプロジェクトファイル)を作成
ローカルにあるどのファイルをJupyterで開くかを決める。また、Jupyter Notebook サーバーがホストするノートブックのディレクトリまたはパスを指定する。言い換えれば、Jupyter Notebook サーバーが起動する際の作業ディレクトリを指定する。このディレクトリには、ユーザーがアクセスしたり、作成したりできるノートブックが含まれる。

* マウント
    ファイルシステムの一部をオペレーティングシステムに接続するプロセスを指す。これによって、ユーザーやアプリケーションは、そのファイルシステムのコンテンツにアクセスできるようになる。コンテナ技術では、ホストシステムのディレクトリやファイルをコンテナ内にマウントすることができる。これにより、コンテナ内のアプリケーションがホストのファイルやディレクトリにアクセスできるようになる。
平たく言うと、コンテナ技術においてはコンテナ内のアプリケーションとホストマシンのファイルシステム間で、データの共有や交換を可能にするということである。  

今回は、ホストマシン上の`C:\hoge\huga\PyLearning`にJupyterをアクセスさせることにした。ホストマシン上にディレクトリを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/387827dc-5781-1d15-3aa9-05a5e3af525b.png)

コンテナ内のJupyter用のディレクトリは後ほどコマンドでまとめて指定する。

# 手順
以下の手順に従ってPythonとJupyterの仮想環境を構築する。
1. コンテナとJupyterを起動する
2. アクセスを確認する
下準備さえ終われば、後はコマンドを実行してコンテナを立ち上げるのみである。

## 1.コンテナとJupyterを起動する
まず、プロジェクトファイルのあるディレクトリに移動する。今回は、先ほど作成したファイルをプロジェクトファイルとする。
```cmd
C:\hoge\huga>cd PyLearning
```
移動したらカレントディレクトリが以下のようになる。
`C:\hoge\huga\PyLearning>`
そのまま以下のコマンドを実行する。
```cmd
docker run -v "%cd%":/notebook02 ^
-p 8889:8888 --rm -it continuumio/anaconda3:latest ^
jupyter notebook --ip 0.0.0.0 --allow-root --no-browser ^
--NotebookApp.disable_check_xsrf=True --NotebookApp.token='' ^
--NotebookApp.password='' /notebook02
```
コンテナが立ち上がり、Jupyterが起動する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/6554c990-9fe6-bdb4-3174-7e4224d0e17a.png)

コンテナが立ち上がっていることはDocker Desktopからも確認することができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/580cdbeb-6ac4-336d-b298-65cb77120074.png)

コマンドがいきなり出てきて困惑していると思うので各オプションと意味を説明する。

:::note info
コマンドの意味
1. `docker run`
このコマンドは、新しいDockerコンテナを起動する。

2. `-v "%cd%":/notebook02`
このオプションは、ホストマシンのカレントディレクトリ（%cd%はWindowsのカレントディレクトリを示す）を、コンテナ内の/notebook02ディレクトリにマウントする。これにより、コンテナ内のアプリケーションは、ホストマシンのカレントディレクトリにアクセスできるようになる。

3. `-p 8889:8888`
このオプションは、ホストマシンの8889ポートを、コンテナ内の8888ポートにマッピングする。これにより、ユーザーはホストマシン上で[http://localhost:8889](http://localhost:8889)にアクセスすることで、コンテナ内で実行されているJupyter Notebookにアクセスできる。

4. `--rm`
このオプションは、コンテナが終了した時に自動的にコンテナを削除する。

5. `-it`
このオプションは、コンテナをインタラクティブモードで起動し、ターミナルをアタッチする。

6. `continuumio/anaconda3:latest`
これは、使用するDockerイメージを指定する。ここでは、continuumio/anaconda3のlatestタグのイメージが使用される。これにはAnaconda3がプリインストールされている。

7. `jupyter notebook --ip 0.0.0.0 --allow-root --no-browser`
これは、コンテナ内で実行されるコマンドを指定する。ここでは、Jupyter Notebookが0.0.0.0のIPアドレスで、root権限で、ブラウザなしで起動される。

8. `--NotebookApp.disable_check_xsrf=True --NotebookApp.token='' --NotebookApp.password=''`
これらのオプションは、Jupyter Notebookのセキュリティ設定を調整する。XSRFチェックを無効にし、トークンとパスワードを空に設定する。これにより、認証なしでJupyter Notebookにアクセスできる。

9. `/notebook02`
最後に、Jupyter Notebookがホストするノートブックのディレクトリとして/notebook02を指定する。これは前述の-v "%cd%":/notebook02オプションでホストマシンのカレントディレクトリにマッピングされている。

:::

## 2.アクセスを確認する
今回は8889番ポートを指定したので以下のリンクを開く。本来は8888番ポートを指定し、アクセスする。
[http://localhost:8889/](http://localhost:8889/)
以下のようなページが開かれたら成功している。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/2d6f5e03-256f-6908-db81-292872da22f2.png)


# VScodeと接続
環境構築が完了したら、さっそくコーディング…!と行きたいところであるが、どうやってファイルを操作したりコーディングするの？となると思います。
そこで、今回はVScodeの拡張機能を利用したリモート接続によって、コンテナと接続して直接コーディングやデバッグを行えるようにしていきます。

## Dev Containersのインストール
まず、VScodeの拡張機能であるDev Containersをインストールする。拡張機能はエディタの左側の四角の集合体のアイコンからアクセスできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/bc6922b8-b90d-77bf-e8a3-a1ab8f9a4cfb.png)

## リモート接続
新しいウィンドウの状態で、左下の><のマークをクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/c89a31b4-4161-33b1-ed36-4327a1878280.png)
するとメニューが出てくるので、`Attach to Running Container`をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/0a1c882e-3a17-81d4-1daf-5ecd5468bb54.png)
先ほどのコンテナの名前を参考にして、適切なものを選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/dfa4526a-c30b-ea44-a4be-81396300f6f0.png)
選択すると、><の部分が、コンテナとリモート接続できていることを表示する。それが確認出来たら、左上のハンバーガーメニューをクリックし、`Open Folder`を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/d26a5458-814c-d4b2-31ff-7b6eb5a451a8.png)
すると、先ほど設定したJupyter用のディレクトリがあるので、それを開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/853b699c-061e-a50e-9c65-ce3c2e51e321.png)
これで、コンテナ内の作業ディレクトリに移動できたことになる。
以下の図では、右側がコンテナ内のエディタで、左側がローカル(ホストマシン上)のエディタとなっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/5b388c0e-3a0f-5706-6323-c2b0419cc284.png)
試しに、K-meansを用いたクラスタの機械学習のPythonコードをコンテナ内で実行してみた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/2af044aa-5310-37f6-58b7-8067ad7da14d.png)
デバッグも行うことができる。まずブレークポイントを設定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/42d17d16-5685-dfba-2118-792709c17877.png)
Xの中身を知りたいので`View Value in Data Viewer`を用いて確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/b379af6d-65bd-2feb-841c-987e98221f0f.png)
すると以下のようにXの配列の中身を確認することができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/4d6e6027-615d-cdc2-c767-ec6fda1b2f2d.png)
必要なライブラリは、コンテナ内のターミナルでインストールすることができる。例えば、opencvをインストールして利用したいときは以下のコマンドを実行する。
```cmd
pip install opencv-python-headless
```
これでインポートが可能になる。
さらに、.ipynb(Jupyterのファイル)もコンテナ内で実行することができる。(別途VScodeのJupyter用拡張機能のインストールが必要)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/96e3e54f-2431-5152-9700-01679c91744d.png)

# おわりに
Dockerは1つのイメージをインストールすれば、いくつでもコンテナを起動することができる優れものです。OSに影響を受けない点も強みであると言えます。
また、軽量であることも相まって、とても重宝しそうです^^
最初にコンテナ化の概念を理解する部分は少し難しいですが、どういう仕組みで何が行われているかがわかれば、わりと合理的な手法であることがわかってきます。
とはいえ、私自身も完全に理解しているわけではないので、様々なことに挑戦しながら理解できたことを共有していけたらと思っています。