runC で aobench を動かす

漢なら runC で aobench を動かしたいですよね!

# Requirements


Ubuntu 14.04 or later や, CoreOS, docker-machine で作られるコンテナ対応済みイメージを使うのがオススメです.

runC では checkpoint 機能を使う場合, checkpoint 対応 Linux カーネルと CRIU が必要になります. 

また, 最近では runC に seccomp がデフォルトで有効になりましたが, CRIU 側の seccomp 対応が未だである(CRIU 1.7 時点)ですので, checkpoint 機能を使う場合は seccomp 無効で runC をビルドする必要があります.


# Build

docker-machine の google ドライバでコンテナ対応済み linux イメージを立てていると仮定します.

まず runC をビルドします. 参考となる Dockerfile は以下の通りです.

```
$ cat Dockerfile

FROM ubuntu:14.04
MAINTAINER syoyo@lighttransport.com

ENV HOME /root
ENV GOPATH /root/go
ENV PATH /root/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

RUN apt-get update
RUN apt-get install -y build-essential git wget curl
RUN apt-get install -y libseccomp-dev seccomp

RUN mkdir -p $GOPATH

RUN wget -qO- https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz | tar -C /usr/local -xzf -

# Build runc
RUN mkdir $GOPATH/src
RUN mkdir $GOPATH/src/github.com
RUN mkdir $GOPATH/src/github.com/opencontainers

# Workaround for invalidate cache for git clone.
ADD dummyfile /data/

RUN cd $GOPATH/src/github.com/opencontainers; git clone https://github.com/opencontainers/runc

# seccomp を無効にする場合は make BUILDTAG="" とする.
RUN cd $GOPATH/src/github.com/opencontainers/runc; make
RUN cd $GOPATH/src/github.com/opencontainers/runc; make install
```

# runC 用 aobench イメージを作る.

すでに aobench イメージが Docker registory にありますので取得し, tar 形式に変換します.

    $ sudo docker pull syoyo/aobench 
    $ sudo docker export $(sudo docker create syoyo/aobench) > aobench.tar
    $ tar -c aobench_dist -xf aobench.tar
    $ runc spec  # runc を実行するディレクトリは aobench_dist ディレクトリではない

`config.json` を編集し, `rootfs` のパスを `aobench_dist` に変更,  `process/args` を `/bin/ao` にします.

# runC で実行する.

`config.json` のあるディレクトリで runc start します.

    $ sudo runc start
    ao: ao.c:321: saveppm: Assertion `fp' failed. 

おおっと! ファイルシステムへの書き込みで失敗してしまいました...

`config.json` で `root/readonly` が **true** になっているのが原因そうです. とりあえず **false** に変えてみます.

    $ sudo runc start
    ao: ao.c:321: saveppm: Assertion `fp' failed. 

ぐぬぬ... ルートファイルシステムは readonly = false でもダメのようです...
(`runtime.json` では `mounts/cgroups` に `ro` フラグが付いているからでしょうか)

適当に `tmpfs` を作りマウントするようにします.

`runtime.json` に `tmpfs` マウントの情報を `mounts` に追加します.

```
{
        "mounts": {
                "cgroup": {
                        "type": "cgroup",
                        "source": "cgroup",
                        "options": [
                                "nosuid",
                                "noexec",
                                "nodev",
                                "relatime",
                                "ro"
                        ]
                },
                ...
                "tmpfs": {
                        "type": "tmpfs",
                        "source": "tmpfs",
                        "options": [
                                "nosuid",
                                "noexec",
                                "nodev",
                                "mode=1777",
                                "size=65536k"
                        ]
                },
```

`config.json` に `/tmp` のマウント情報を記載します. また, CWD を `/tmp` に設定して, `/tmp` で `/bin/ao` を実行するようにします.

```
        "process": {
                ...
                "args": [
                        "/bin/ao"
                ],
                "cwd": "/tmp"
        },

        "mounts": [
                {
                        "name": "proc",
                        "path": "/proc"
                },
                ...
                {
                        "name": "tmpfs",
                        "path": "/tmp"
                },
```

再度実行します.

    $ sudo runc start
    Output ao.ppm

Voila! runC で aobench が動きました!

# プロセスの pause/resume を試す.

`pause`, `resume` でプロセスの中断と再開を行うことができます.

    $ sudo runc start
    $ sudo runc pause
 
    # 別のターミナルを開く
    $ sudo runc resume

Cool!

`pause` すると, top で見ると計算がストップしているのが確認できます.

# Checkpointing を試す.

Checkpoint 機能を使うことで, プロセスの状態をファイルにダンプし, 別のマシンに転送してリストアすることでプロセスのマイグレーションができます.

Checkpoint 機能を使うには, システムに CRIU http://criu.org/Main_Page がインストールされている必要があります. 今回は CRIU 1.7 で確認しました.

`config.json` にて,

* `process/terminal` = false
* `root/readonly` = false

を設定しておく必要があります.

    $ sudo runc start
    $ sudo runc checkpoint # 別ターミナル
    criu failed: type DUMP errno 0
    log file: /run/opencontainer/containers/tmp/criu.work/dump.log
    
ぐぬぬ... dump に失敗してしまいました.

https://github.com/xemul/criu/issues/25

seccomp 対応の runC では, CRIU 側がまだ対応できていないのが原因でした. 
とりあえず runC を seccomp 無効でビルドしてみます(runc の Makefile で BUILDTAG を編集する). また, `runtime.json` での `seccomp` セクションを削除しておきます.

seccomp 無効の runC で再度実行してみます.

    $ sudo runc start
    $ sudo runc checkpoint # 別ターミナル
    # checkpoint/ ディレクトリにプロセスのダンプが書かれます.
    $ sudo runc restore

Voila! プロセスのチェックポイント実行に成功しました!

To be continued...

# TODO

* aobench の結果ファイルの回収(ネットワーク or stdout に流す?)
* checkpoint を異なるマシン間で試す.