Vagrant による EC2 のプロビジョニング + Jupyter（IPython Notebook）on Docker

# 概要
（前半）Vagrantを利用してのEC2のプロビジョニング
（後半）Dockerを利用してのJupyter（IPython Notebook）の起動

初めてQiitaに書かせていただきます。
正月休みに[ゼロから学ぶディープラーニング](https://www.oreilly.co.jp/books/9784873117584/)と、あわよくばTensorFlowを勉強しよう、せっかくなら環境構築も勉強しよう……と試してみたので、備忘録兼ねての紹介です。
今回前後半は独立しているので片方のみの参照も可能かと思います。


### :)
* EC2なのでスペックの上げ下げが簡単
* VagrantでEC2のStart/Stopが簡単に -> 節約に  
  作業後の落とし忘れミスを最小限にしてくれます。
* Dockerで可搬性向上  
  ここは勉強兼ね。データ分析環境をあちこちに立てるのに便利、という印象。

### :(
* EC2にお金がかかる
* Dockerを噛ませる意味合いが薄い  
  書籍の勉強のように最初から環境決め打ちなら、Ansibleなどでシンプルかつ全体を通しで、環境構築できる気がします。  
  

# 構築手順  
前提として

* Vagrantのインストール
* EC2の起動権を持つIAMユーザーのクレデンシャルの設定
* EC2を起動するVPCやサブネットの設定

が必要です。

こちらが参考になるかと思います。

* Vagrantのインストール
  * [INSTALLING VAGRANT](https://www.vagrantup.com/docs/installation/)
  * [Vagrant を Mac OS X にインストールする手順](http://weblabo.oscasierra.net/install-vagrant-onto-macosx/)
* クレデンシャルの設定
  * [Deploying a Development Environment in Amazon EC2 Using the AWS Command Line Interface](http://docs.aws.amazon.com/ja_jp/cli/latest/userguide/tutorial-ec2-ubuntu.html)
  * [AWS CLIで複数アカウントのアクセスキーを管理して扱う設定](http://qiita.com/kwsmkn/items/ce72d8e4cc35f1fc01b5)

## （前半）Vagrantを利用してのEC2のプロビジョニング
HashiCorpのGitHubページ: [Vagrant AWS Provider](https://github.com/mitchellh/vagrant-aws) を参考に設定を行います。

まずAWSを利用するためのプラグインをインストールします。

```
$ vagrant plugin install vagrant-aws
```

マシンイメージに相当するBOXですが、EC2用にダミーボックスが公開されているのでこちらを利用します。  
（公式だとbox名がdummyですが、少し曖昧なのでec2と変えています。）

```
$ vagrant box add ec2 https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box
```

次に作業用のディレクトリへ移動し、Vagrantfileの作成を行います。

```
$ mkdir jupyter_ec2
$ cd jupyter_ec2
$ vim Vagrantfile
```

Vagrantfile を編集し、各種パラメータを設定します。今回はあらかじめ作成したVPCでの起動を指定しています。  

他にもかなりの数のパラメータがあるようで、指定したい場合は適宜READMEや、[AWS APIドキュメント](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/APIReference/Welcome.html)を参照するのが良いかと思います。

```ruby:Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "ec2" # dummy box name

  config.vm.provider :aws do |aws, override|
    # AWSクレデンシャル
    aws.aws_dir = ENV['HOME'] + "/.aws/"
    aws.aws_profile = "YOUR_PROFILE"
    aws.keypair_name = "YOUR_AWS_ACCESS_KEY"
    
    # SSHクレデンシャル
    override.ssh.username = "ec2-user"
    override.ssh.private_key_path = "SSH_KEY_PATH"

    # VPC設定
    aws.region = 'ap-northeast-1'
    aws.availability_zone = 'ap-northeast-1a'
    aws.subnet_id = 'subnet-XXXXXXXX'
    aws.security_groups = 'sg-XXXXXXXX'

    # EC2設定
    aws.ami = "ami-9f0c67f8" # Amazon Linux 2016.09.01
    aws.instance_type = 'm4.large'
    aws.elastic_ip = true
    aws.associate_public_ip = true
    aws.tags = { 'Name' => 'NAME_TAGS_VALUE' }

    # EBS設定
    aws.block_device_mapping = [
      {
        'DeviceName' => "/dev/xvda",
        'Ebs.VolumeSize' => 50,
        'Ebs.DeleteOnTermination' => true,
        'Ebs.VolumeType' => 'standard',
      }
    ]
  end
end
```

編集終了後、EC2の起動を行います。

```
$ vagrant up --provider=aws
```

うまくいかないときですが、

```
$ export VAGRANT_LOG=DEBUG
```

としたのち`vagrant up`を再実行すると詳細なエラーメッセージを表示してくれるようになります。
> エラーメッセージを元に戻したいときは`export VAGRANT_LOG=WARN
`

セキュリティグループのSSHもきちんと開けているか注意しましょう。上限緩和していない場合EIP数上限も引っかかりがちです。

起動し終わったら

```
$ vagrant ssh
```

でSSHログインできるか確認します。

```
$ vagrant halt
```

でインスタンスstopが行えますので、作業しないときは適宜停止して節約に努めます。

## （後半）Dockerを利用してのJupyter（IPython Notebook）の起動
[Installing Docker](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html)を参考に、まずEC2にDockerをインストールします。

```
$ sudo yum update -y
$ sudo yum install -y docker
```

docker daemonを起動し、インスタンス再起動後も自動で起動するようにしておきます。

```
$ sudo service docker start
$ sudo chkconfig docker on
```

sudo 権限なしでもdockerコマンドを追加できるようにec2-userをdockerグループに追加しておきます。

```
$ sudo usermod -a -G docker ec2-user
```

一旦exitし、再ログインすることでsudo なしでdockerが起動するようになります。

次にDockerfileの記述を行います。

```
$ vim Dockerfile
```

今回は簡単に進めるために、anacondaのイメージをベースにTensorFlowをインストールする形にします。
CMD で`docker run` 時にjupyterが起動するように指定をします。

```Dockerfile 
FROM continuumio/anaconda:latest

RUN mkdir /opt/notebooks
RUN pip install tensorflow
# Another installations comes here.

CMD ["/opt/conda/bin/jupyter", "notebook", "--notebook-dir=/opt/notebooks", "--ip='*'", "--port=8888", "--no-browser"]
```

編集終了後、Dockerfileからコンテナイメージを作成します。

```
$ docker build -t myjupyter .
```

最後にコンテナを起動します。notebookの永続化やホスト側からも覗けるようにvolumeを指定しマウントします。また、--restartオプションによりインスタンスstop/start後もdockerデーモンが自動でコンテナを立ち上げるようにします。

```
$ mkdir notebooks
$ docker run -p 8888:8888 -v /home/ec2-user/notebooks:/opt/notebooks --restart=always myjupyter
```

セキュリティグループでTCPのポート8888番の通信を許可したのち、ブラウザで`[Elastic IP]:8888`へアクセスすることで、Jupyterの起動を確認します。

--- 

これで勉強するときだけ`vagrant up`しブラウザ上で学習、一旦やめるときは`vagrant halt`で課金停止、ローカルで試したいときは同じDockerfileからビルド、ipythonnbをVagrant経由でとってくれば簡単移植、な環境になった・・・！はずなので、書籍勉強しつつ適宜修正していこうと思います。