CentOS7にJupyter Notebookを導入

# 1. 目的

仮想マシン上の__CentOS__に__Jupyter Notebook__を導入する。
加えて，ホストマシンなどリモートマシンからブラウザでアクセスし実行できるようにする。

# 2. 環境

|ホストマシン|構成|
|:--|:--|
|OS | OS X 10.11.6 |
|VitrtualBoxバージョン|5.1.18|

|仮想マシン|構成|
|:--|:-----------------|
|OS| CentOS 7.3 (minimal)|
|メモリ容量|512MB|
|CPUコア数|1コア|
|ストレージ容量|8GB|
|ネットワーク設定|ブリッジアダプター|

VirtualBoxと，CentOSの導入方法は省く。

# 3. 導入

## 3.1. Yumで既存のパッケージを更新

```bash:Yumのアップデート
$ sudo yum update -y
```

## 3.2. Anacondaを用いて必要なものを導入

```bash:Anacondaを導入

# curlでAnacondaを入手
$ curl https://repo.continuum.io/archive/Anaconda3-4.3.1-Linux-x86_64.sh -O
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  474M  100  474M    0     0  3431k      0  0:02:21  0:02:21 --:--:-- 3837k

# bzip2を導入
#   Anacondaの導入に必要
#   minimalだとインストールされていない
$ sudo yum install bzip2 -y

# shファイルを実行
$ bash ./Anaconda3-4.3.1-Linux-x86_64.sh 

# Pythonのバージョンを確認
$ python --version
Python 3.6.0 :: Anaconda 4.3.1 (64-bit)

# Jupyterのバージョンを確認
$ jupyter --version
4.2.1

```

## 3.3. JupyterNotebook の実行

```bash:JupyterNotebookを実行
$ jupyter notebook
```

JupyterNotebookが起動できることを確認する。
しかし，この状態ではリモートマシンからJupyterNotebookにアクセスできない。
よって，外部から接続できるようにコンフィグファイルを編集する。

# 4. リモートから接続できるようにする

## 4.1. configファイルの作成
```bash
# ホームディレクトリにConfigファイルのパスが通っていることを確認
$ jupyter --path

# ホームディレクトリにconfigファイルを作成
$ mkdir ~/.jupyter
$ touch ~/.jupyter/jupyter_notebook_config.py
```

## 4.2. configファイルの編集

```py3:~/.jupyter/jupyter_notebook_config.py

c = get_config()

c.NotebookApp.ip = '*'
c.NotebookApp.open_browser = False
c.NotebookApp.port = 8888

```

- すべてのIPアドレスから接続を受け入れる。
- Jupyterは8888番ポートで起動する。
- JupyterNotebookの起動時にブラウザを立ち上げない。


## 4.3. firewalld の編集

firewalldにルールを追加して，外部から8888番ポートへのアクセスを許可させる。

```bash:8888番ポートを開放
$ sudo firewall-cmd --add-port=8888/tcp --zone=public --permanent
success
$ sudo firewall-cmd --reload
success
$ sudo firewall-cmd --list-all --zone=public
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3 enp0s8
  sources: 
  services: dhcpv6-client http ssh
  ports: 8888/tcp
  protocols: 
  masquerade: no
  forward-ports: 
  sourceports: 
  icmp-blocks: 
  rich rules: 

```

## 4.4. ログインパスワードの設定

### 4.4.1. iPythonで暗号化された文字列を生成

```bash
# iPythonを起動
$ ipython

# 暗号化された文字列を生成するためにライブラリ読み込み
In [1]: from notebook.auth import passwd

# パスワードを入力して暗号化された文字列を生成
In [2]: passwd()
Enter password: 
Verify password: 
Out[2]: 'sha1:(暗号化された文字列)'

# iPythonから抜ける
In [3]:                                                                                                                                               
Do you really want to exit ([y]/n)? y
```

### 4.4.2. 暗号化された文字列をJupyterのコンフィグファイルに書き込む

```py3:~/.jupyter/jupyter_notebook_config.py

c.NotebookApp.password = u'sha1:（4.4.1.で生成した文字列）'

```

## 4.5. Jupyter Notebookを実行

```bash:JupyterNotebookを実行
$ jupyter notebook
```

Jupyter Notebookを起動したら，実行しているマシンに割り当てられているIPアドレスの8888番にアクセスする。

![JupyterNotebook ログイン画面](https://qiita-image-store.s3.amazonaws.com/0/37765/5c6f7ca7-d3ac-a033-4c45-7f8dd1dba884.png)

パスワードの入力を求められるので，先ほど設定したパスワードを入力する。

![JupyterNotebook ホーム画面](https://qiita-image-store.s3.amazonaws.com/0/37765/fce03d1b-2d19-098c-786f-32ea63ffa3dd.png)

ログインに成功すると，ホームディレクトリのファイル一覧が表示される。


# 5. systemd で JupyterNotebook をサービス化する

## 5.1. Unit定義ファイルを作成

```bash
# root になる
$ su root

# Jupyterのパスを確認
\# which jupyter

# Unit定義ファイルを作成
\# touch /etc/systemd/system/notebook.service
```

## 5.2. Unit定義ファイルの編集

```bash
[Unit]
Description = Jupyter Notebook

[Service]
Type=simple
PIDFile=/var/run/jupyter-notebook.pid
ExecStart='Jupyterまでの絶対パス' notebook
WorkingDirectory='Anacondaを導入したユーザーのホームディレクトリのパス'
User='Anacondaを導入したユーザー名'
Group='Anacondaを導入したユーザー名'
Restart=always

[Install]
WantedBy = multi-user.target
```
- __ExecStart__ には 5.1. で確認したJupyterのパスにnotebookを引数に持つコマンドを入力する。
- __WorkingDirectory__ にはAnacondaを導入したユーザーのホームディレクトリのパスを指定する。
- __User__ と __Group__ には，Anacondaを導入したユーザー名を入力する。


## 5.3. 登録されたUnitの一覧を取得

```bash
\# systemctl list-unit-files --type=service

notebook.service                            disabled
```

出力された一覧の中に，先ほど定義したUnitファイルが存在していることを確認。

## 5.4. 起動時に実行するように設定する

```bash
# 起動時にJupyterが自動起動するように設定
\# systemctl enable notebook

# サービスを開始
\# systemctl start notebook

# Jupyterが正しく動いているか確認
\# systemctl status notebook
● notebook.service - Jupyter Notebook
   Loaded: loaded (/etc/systemd/system/notebook.service; enabled; vendor preset: disabled)
   Active: active (running) since 月 2017-03-20 21:27:49 JST; 2s ago

# 再起動してJupyterが起動していることを確認する
\# reboot
```

# 6. SSLの適用

## 6.1. OpenSSLを利用して，SSLサーバー証明書と秘密鍵を作成

自己証明書と秘密鍵の作成にあたって，[Using SSL for encrypted communication](http://jupyter-notebook.readthedocs.io/en/latest/public_server.html#using-ssl-for-encrypted-communication)では，次のコマンドが例として挙げられている。

```bash
$ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mykey.key -out mycert.pem
Generating a 1024 bit RSA private key
```

OpenSSLコマンドの引数の意味については，[CentOS7.2 64bit OpenSSLを使用して秘密鍵、CSRを作成し、自己署名のSSLサーバー証明書を作成](http://www.kakiro-web.com/linux/ssl.html)の「自己署名のSSLサーバー証明書を作成」を読むと分かる。

上のコマンドの場合

- 秘密鍵作成とCSR作成を一度にする
- x509という証明書の規格
- 証明書の有効期限は365日

ということがわかる。

## 6.2. configファイルの編集

6.1.で作成した証明書と秘密鍵を，_~/.jupyter_フォルダに配置しておく。

```bash:自己証明書と秘密鍵ファイルの移動
$ mv mycert.pem ~/.jupyter/
$ mv mykey.key ~/.jupyter/
```

configファイルに証明書と秘密鍵の場所を編集する。

```py3:~/.jupyter/jupyter_notebook_config.py
c.NotebookApp.certfile = u'${ホームディレクトリへの絶対パス}/.jupyter/mycert.pem'
c.NotebookApp.keyfile = u'${ホームディレクトリへの絶対パス}/.jupyter/mykey.key'
```

## 6.3. サービスを再起動してHTTPSでアクセスする

```bash
$ sudo systemctl restart notebook

$ sudo systemctl status notebook
● notebook.service - Jupyter Notebook
   Loaded: loaded (/etc/systemd/system/notebook.service; enabled; vendor preset: disabled)
   Active: active (running) since 月 2017-03-20 22:15:08 JST; 6s ago
```

# 7. デフォルトの作業ディレクトリを変更する

現在，デフォルトでホームディレクトリが指定されている Jupyter Notebook の作業ディレクトリを，別の場所に変更する。

## 7.1. config ファイルの編集

```bash:ホームディレクトリ直下にJupyter用のディレクトリを作成する
$ mkdir ~/jupyter_files
```

config ファイルを編集する。

```py3:~/.jupyter/jupyter_notebook_config.py 
c.NotebookApp.notebook_dir = u'${先に作成したディレクトリへの絶対パス}'
```

```bash:JupyterNotebookを再起動
$ sudo systemctl restart notebook
```

## 7.2. 変更を確認

Jupyter Notebook にアクセスする。
先ほどまで表示されていた，"anaconda3"ディレクトリが表示されなくなっていることがわかる。

![Notebook Home画面](https://qiita-image-store.s3.amazonaws.com/0/37765/9dcbce62-73fc-4304-a8ca-ad0172c444e4.png)

ターミナルで先ほど指定した作業ディレクトリにファイルを作成すると，jupyter notebookでも変更が確認できる。

```bash
$ cd ~/jupyter_files/
$ touch TEST_FILE.py
```

![notebook home画面](https://qiita-image-store.s3.amazonaws.com/0/37765/4c47d292-6899-d212-10ef-b254e039cabf.png)

# 8. 参考WEBサイト
- [Jupyter Running the Notebook](https://jupyter.readthedocs.io/en/latest/running.html#running)
- [Config file and command line options](http://jupyter-notebook.readthedocs.io/en/latest/config.html#options)
- [Using SSL for encrypted communication](http://jupyter-notebook.readthedocs.io/en/latest/public_server.html#using-ssl-for-encrypted-communication)
- [IPython notebookをリモート接続する](http://qiita.com/tdrk/items/e6c293021cbcb0e96cd2)
- [[メモ] らずぱいにJupyter notebook](http://qiita.com/mt08/items/edb9bcaeaa416d3e5319)
- [CentOS7でポートを開放する方法。](http://senoway.hatenablog.com/entry/2015/02/11/142139)
- [Systemdを使ってさくっと自作コマンドをサービス化してみる](http://qiita.com/DQNEO/items/0b5d0bc5d3cf407cb7ff)
- [OpenSSLコマンドの備忘録](http://qiita.com/takech9203/items/5206f8e2572e95209bbc)
- [OpenSSLコマンドによる公開鍵暗号、電子署名の方法](http://qiita.com/kunichiko/items/3c0b1a2915e9dacbd4c1)