VS CodeのDev ContainerでJupyter環境を作る

簡単に使える[Jupyter](https://jupyter.org/)環境があったらいいな、と思ったので、VS CodeのDev Containersを利用して作ってみることにしました。

## Dev Container(s)とは

一言でいうと、Dockerコンテナ上に開発環境を構築して扱うための仕組みのことです。
所定の設定ファイルにコンテナの設定を記述して、Dev Containersに対応したツールを使うと、誰でも簡単に開発環境を立ち上げることができます。

VS Codeの[Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)拡張を使うと、コンテナの自動起動はもちろんのこと、コンテナ内をVS Codeで開いて（接続して）、ローカルでの操作と同じような開発体験が得られます。

### 参考

- [Development containers](https://containers.dev/)
- [Developing inside a Container using Visual Studio Code Remote Development](https://code.visualstudio.com/docs/devcontainers/containers)

## レイアウトとファイルの内容

今回はVS Codeを想定して、次のようなファイルレイアウトで準備します。
（ここでは`jupyter-sandbox`というディレクトリに配置することにしました。）

```
jupyter-sandbox
├── .devcontainer        ← devcontainerの設定ディレクトリ
│  └── devcontainer.json
├── .vscode              ← VS Codeの設定ディレクトリ
│  └── extensions.json
├── compose.yaml         ← Docker Composeファイル
└── work                 ← Jupyterのノートブック置き場ディレクトリ
```

:::note
`compose.yaml`は`.devcontainer`ディレクトリに入れても良かったのですが、Dev Containersを利用せずに`docker compose`単独での運用も考慮して直下に配置してみました。
:::

### `compose.yaml`

`compose.yaml`にはJupyterを起動するための定義を普通に書きます。
この例では`jupyter/scipy-notebook`を使いましたが、お好みで他のバリエーションに変えても結構です。

`volumes`の部分は、`/home/jovyan`を通常のDockerボリュームとしつつも、ノートブックを保存する`/home/jovyan/work`についてはバインドマウントとしました。
これでローカルからもノートブックのファイルを見ることができます。

```yaml:compose.yaml
# nameは$COMPOSE_PROJECT_NAMEの指定
# 指定しないとディレクトリ名が使われます
# name: jupyter-sandbox

services:
  jupyter:
    image: jupyter/scipy-notebook:2023-01-16
    ports:
      - 8888:8888
    environment:
      TZ: Asia/Tokyo
    volumes:
      - jupyter-jovyan:/home/jovyan
      - ./work:/home/jovyan/work

volumes:
  jupyter-jovyan:
```

ちなみに、Dev Containersを使わずに、Docker Composeだけで運用するのもアリだと思います。
ブラウザからJupyterが開ければいい場合はそれでもよいでしょう。

:::note
今どき（？）のComposeファイルについては、この辺を参照してください。

- [Docker Compose Specification](https://compose-spec.io/)
- [Docker Compose V2で変わったdocker-compose.ymlの書き方](https://zenn.dev/miroha/articles/whats-docker-compose-v2)
:::

### `.devcontainer/devcontainer.json`

このファイルでDev Containersの設定を行います。
設定ファイルの形式や項目の詳細については、前述の参考リンクを見ていただくとして、ここではざっくりと次のような設定をしています。

- `dockerComposeFile`と`service`で先程の`compose.yaml`の`jupyter`サービスを使うように指定。
- `workspaceFolder`で`/home/jovyan/work`をワークスペースとするように指定。
- `forwardPorts`で`8888`をポートフォワードするように指定（ブラウザでもアクセスできるようにする）。
- `customizations`の部分はDev Containers対応ツール固有の設定を書くのですが、ここではVS Code用の追加設定を書いています。
    - コンテナ内に導入する機能拡張やPythonインタプリタのパスを指定しています。

```json:.devcontainer/devcontainer.json
{
	"name": "Jupyter SciPy Notebooks",

	"dockerComposeFile": "../compose.yaml",
	"service": "jupyter",
	"workspaceFolder": "/home/jovyan/work",
	
	"forwardPorts": [8888],

	"remoteUser": "jovyan",

	"customizations": {
		"vscode": {
			"settings": { 
				"python.defaultInterpreterPath": "/opt/conda/bin/python"
			},
			
			"extensions": [
				"ms-python.python",
				"ms-python.vscode-pylance"
			]
		}
	}
}
```

今回は設定していませんが、コンテナ起動時に任意のコマンドを実行して環境をセットアップするといったことも可能です。興味があれば調べてみてください。

### `.vscode/extensions.json`

VS Codeの推奨拡張機能にPython用の拡張を設定します。
Dev Containers側に設定を入れたのでローカル側は不要な気もしますが、いちおう設定しておきます。

```json:.vscode/settings.json
{
    "recommendations": [
        "ms-python.python",
        "ms-python.vscode-pylance"
    ]
}
```

## 使い方

上記で作成したディレクトリ（ここでは`jupyter-sandbox`）をVS Codeで開くと、次のようなポップアップが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1809626/eedf4316-7e9c-f2d1-78ba-8a57641e4217.png)

ここで「コンテナーで再度開く」を選択すると、VS CodeがDev Containerを起動して、自動的にコンテナ内のワークスペースを開いてくれます。

:::note
コマンドパレットから`Dev Containers: Reopen in Container`を実行することでも起動できます。

ポップアップが表示されない、消しちゃった、という場合はこちらから起動してみてください。
:::

あとはVS Code上でノートブックのファイルを作成して好きなように使ってください。
今回の設定では`localhost:8888`へのポートフォワードも設定したので、Dev Container起動中はブラウザからもアクセスできます。

ブラウザアクセス用のURLやアクセス用のトークン等は`docker compose logs`等でコンテナのログを確認するとわかります。

### Dev Containerの終了方法

VS Codeのウィンドウを閉じると自動的にDev Containerも終了します。

コンテナ自体は削除されません（`exited`のまま残ります）。
コンテナを削除してしまうと、（バインドマウントしているものは除いて）作業中の内容などが消えてしまうので注意してください。

## さいごに

VS CodeとDev Containersを利用することで簡単にJupyter環境を用意することができました。

私はこれをベースにしてTypeScript, Go, Rust, Kotlin, Rubyのカーネルを追加した環境を使っています。
みなさんも自分独自のDev Container作りにチャレンジしてみると面白いかもしれません。