Docker for MacでDataScienceな環境を作る

データサイエンスの学習のためにデータサイエンスができるDockerコンテナを作成します。いろんなモジュール入れたり消したり試行錯誤できるようにコンテナを使ってみたいという意図です。Docker for Macのインストールは簡単だったので省略します。

# 環境情報
MacBook Air (13-inch, Mid 2011)
macOS High Sierra (v10.13.6)
Docker for Macのバージョンは以下の通り
![スクリーンショット 2018-08-05 23.18.41.png](https://qiita-image-store.s3.amazonaws.com/0/200471/be9c1fec-29be-f8bc-81cf-7669a9f55f1b.png)


# 作成するコンテナの概要
- OS
    - ubuntu16.04
- インストールするソフトウェア
    - vim(途中ファイル編集が必要なため)
    - Python3
    - その他Pythonのモジュールをいくつか

# データサイエンスなDockerコンテナを作成

## 元となるコンテナを作成
まずはubuntu16.04のコンテナを作成します。

```zsh
docker run -it --name datascience -p 8888:8888 ubuntu:16.04 /bin/bash
```

この`docker run`コマンドについて説明します。参考に`docker run`のヘルプから使ってる部分を抜粋したのが以下です。

```zsh
% docker run --help
Usage :	docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
〜中略〜
  -i, --interactive                    Keep STDIN open even if not attached
  -t, --tty                            Allocate a pseudo-TTY
〜後略〜
```

- `-it`
    - インタラクティブにコンテナとやりたいするためのオプションです。
- `--name datascience`
    - コンテナの名前を「datascience」に指定しています。
    - 好きな名前でOKです。
- `-p 8888:8888`
    - ホストの8888ポートに来たパケットをコンテナの8888に流す設定です。<br>[host-port]:[container-port]の順番に指定します。
- `ubuntu:16.04`
    - 元となるイメージを指定します
    - dockerhubにあるubuntu16.04のイメージを使いました
- `/bin/bash`
    - コンテナ起動時に実行するコマンドを指定します
    - /bin/bashとすることでbashが起動します

## コンテナ内に必要なものをインストール
最低限必要そうなものをインストールしていきます。特段説明はしません。

```bash
# repositoryの情報をupdate
$ apt-get update

# vimとpythonとpipをインストール
$ apt-get -y install vim python3 python3-pip

# datascienceに必要なものをインストール
$ pip3 install numpy pandas jupyter matplotlib scikit-learn seaborn
```

## JupyterNotebookの設定

まずはコンフィグファイルを作成して二箇所編集します。

```bash
# コンフィグファイルを作成
$ jupyter notebook --generate-config
# コンフィグファイルを編集
$ vim ~/.jupyter/jupyter_notebook_config.py
```

編集内容は以下の通り。
- `c.NotebookApp.ip`の値を`'localhost'`から`'0.0.0.0'`に変更してアンコメント。
- `c.NotebookApp.allow_root`の値を`False`から`True`に変更してアンコメント。

```python:.jupyter/jupyter_notebook_config.py
〜略〜
## The IP address the notebook server will listen on.
c.NotebookApp.ip = '0.0.0.0'
〜略〜
## Whether to allow the user to run the notebook as root.
c.NotebookApp.allow_root = True
〜略〜
```

パスワードを設定するのは以下のコマンドでOK。

```bash
$ jupyter notebook password
Enter password: 
Verify password: 
```

JupyterNotebookを起動します。ルートディレクトリで実行するとファイル全部丸見えで怖いので「/datascience」というディレクトリを作成してそこで実行することにしています。

```bash
# /datascienceディレクトリを作成
$ mkdir /datascience
# /datascienceに移動
$ cd datascience
# jupyternotebokを起動
$ jupyter notebook
```

ブラウザで「http://localhost:8888」にアクセスして以下の画面が表示されればOK。
![スクリーンショット 2018-08-05 22.39.44.png](https://qiita-image-store.s3.amazonaws.com/0/200471/da2554f3-6e69-ffe1-9b71-b53b44a776ac.png)

設定したパスワードでログインできれば以下のような画面になります。
![スクリーンショット 2018-08-05 22.40.39.png](https://qiita-image-store.s3.amazonaws.com/0/200471/1a76d32c-6a1d-364b-b488-df22a397c566.png)

# 作ったコンテナをイメージにまとめる

晴れてデータサイエンスなコンテナが作成できました。次は、作ったコンテナをイメージとして固めておきます。まず`ctrl + p`→`ctrl + q`と押してコンテナからデタッチしてホストのプロンプトに戻ります。この時exitしてしまうとコンテナがstopします。走ってるプロセス(この場合は/bin/bash)がなくなってしまうためです。もしexitしてしまった場合は、以下のコマンドで再度起動させてあげてください。

```zsh
docker start -it datascience /bin/bash
```

それではひとまずコンテナの状態を確認しましょう。

```zsh
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
8f4d3a546210        ubuntu:16.04        "/bin/bash"         21 minutes ago      Up 21 minutes       0.0.0.0:8888->8888/tcp   datascience
```

CONTAINER IDの「8f4d3a546210」は違う値になってると思いますが、STATUSがUPになっており元気よくdatascienceコンテナが動いてくれているようです。ではDockerイメージに固めていきましょう。

```zsh
% docker commit --change='CMD bash -c "cd /datascience && jupyter notebook"' datascience admin/datascience
```

本コマンドのオプションと引数について説明して行きます。参考に`docker commit`コマンドのヘルプは以下の通り。

```
% docker commit --help

Usage:	docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

Create a new image from a container's changes

Options:
  -a, --author string    Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
  -c, --change list      Apply Dockerfile instruction to the created image
  -m, --message string   Commit message
  -p, --pause            Pause container during commit (default true)
```

- `--change='CMD bash -c "cd /datascience && jupyter notebook"'`
    - commitするときにDockerfileへの追記などができるオプションです。
    - CMDはrun/start時に実行されるコマンドを指定する部分です。
    - `/datascience`ディレクトリに移動してjupyter notebookを起動するようにしています。
    - こうしておけばコンテナを立ち上げるだけでjupyter notebookが使える状態になります。
- `datascience`
    - コンテナを指定する部分です
    - 今回はコンテナ名で指定していますが、コンテナIDでも指定可能です。
- `admin/datascience`
    - Dockerイメージの名前です。
    - dockerhubでは[repo_name]/[image_name]という表記をよく使うのでそんなイメージで描きました。
    - イメージ名にコンテナ名と同じdatascienceを使いましたが、同じである必要はありません。

# 作ったイメージからコンテナを立ち上げてみる
これで好きなだけデータサイエンスなコンテナを立ち上げる下地が整いました。試しにコンテナを立ち上げてみましょう。

```
% docker run -d --name datascience2 -p 8887:8888 admin/datascience 
```

ではコマンドの説明です。

- `-d`
    - コンテナを立ち上げた後すぐデタッチするためのオプションです。
    - これを入れていない場合はjupyter notebookが立ちあがった状態で止まりますのでctrl+p→ctrl+qで抜けてください。
- `--name datascience2`
    - `datascience`はすでに使ってあるので別の名前を設定します。
- `-p 8887:8888`
    - ポート番号の指定ですね。
    - すでにホスト側の8888は使ってるので8887を指定しました。
- `admin/datascience `
    - 先ほど作成してコンテナイメージです。

この記事の初めに元となるコンテナを作ったときは、最後に`/bin/bash`をつけましたが、今回は何も与えていません。こうすることでDockerFileに記述されているCMDの部分が実行されます。`commit`の`--change`で指定した部分です。

`docker ps`をでdatascience2の実行コマンドを確認してみましょう。

```
% docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
4cb7dfaf55a8        admin/datascience   "/bin/sh -c 'bash -c…"   3 minutes ago       Up 3 minutes        0.0.0.0:8887->8888/tcp   datascience2
8f4d3a546210        ubuntu:16.04        "/bin/bash"              36 minutes ago      Up 36 minutes       0.0.0.0:8888->8888/tcp   datascience
```

切れちゃっててわかりづらいので`--no-trunc`オプションをつけて確認しましょう

```
CONTAINER ID                                                       IMAGE               COMMAND                                                          CREATED             STATUS              PORTS                    NAMES
4cb7dfaf55a8125306eb3e22a3e5ca73f6cdc76947ac974180aa2dd6d32d6847   admin/datascience   "/bin/sh -c 'bash -c \"cd /datascience && jupyter notebook\"'"   5 minutes ago       Up 4 minutes        0.0.0.0:8887->8888/tcp   datascience2
8f4d3a54621096a96cfe8901111e520892d255127023bf3886ea127250c6879a   ubuntu:16.04        "/bin/bash"                                                      38 minutes ago      Up 38 minutes       0.0.0.0:8888->8888/tcp   datascience
```

`/bin/sh -c 'bash -c \"cd /datascience && jupyter notebook\"'`がCOMMANDとして実行されているのがわかります。

# pythonモジュールの追加インストール

最低限のものは入れたつもりですが、他にも欲しくなるかもしれません。その場合は２つやり方があります。

## execで入ってpipでインストールする

`docker exec`コマンドを使えばコンテナにログインでき流ので、`pip3`コマンドで適宜インストールしましょう。

```
docker exec -it datascience2 /bin/bash
```

## JupyterNotebook上でインストールする

JupyterNotebookでは先頭に「`!`」をつけることで外部コマンドの実行が可能です。
![スクリーンショット 2018-08-05 23.05.35.png](https://qiita-image-store.s3.amazonaws.com/0/200471/7432dc28-b9ac-5c3c-23fa-ce960a6079e0.png)

# 終わりに
自分の備忘録、勉強のためにまとめました。もし理解が間違っているところやアドバイスなどあればいただけるとありがたいです。

# 追記
pandasとsklearnをimportしたらwarningが出てきた。

```:pandasをimportした時
import pandas
/usr/lib/python3.5/importlib/_bootstrap.py:222: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
  return f(*args, **kwds)
/usr/lib/python3.5/importlib/_bootstrap.py:222: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
  return f(*args, **kwds)
```

```:sklearnをimportした時
import sklearn
/usr/lib/python3.5/importlib/_bootstrap.py:222: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
  return f(*args, **kwds)
/usr/lib/python3.5/importlib/_bootstrap.py:222: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
  return f(*args, **kwds)
/usr/lib/python3.5/importlib/_bootstrap.py:222: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
  return f(*args, **kwds)
```


[ここ](https://github.com/ContinuumIO/anaconda-issues/issues/6678#issuecomment-337279157)を見る限り無視しても良さそうですがなんで出たんだろう。。。

> This warning (not an error) is safe to ignore.