osx(El Capitan)上でdockerを使ってoctaveの開発環境を構築


*※2016/2/7 以下を追記 ・Dockerfileを画像を保存できるように修正 ・起動スクリプトを追記 ・Octave4.0でsubmit関数実行時にエラーが出ることに対する対策を追記*

## 背景 ##

Couseraの[Machine Learningのコース](https://www.coursera.org/learn/machine-learning/)の課題をこなすためにOctave開発環境が必要になるのですが、[公式wiki](http://wiki.octave.org/Octave_for_MacOS_X)によるとEl CapitanではOctaveはもはやvagrantを使って仮想マシンで動かすことが推奨されているようです。しかしOctaveを使うためだけに仮想マシンを一つ作るのも憚られるものがあるので、dockerで使えないかを試してみました。
以下は2016/2/1時点の手順です。

## 手順 ##

以下のような手順で構築可能でした。4, 8あたりで引っかかりましたがなんとかこれで起動するようになりました。

1. [Docker Toolbox](https://www.docker.com/products/docker-toolbox)をインストール
2. `docker-machine create default --driver virtualbox`でvirtualbox上にdefaultという名前でdocker実行用の仮想マシンを構築
3. 仮想マシンを`docker-machine stop default`で停止
4. [ここ](https://github.com/tianon/boot2docker-legacy/releases)からboot2docker.isoのPreRelease版を落として来て、defaultが使うisoファイル(~/.docker/machine/machines/default/boot2docker.iso)に上書きする。
    * docker2dockerのバージョン1.9.1だとDockerイメージのビルド時に"Setting up ca-certificates-java"と表示され[固まる問題](https://github.com/docker/docker/issues/18180)が発生するため
5. `docker-machine start default`で仮想マシンを起動
6. xquartzをインストール
7. `brew install socat`を実行
    * dockerからのX11でGUIを転送してxquartzで描画するためにsocatが必要
8. xquartzをアプリケーションから起動した状態でターミナルから`printenv DISPLAY`を実行すると`/private/tmp/com.apple.launchd.SblSCN3ZNz/org.macosforge.xquartz:0`のような値を返すはずなのでこれを確認
    * tmux上などで実行するとこのDISPLAY環境変数がセットされていないことがあるので注意
9. 8を確認したターミナル上で`socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"`を実行し、ディスプレイ転送の準備をする
10. ターミナルを別に開き、下記のDockerfileを置いたディレクトリ下で`docker build -t wrist/octave .`を実行し、dockerイメージをビルド
11. `docker run --rm -e DISPLAY=192.168.10.10:0.0" -i -v /Users/wrist/work/octave/home:/root -t wrist/octave octave`を実行してコンテナを作成し、GUIが転送されて来れば成功
    * 192.168.10.10の部分はホストとなっているマシンのローカルIP
    * `-v`でホストマシンのボリュームをdockerコンテナ上にマウント可能

## 起動スクリプト(2016/2/7追記) ##

上記手順ではXQuartzを実行した後にsocatを実行していますが、実はこれでは駄目っぽく、XQuartzを実行した時点で6000番ポートをXQuartzが使うようになるので、ややこしいですがターミナルでsocatを実行した後にXQuartzを起動する必要があるようです。
というわけでややこしいので以下のような起動スクリプトを書きました。docker-machineで作成したVM、socatプロセス、およびXQuartzプロセスがそれぞれ起動(存在)しているかを順番に確認して、起動してなければそれぞれ起動させ、その上でローカルのIPアドレスを調べてそのIPアドレスをDISPLAY環境変数にセットした上でdocker runでコンテナを起動させます。

```sh:run_octave.sh
#!/bin/bash
# vim:fileencoding=utf-8

machine_name="default"
container_name="wrist/octave"

local_home="${HOME}/work/octave/home"
docker_home="/root"

exec_command="octave"

count=`VBoxManage list runningvms | grep ${machine_name} | wc -l`
if [ $count = 0 ]
then
        echo "[run octave] running docker-machine..."
        docker-machine start ${machine_name}
        docker-machine regenerate-certs -f ${machine_name}
fi

eval "$(docker-machine env ${machine_name})"

# run socat before XQuartz
count=`ps aux | grep socat | grep -v grep | wc -l`
if [ $count = 0 ]
then
        echo "[run octave] running socat in background..."
        socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" &
fi

count=`ps aux | grep XQuartz | grep -v grep | wc -l`
if [ $count = 0 ]
then
        echo "[run octave] open XQuartz..."
        open -a XQuartz
fi

nics=`ifconfig | awk -F: '/^[a-z]/ && !/^lo/ { print $1 }'`
ipv4_detected=""
for nic in ${nics}; do
        ipv4s=$(ifconfig ${nic} | awk '$1 == "inet" { print $2 }')
        for ipv4 in ${ipv4s}; do
                if [ -n "${ipv4}" ]
                then
                        ipv4_detected=${ipv4}
                        break
                fi
        done

        if [ -n "${ipv4_detected}" ]
        then
                echo "[run octave] docker run using display IP: ${ipv4_detected}"
                docker run --rm -e DISPLAY=${ipv4_detected}:0.0 -i -t \
                        -v ${local_home}:${docker_home} \
                        ${container_name} \
                        ${exec_command}
                break
        fi
done
```


## Dockerfile ##

*※2016/2/7追記 画像を保存するためにoctave上で`print -dpng 'plot.png'`を実行する際にエラーが出るため、最後の2行を追記*

```Dockerfile
FROM ubuntu:14.04
MAINTAINER Hiromasa OHASHI <stoicheia1986@gmail.com>

ENV DEBIAN_FRONTEND noninteractive

RUN apt-get -y install software-properties-common
RUN add-apt-repository ppa:octave/stable

RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get install -y texinfo gnuplot octave liboctave-dev octave-info

# japanese environment
RUN apt-get install -y language-pack-ja-base language-pack-ja
RUN update-locale LANG=ja_JP.UTF-8 LANGUAGE="ja_JP:ja"
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
RUN apt-get install -y fonts-takao

RUN apt-get install -y xfig
RUN apt-get install -y epstool
```

## Octave4.0でのsubmit関数のエラー(2016/2/7追記) ##

上記DockerfileだとOctave4.0が入りますが、4.0だと課題を送る時のsubmit関数を実行する際にエラーが出ます。下記リンクを参考に修正を施すことで実行できるようになります(下記記事では2箇所となっていますが、同様の記述があるmファイルを全て修正する必要があるようです)。

[Machine Learning - Week2 Octave 4.0で課題をする時のエラー](http://www.kegamin.com/entry/20151116/1447681287)

また[公式フォーラム](https://learner.coursera.help/hc/en-us/community/posts/204693179-linear-regression-submit-error)にも上記現象のスレッドがあります。