ubuntu20.04 + cuda11.2 + cudnn8 + py3.8.12 + jupyterlab3.1.12 + poetry のコンテナ環境作り方


## やりたいこと
- 新しめな環境一式で、jupyterlab実行したい。
- pythonのパッケージは、poetryで管理したい。

## 環境一式
- 一式を以下に公開していますので、docker-composeでお使いください。
  - https://github.com/nokomoro3/dockerfiles/tree/qiita-2021-09-23/ubuntu20.04-cuda11.2-cudnn8-py3.8.12-jupyterlab3.1.12-poetry

## 解説

### Dockefile側

#### ベースイメージ

```dockerfile
FROM nvidia/cuda:11.2.0-cudnn8-runtime-ubuntu20.04
```

- dockerhubから新しいものを選びました。
  - https://hub.docker.com/r/nvidia/cuda/tags
- CUDAとcudnnは、以下のtensorflowが動くバージョンの組み合わせを選択しています。
  - https://www.tensorflow.org/install/source?hl=ja#gpu

#### Python更新

```dockerfile
#--------------------------
# python3.8.12
#--------------------------
RUN wget https://www.python.org/ftp/python/3.8.12/Python-3.8.12.tar.xz 2>/dev/null 1>/dev/null
RUN tar xJf Python-3.8.12.tar.xz && \
    cd Python-3.8.12 && \
    ./configure && \
    make && \
    make install
RUN apt purge -y python3 python
RUN python3 -m pip install --upgrade pip
RUN cd /usr/local/bin && \
    ln -s python3 python
```
- ベースイメージは、python3.6.9と古いため、python3.8の最新を入れます。（3.9はまだ怖いので入れない）
- makeでビルドするので、不足するツールがある場合は、apt installしてください。

#### JupyterLab

```dockerfile
#--------------------------
# jupyterlab
#--------------------------
RUN pip install jupyterlab==3.1.12
RUN pip install jupyterlab_widgets
RUN pip install ipywidgets
RUN jupyter nbextension enable --py widgetsnbextension
```
- widgetsは、tqdmでプログレスバーを出すために必要です。

#### JupyterLab extentions

```dockerfile
# jupyterlab/toc
RUN apt install -y nodejs npm && npm install n -g && n stable && apt purge -y nodejs npm
RUN jupyter labextension install @jupyterlab/toc

# jupyterlab/git
RUN pip install jupyterlab-git
RUN jupyter labextension install @jupyterlab/git

# tensorboard for jupyterlab3
# ref. https://github.com/chaoleili/jupyterlab_tensorboard/issues/28
RUN pip install git+https://github.com/cliffwoolley/jupyter_tensorboard.git
```
- 拡張機能を入れます。
- jupyterlab/tocは、ipynbで目次を見れるようになります。
- jupyterlab/gitは、git機能です。
- tensorboardは、jupyterlab3で公式ではまだ動かないため、有志のレポジトリをインストールします。
  - https://github.com/chaoleili/jupyterlab_tensorboard/issues/28

#### JupyterLab settings

```dockerfile
# Set up our notebook config.
COPY jupyter_lab_config.py /root/.jupyter/jupyter_lab_config.py
```

- 設定ファイルには、以下を記載します。

```python
import os
from jupyter_server.auth import passwd

c.ServerApp.allow_remote_access = False
c.ServerApp.allow_root = True
c.ServerApp.base_url = "/"
c.ServerApp.iopub_data_rate_limit = 10000000000
c.ServerApp.ip = '*'
c.ServerApp.notebook_dir = '/app/notebooks'
password = "PASSWORD"
c.ServerApp.password = passwd(password)
c.ServerApp.token = ''
c.ServerApp.port = 8888
```

- PASSWORDは適宜変更してください。
- notebook_dirは、jupyter起動時のデフォルトパスを指定します。

#### poetry

```dockerfile
#--------------------------
# poetry
#--------------------------
RUN pip install poetry
RUN poetry config virtualenvs.create false
# RUN poetry config virtualenvs.in-project true

WORKDIR "/app"
COPY ./app/pyproject.toml pyproject.toml
RUN poetry install
RUN python3 -m ipykernel install --user --name=app
```

- `virtualenvs.create false`とすることで、globalにpoetryを使います。
- pyproject.tomlは永続化したいので、後でdocker-composeの時にもマウントします。
- ここでは、`poetry install`までやっておきたいので、いったんコンテナ内に複製します。
- kernelも追加してappとしておきます。デフォルトのkernelは使えないので、appを使ってください。

### docker-compose側

```yaml
version: '2.3'
services:
  jupyterlab:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./app:/app
    ports:
      - 8888:8888
      - 6006:6006
    runtime: nvidia
    restart: always
    tty: true
    networks:
      - jupyterlab-network
networks:
  jupyterlab-network:
    external: false
```
- GPUを使いますので、runtimeはnvidiaにします。
  - ただし、Windows10では、GPUは使えません。
- volumesマウントには、./appをマウントして、./app/pyproject.tomlやnotebookを永続化します。

### poetryによるパッケージ管理

- poetryを使ってパッケージ管理をしています。
- notebook起動後、以下のコマンドでインストールができます。

```ipynb
!poetry add {PACKAGE_NAME}
```

## 発展：フォルダ毎にpoetryでパッケージ管理

### dockerfileの修正

- dockerfileを以下のように修正する必要があります。
- こちらにした場合、defaultのpythonをpoetryでパッケージ管理できなくなりますのでご注意ください。
  - これを両立する回避策が見つかりませんでした…

```yaml
#--------------------------
# poetry
#--------------------------
RUN pip install poetry
# RUN poetry config virtualenvs.create false
RUN poetry config virtualenvs.in-project true

# WORKDIR "/app"
# COPY ./app/pyproject.toml pyproject.toml
# RUN poetry install
# RUN python3 -m ipykernel install --user --name=app
```

### プロジェクト作成 + kernel作成

- コンテナ起動後、execで入ります。

```
# pwd
/app/notebooks
```

- フォルダを作成して、poetry initします。
- 質問には適当に回答します。

```
# mkdir -p sample_project
# cd sample_project/
# poetry init
```

- 以下２つをpoetry addします。(widgetsはtqdmするのに必要)

```
# poetry add ipykernel
# poetry add ipywidgets
```

- venvのkernelをjupyterに追加します。

```
# .venv/bin/python3 -m ipykernel install --user --name=sample_project
Installed kernelspec sample_project in /root/.local/share/jupyter/kernels/sample_project
```

### JupyterLabの画面でkernelを選択

- ブラウザ画面を更新すると、Launcherにsample_projectが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520396/de942a5e-ff8d-bf39-2b93-f817450d8cc6.png)

- これをクリックすれば、sample_projectの環境でnotebookが起動できます。

### パッケージ管理

- poetry addでパッケージ管理できるようになっていますが、<r>
.venvがあるフォルダ内にいる必要があるので、そこだけ注意が必要です。

- poetry addする前に、pwdで自身の位置を確認してください。

```ipynb
!pwd
/app/notebooks/sample_project # この配下ならOK!
```

### kernelの削除

```ipynb
!jupyter kernelspec uninstall sample_project -y
[RemoveKernelSpec] Removed /root/.local/share/jupyter/kernels/g2net
```