データ分析環境の構築にDockerを利用しよう

## 📜 要約

コンテナー管理ソフトウェアのDockerを利用することで、データ分析の場面で利用頻度の高いRおよびPythonの分析環境として実行することが出来るRStudio Server、Jupyter、Beaker Notebookを容易に構築可能になる。Dockerを使うことの利点として、複数人でのデータ分析や将来の利用面においてデータ分析結果の再現性を高められると考えられる。

## 🍵 前置き〜データ分析者が直面する再現性への挑戦

データ分析の結果が、**自分以外では再現できない、同じデータを使っているのにナンデ！？**ということが時々あります。その原因として多いのが分析に関わる人間が利用する**実行環境の違い**、です。大きなものではOS、小さなものでは分析に利用するツール本体や拡張機能（パッケージやモジュール）のバージョンの違いがあります。

こうした再現性の問題は、複数人でデータ分析を行う時だけでなく、個人で分析を進めていく際にも気をつけていきたいところです。昔やった分析結果を将来再現できるなんて保証はありません。

使用環境についてメモを残しておくことは大事でしょう。しかし、そうしたメモ書きがあっても同様の環境を構築することは面倒であり、手間のかかるものです。使用環境のメモ書きは分析を再現する際に参考にはなりますが、実用的ではありません。

## 🐳 Dockerをベースにした分析環境の構築

ではどうするか、という問いに対して、**Dockerを使うことが一つの解決策として考えられます**。Dockerはオープンソースのコンテナー管理ソフトウェア（しばしば仮想マシンと比較される）の１つであり、エンジニアの開発場面で利用される機会の多いツールです。

しかし、Dockerが提供する仮想環境はデータ分析においても有効な機能を果たします。各地で言われていることですが、次にあげるようなDockerの特徴はデータ分析者の求める再現性を確保するためには理想に近いものでしょう。

- **軽量**：Dockerでは仮想環境を動作させるためのファイルシステムがホストとなるコンピュータ内で共有される。ファイルシステムの差分しかディスク容量を消費しないため、必要以上の容量を必要としません。
- **共有のしやすさ**：Dockerが構築するコンテナー型の仮想環境はDockerfileという設定ファイルによって記述されます。そのDockerfileを使えば、異なるコンピュータ上でも同じコンテナー型の仮想環境を作成することができます。ツールのバージョンや依存関係はDockerfileに依存しているので、共通の環境を構築できます。

### RおよびPythonユーザーのためのDockerイメージ

データ分析の際に頻繁に利用されるRやPython（Julia, Rubyも）を利用している人には、[RStudio](https://www.rstudio.com)や[Jupyter](http://jupyter.org)、[Beaker Notebook](http://beakernotebook.com)といった分析ツールが人気かと思います。

Dockerでは、これらの分析ツールを仮想環境で実行するためのDockerイメージが[Docker hub](https://hub.docker.com)によって公開されており、容易に分析環境を構築することができます。独自の環境を構築していきたいという場合にも、最低限必要なツールなどの記述がすでに書かれているこれらをベースにするのが良いでしょう。

- RStudio Server
    - [rocker/hadleyverse](https://hub.docker.com/r/rocker/hadleyverse/)… RStudio Server, LaTeX環境, モダンなRパッケージが含まれる
    - [rocker/rstudio](https://hub.docker.com/r/rocker/rstudio/)
- Jupyter
    - [jupyter/all-spark-notebook](https://hub.docker.com/r/jupyter/all-spark-notebook/)… Jupyter, Scala, R, Sparkなどが含まれる

    - [jupyter/datascience-notebook](https://hub.docker.com/r/jupyter/datascience-notebook/)
    - [jupyter/notebook](https://hub.docker.com/r/jupyter/notebook/)
- Beaker Notebook
    - [beakernotebook/beaker](https://hub.docker.com/r/beakernotebook/beaker/)... Python, R, Groovy, Julia, Node, JavaScript, SQL, C++などをサポート

Jupyterについては@kshigeru さんの[記事](http://qiita.com/kshigeru/items/2cd504e927869163b4c8)がすぐれています。Beaker NotebookのDockerイメージ利用についは@keiono さんの[記事](http://qiita.com/keiono/items/b8852172aac026f9f3a7)に詳しいです。再現性の問題も取り上げているので是非ご覧ください。

ここでは以下**主にRユーザーを対象に、Rを分析環境として利用する場合の話を中心に扱います**。なおイメージの編集方法やコミット、Dockerfileの作成などは共通です。

## 🔵 RStudio ServerをDocker環境で利用する

上記のhadleyverseイメージを使って、RStudio Serverを動作させてみましょう。すでにDockerが実行できる条件が揃っていれば、ターミナルで次のコマンドを実行してRStudio Serverが起動します。

```bash:rstudio_serverの起動.sh
docker run --rm -p 8787:8787 rocker/hadleyverse
```

最初の起動では、Docker hubから必要なファイルをダウンロードしてくるので時間がかかります。その間に[Dockerfile](https://hub.docker.com/r/rocker/hadleyverse/~/dockerfile/)の中身を見て、どのようなものが含まれているか認識しておくのも良いでしょう。

### hadleyverseについて補足

hadleyverseには主に以下のものが含まれます。いろいろなシステムを含んでいるためちょっと容量が大きいです。

- git
- TeX環境（texlive、ghostscript、imagemagick… ）
- pandoc
- vim
- RおよびRStudio Server
    - 各種のRパッケージ

hadleyverseを利用する際の日本語フォント周りを調整したDockerfileを@jnobuyuki さんが[作成されています](http://qiita.com/jnobuyuki/items/e6fdf0135e0a242dc1fa)。日本語でのプロットを行う場合には参考にどうぞ。

<img width="1105" alt="スクリーンショット 2016-04-06 21.12.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/19462/03794192-d286-d2d7-5766-1d950312bd19.png">

上のような状態になったらローカルホスト内で起動していますので確認してみましょう。Dockerが利用するIPアドレスは、上記の画面にも出ていますが`docker-machine ip`でも確認できます。

ブラウザで `http://192.168.99.100:8787/` を開くと、RStudio Serverへのログイン画面となります。先のコマンドではユーザーを指定しなかったので、ここでは以下のような入力をしてログインします。

- Username: rstudio
- Passward:  rstudio

無事にログインできたでしょうか。あとは普段通り、分析を進めることができます。

### docker run オプション

先のdocker コマンドをもう一度見てみましょう。

```bash
docker run --rm -p 8787:8787 rocker/hadleyverse
```

`--rm`オプションは、起動したコンテナを利用後に破棄するオプションです。hadleyverseのような大きなイメージを利用する場合、docker コンテナが残っているとディスク容量を圧迫するので削除するのが良いでしょう。

`-p`オプションおよび次の`8787:8787`はポートフォワードを指定しています。

その他の`docker run`オプションとして以下のようなものがあります。

- `-d`… コンテナをバックグラウンドで動かす
- `-e`… 環境設定
- `-v`… ホストディレクトリをコンテナにマウントする

特に大事になるのが`-v`オプションです。`-v`を指定することでローカル環境にあるファイル、ディレクトリを仮想環境と連携することが可能になります。

`-v`は次のようにして記述します。

```bash
docker run --rm -p 8787:8787 -v /Users/uri/Desktop/liftr/:/home/rstudio/liftr rocker/hadleyverse
# liftr フォルダが home/rstudio/ 以下にできる
```

再びRStudio Serverを起動すると、きちんと`liftr`フォルダがあります。`-v`を使ってマウントしたディレクトリやファイルは
**仮想環境で生成したファイルは自動的にローカル環境にも生成され、仮想環境で加えた変更はローカル環境のファイルにも反映される**という特徴を持ちます。

### 分析環境をオレオレ整備していく（コミットによるDockerイメージの作成）

`rocker/hadleyverse`イメージにはすでに便利なRパッケージが含まれていますが、どうしてもそれ以上にRパッケージが必要になると思います。Dockerでは、既存のイメージを拡張していくための仕組みとして、`commit`機能を備えています。次のような手順を踏んで、Dockerイメージをオレオレ環境に整備していきましょう。

まずはRStudio Server上で通常の方法でパッケージをインストールします。GitHubからパッケージをインストールするための`devtools::install_github()`を使っても問題ありません。

```r:パッケージのインストール
install.packages("liftr")
devtools::install_github("hoxo-m/pforeach")
```

インストールが完了したら、ターミナルに戻り、Dockerで起動しているイメージを確認します。

```bash
docker ps
# CONTAINER ID        IMAGE                COMMAND             CREATED               STATUS              PORTS                              NAMES
# a2f0d66896a2        rocker/hadleyverse   "/init"             About a minute ago   Up  About a minute   1410/tcp, 0.0.0.0:8787->8787/tcp   sleepy_bell
```

ここでのCONTAINER IDを対象に変更をコミットします。`commit`では、CONTAINER IDに加え、メッセージとイメージ名が必要になります。

```bash
docker commit -m "add packages" a2f0d66896a2  hoxom_verse
# sha256:335c72a5025896439a30cea4a3372c82e76a356b11ccef8b6549dfd2337d715f
```

その後イメージの一覧を表示してみると、先ほどコミットした`hoxom_verse`イメージが追加されています。

```bash
docker images
# REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
# hoxom_verse                  latest              335c72a50258        2 minutes ago       2.839 GB
# rocker/hadleyverse           latest              7cba8b06f25e        2 weeks ago         2.838 GB
```

コミットした`hoxom_verse`イメージは`rocker/hadleyverse`をベースにしており、追加した２つのパッケージが利用可能な状態となっているので、以降はこちらを利用するようにします。

```bash
docker run --rm -p 8787:8787 hoxom_verse
```

もしイメージが不要になったら`docker rmi <IMAGE ID>`で削除することができます。

### Dockerfileでオレオレ環境を作る

Dockerfileは、Dockerイメージを作成するために必要な設定ファイルです。Dockerfileを利用することで、第三者の環境でも共通の環境が構築することが可能になります（Docker hubはこの仕組みを利用）。

Dockerfileは次のような記述をします。

```
FROM <image> 元となるイメージを指定する。:の後ろでタグ指定可能
MAINTAINER <name> 管理者の名前
RUN <command> ビルド中に実行したいコマンドを指定。パッケージのインストールとか設定とか
CMD <command> 起動後のコンテナで実行したいコマンドを指定する。1つしか書けない
EXPOSE <port> [</port><port> ...] 外部に公開するポートを指定
ENV <key> <value> 環境変数の設定
ADD <src> <dest> ファイルを配置。絶対パスで記述する
```

あらかじめ分析に必要な環境が決まっている場合は、こうしたDockerfileを書いておくと良いかもしれません。

#### {liftr}パッケージでR MarkdownをもとにしたDockerfileを生成する

さて、Dockerfileの記述はLinuxに慣れていないと訳がわからなかったりします。そこで便利なのが**[`{liftr}`](https://cran.r-project.org/web/packages/liftr/)**パッケージです。**`{liftr}`**パッケージでは、R MarkdownファイルのYAML部分に必要な記述をしておくことで、自動的にDockerfileを生成してくれます。

使い方は、拡張子 `Rmd`ファイルについて、Dockerfile生成のための記述を加えていくだけです。また、分析結果を仮想環境で検証するために、分析内容自体をファイルに記述しておくと、こちらも自動的で検証してくれます。

Dockerfileを生成するのに必要な箇所は`Rmd`ファイルのYAML部分に書いていきます。以下のようにliftrブロックの記述がDockerfileを生成するのに利用されます。

```yaml
---
title: "Dockerized R Markdown Document"
author: "Uryu Shinya"
date: "`r Sys.Date()`"
output:
  html_document
liftr:
  from: "rocker/hadleyverse:latest"
  maintainer: "Uryu Shinya"
  maintainer_email: "suika1127@gmail.com"
  latex: false
  pandoc: false
  cranpkg:
    - kernlab
    - liftr
  ghpkg:
    - "uribo/lab.note"
    - "dichika/yeah"
---
```


{liftr}で使用されるオプションは以下のものがあり、maintainerとmaintainer_emailは必須項目となります。配布用に名前とメールアドレスを書いておきましょう。

- **maintainer**… Dockerfileの管理者名
- **maintainer_email**: Dockerfile管理者のメールアドレス
- **from**: Dockerイメージのベースとなるイメージ。デフォルトではDocker hub内の`rocker/r-base:latest`が利用される。r-baseではRStudio Serverが含まれていないので、`rocker/hadleyverse`としておくと良い
- **latex**: LaTeX環境を整備するか否か（初期値ではfalse）。hadleyverseで構築されるので必要なし
- **pandoc**: Pandoc環境の構築（初期値でtrue）。こちらもhadleyverseに含まれている
- syslib
- **cranpkg**: CRANからインストールしてくるパッケージ名を記述。バージョンと併せて表記することで特定のバージョンがインストールされる（`e.g. ggplot2/1.0.0`）。バージョンを省略すると最新版がインストールされる
- **biocpkg**: Bioconductor に登録されているパッケージをインストール
- **ghpkg**: GitHubにリポジトリがあるRパッケージをインストール。`username/repository`という表記をする

ではDockerfileを生成してみましょう。関数`lift()`にR Markdownのパスを渡します。

```r:Dockerfileを生成
library(liftr)
lift("~/Documents/liftr/lift_docker.Rmd")
```

Dockerfileは次のようになりました。このDockerfileをビルドすることで、このDockerfileによって構築される環境を簡単に再現できます。

```
FROM rocker/hadleyverse:latest

MAINTAINER Uryu Shinya <suika1127@gmail.com>

# System dependencies for required R packages
RUN  rm -f /var/lib/dpkg/available \
  && rm -rf  /var/cache/apt/* \
  && apt-get update -qq \
  && apt-get install -t unstable -y --no-install-recommends \
    ca-certificates \
    libssl-dev \
    libcurl4-openssl-dev \
    libxml2-dev \
    libssh2-1-dev \
    git

RUN Rscript -e "install.packages(c('devtools','knitr','rmarkdown','shiny','RCurl'), repos = 'https://cran.rstudio.com')"

RUN Rscript -e "source('https://cdn.rawgit.com/road2stat/liftrlib/fab41764ea8b56677d05c70c86225774164b6ca0/install_cran.R');install_cran(c('kernlab','liftr'))"

RUN Rscript -e "devtools::install_github(c('uribo/lab.note','dichika/yeah'))"

RUN mkdir /liftrroot/
WORKDIR /liftrroot/
```

```bash
docker build -t uribo/liftr ~/Documents/liftr/
docker run --rm -p 8787:8787 uribo/liftr
```

というわけで、分析に利用するR Markdownから直接Dockerfileを生成することができました。パッケージ管理が面倒になりがちなので、こうしたツールを利用するとDockerを使ったデータ分析環境の運営が楽になりそうですね。

Dockerについては「仮想化に必要なVirtualBoxを使わずに[簡単に利用出来るようになる](https://blog.docker.com/2016/03/docker-for-mac-windows-beta/)」という記事も最近出ており、ベータ版の利用登録しておくと、しばらくして承認が下りる状態なそうなので、春ということで皆さまぜひ試してみてはいかがでしょう。

（ちなみにRではパッケージのバージョンの差異による再現性が度々問題となるので、{packrat}というシステムもあります ref) [packratで人類の拡大再生産に貢献する - 盆栽日記](http://d.hatena.ne.jp/dichika/20131220/p1), [Packratの使い道を考えてみた - Qiita](http://qiita.com/uri/items/5d473a5df91954585e62)）

## 🔗 参考

- [Dockerを理解するための8つの軸 | Think IT（シンクイット）](https://thinkit.co.jp/story/2015/07/29/5382)
- [R使いのためのDocker「Rocker」を使ってみる(Windows)](http://notchained.hatenablog.com/entry/2014/12/20/151133)
- [R Docker tutorial](http://ropenscilabs.github.io/r-docker-tutorial/)
- [Jupyter の Docker イメージを使ってみる - Qiita](http://qiita.com/kshigeru/items/2cd504e927869163b4c8)
- [docker コマンド チートシート - Qiita](http://qiita.com/voluntas/items/68c1fd04dd3d507d4083)