Python3.7.3 と jupyterLab を入れたコンテナの作成

※2019/05/14
Dockerfileで記述ミス・記述漏れがあったため修正。

## 作った目的
以前、「[Python3.7.1 と jupyter notebook を入れたコンテナの作成](https://qiita.com/yasthon/items/2f1c2d485ecaf5a20796)」という記事を載せていましたが、Python3.7.3が出ていたため作り直しました。
[CVE-2019-9947](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9947)と[CVE-2019-9948](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9948)の対応済みでもあるし良い機会かと。
ついでに軽量化目的でイメージを変更しています。

## 環境
- CentOS Linux release 7.6.1810（ホストOSです）
- Docker version 18.09.3
- docker-compose version 1.23.2
- Python 3.7.3（コンテナの中）
- jupyter-lab 0.35.4（コンテナの中）

## 軽量化について
以前は、CentOSのイメージを使用して、1.31GB とヘビーでした。
今回はイメージを**python:3.7.3-slim-stretch**にして、734MBになりました。

DockerHubに[pythonイメージ](https://hub.docker.com/_/python)があり、特に**python:3.7.3-alpine3.9**は軽量化にかなり貢献できると考えていました。
実際に作ってみると、Pythonモジュールのインストールで、'' not found '' の嵐で諦めました。

最終的に、**python:3.7.3-slim-stretch**で落ち着きました。
ちなみに、**python:3.7.3-slim-stretch**のイメージサイズは143MBです。

## Dockerのインストール
Dockerのインストールは、[以前の記事](https://qiita.com/yasthon/items/2f1c2d485ecaf5a20796)や[数多くある素晴らしい記事](https://www.google.com/search?q=docker%20インストール%20centos7)を参考にしてください。

## ファイル構成
以下の５つのファイルを同じディレクトリに置きます。

- Dockerfile
- docker-compose.yml
- jupyter_notebook_config.py
- requirements.txt
- .env

ファイルを置いたディレクトリで`docker-compose`コマンドを実行します。


## Dockerfile

```dockerfile:Dockerfile
FROM python:3.7.3-slim-stretch

ENV PYTHONPATH "/opt/python/library"

COPY requirements.txt /tmp/requirements.txt

RUN set -x && \
    pip install -U pip && \
    pip install -r /tmp/requirements.txt && \
    mkdir -p /opt/python/library && \
    mkdir -p /opt/python/jupyter && \
    mkdir -p /opt/python/jupyterlab && \
    mkdir ~/.jupyter && \
    rm /tmp/requirements.txt

COPY ./jupyter_notebook_config.py /root/.jupyter/jupyter_notebook_config.py

EXPOSE 8888
CMD ["jupyter", "lab", "--allow-root"]
```

## docker-compose.yml

```json-doc:docker-compose.yml
version: '3'

services:
  python:
    build:
      context: "./"
      dockerfile: "Dockerfile"
    image: ${TAG}
    hostname: ${TAG}
    container_name: ${TAG}
    ports:
      - "8888:8888"
    volumes:
      - /opt/${TAG}/jupyter:/opt/python/jupyter
      - /opt/${TAG}/jupyterlab:/opt/python/jupyterlab
      - /opt/${TAG}/library:/opt/python/library
    logging:
      driver: "journald"
```

## jupyter_notebook_config.py

```python:jupyter_notebook_config.py
c = get_config()
c.NotebookApp.ip = '0.0.0.0'
c.NotebookApp.open_browser = False
c.NotebookApp.port = 8888
c.NotebookApp.notebook_dir = '/opt/python/jupyter'
c.LabApp.user_settings_dir = '/opt/python/jupyterlab/lab/user-settings'
c.LabApp.workspaces_dir = '/opt/python/jupyterlab/lab/workspaces'
c.NotebookApp.password = u'sha1:63cae364b3cd:c4319cba1eeb1bcf011a7d3fabd6448f95ae18c5'
```

c.NotebookApp.password に設定したパスワードは「password」のhash値です。

パスワードを変更する場合は、**コンテナ起動後に**以下のコマンドを実行し、パスワード入力後に出力されるhash値に変更します。
コンテナ名を変更した場合は、コマンドも修正します。

```
$ docker container exec -it python373 bash
[root@python373 /]# python -c 'from notebook.auth import passwd;print(passwd())'
Enter password:
Verify password:
sha1:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  #これに変更
[root@python373 /]# sed -i -e "/^c\.NotebookApp\.password/s/\(sha1:\).*/\1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\'/" ~/.jupyter/jupyter_notebook_config.py 
[root@python373 /]# exit
$ docker-compose restart
```
コンテナを削除すると、パスワードも消えます。
必要であれば、commitして新しいイメージを作成します。

## requirements.txt
コンテナ内にインストールされるpythonのモジュールです。

```bash:requirements.txt
jupyterlab==0.35.4
matplotlib==3.0.3
numpy==1.16.2
pandas==0.24.2
plotly==3.7.1
seaborn==0.9.0
```

## .env
このファイルのTAGが、docker-compose.yml内の${TAG}に展開されます。

```bash:.env
TAG=python373
```

## コンテナ作成
    $ docker-compose build

## コンテナ起動
    $ docker-compose up -d

## コンテナ停止と削除
    $ docker-compose down

## jupyterへのアクセス
ブラウザから以下へアクセスします。

    http://サーバのIPアドレス:8888/

パスワードは、「password」です。
変更した場合は、変更したパスワードを入力します。

## ボリュームの話
ホストの`/opt/python373/jupyter`をコンテナ内の`/opt/python/jupyter`にマウントしています。
jupyterで入力したコマンドは、ホストのボリュームに保存されます。

ホストの`/opt/python373/jupyterlab`をコンテナ内の`/opt/python/jupyterlab`にマウントしています。
jupyterlabの設定（たとえばテーマをdrakに変更）は、ホストのボリュームに保存されます。

また、ホストの`/opt/python373/library`をコンテナの`/opt/python/library`にマウントしており、パスを通しています。
そのため、次の手順でライブラリを追加できます。

## ライブラリの追加方法
ライブラリを追加する場合は、ホストで以下のコマンドを実行します。（コンテナは起動したままですよ）
以下の例では、simplejsonライブラリを追加しています。

    $ docker container exec -t python373 pip install simplejson -t /opt/python/library

## 最後に
この環境を作って一週間ほど起動させて使っていますが、特に問題は起きていません。
scikit_learnやtensorflowも追加できています。（私にスキルがないため追加しただけです。またtensorflowは単体でDockerイメージがあるので微妙ですが・・・）
これからPythonならびに機械学習・深層学習の勉強をガンガン進めていければ・・・いいなぁ。