Rocket (Docker alternative by CoreOS)を試す

## Motivation

CoreOSがApache 2ライセンスで2014年12月1日に公開した[Rocket](https://github.com/coreos/rocket/)を試してみます。流行の[Docker](https://www.docker.com/)のalternativeになりうるのか。

## Conclusion

 * ACI (App Container Image)で固められたetcdがRocketで動作することを確認した
 * systemd-nspawnによるサンドボックス化がコンテナ動作の核となっている
 * Dockerイメージか、Rocket ACIか、どちらのコンテナ形式で多くのアプリがbuild, ship, runされるようになるか。結局、「形式、フォーマット」なので、多くの人がそれに乗っかるかどうか、乗っかるだけのよさがあるかどうかが今後の注目ポイント

## TODO

 * [ACI spec](https://github.com/coreos/rocket/blob/master/app-container/SPEC.md)を深追いしてDockerイメージとの違いを明らかにする
 * そもそもsystemd-nspawnっておいしいの？を明らかにする
 * stage0, stage1, systemd-nspawnといったRocketのシーケンス、プロセスモデルを明らかにする

## Backgroud

RocketはDockerのalternativeとしてCoreOSの人たちがつくった。曰く…

> CoreOS is building a container runtime, Rocket
> https://coreos.com/blog/rocket/

> Unfortunately, a simple re-usable component is not how things are playing out. Docker now is building tools for launching cloud servers, systems for clustering, and a wide range of functions: building images, running images, uploading, downloading, and eventually even overlay networking, all compiled into one monolithic binary running primarily as root on your server. The standard container manifesto was removed. We should stop talking about Docker containers, and start talking about the Docker Platform. It is not becoming the simple composable building block we had envisioned.

> 残念なことに、単純で再利用可能なコンポーネントというのは現在のDockerが目指している方向ではない。Dockerは今や、クラウドサーバ群、クラスタ向けシステム群を立ち上げるためのツール群と、幅広い機能(イメージの作成・動作・アップロード・ダウンロード、さらにはオーバレイネットワークまでも)を、rootで動作するモノリシックなバイナリで提供している。Standard Container Manifestoは削除された。Dockerコンテナについて話すのではなく、Dockerプラットフォームについて話さなくてはならなくなった。我々が望んでいた、シンプルで組み合わせ可能なビルディングブロックではなくなってしまっている。

確かにDocker自身のロードマップとしても、単なる「コンテナイメージの規定と保持、コンテナのハンドリング」、というところから、オーケストレーション、ネットワークなど周辺系に広がりを見せていたのは確か。その辺はDockerの外でやるべき、すくなくともモノリシックなつくりは回避すべき、というのがCoreOSの人たちの意見に聞こえます。

単純にForkする方向ではなく、独自にRocketを作るに至った理由としては、曰く、

> CoreOS is building a container runtime, Rocket
> https://coreos.com/blog/rocket/

> Why not just fork Docker?

> From a security and composability perspective, the Docker process model - where everything runs through a central daemon - is fundamentally flawed. To “fix” Docker would essentially mean a rewrite of the project, while inheriting all the baggage of the existing implementation.

> セキュリティと組合せ可能性の観点から、Dockerのプロセスモデル、すなわち、中央集権的なデーモン経由ですべてが動作する、というのは、本質的に欠陥がある。Dockerを"修正する"ことは、すなわち過程で既存の実装に関する悩みをすべて引き継ぐことになり、本質的にプロジェクト全体を書き直すことになってしまう。

となかなか痛烈です。ということで、まずはRocketがどんなものかを理解するため動作させてみました。

## Environment

CentOS 7.0 x86_64 powered by Vagrant on Windows 7.

## Setup

https://github.com/coreos/rocket のREADME.mdにセットアップ手順が書いてあります。ライセンスはApache 2、言語はGoです。

```lang:shell-session
curl -L https://github.com/coreos/rocket/releases/download/v0.1.0/rocket-v0.1.0.tar.gz -o rocket-v0.1.0.tar.gz
tar xvzf rocket-v0.1.0.tar.gz
cd rocket-v0.1.0
./rkt help
```

セットアップ手順といっても、ELF 64-bit LSB executableなバイナリのtarballをダウンロードしてきて展開するだけです。展開された内容とfile, lddの結果は以下の通り。

```lang:shell-session
[vagrant@localhost rocket-v0.1.0]$ ls -l
total 37560
-rwxr-xr-x 1 vagrant vagrant  8127496 Dec  1 07:00 actool
-rw-rw-r-- 1 vagrant vagrant     5386 Dec  1 07:01 README.md
-rwxr-xr-x 1 vagrant vagrant 30320048 Dec  1 07:00 rkt
[vagrant@localhost rocket-v0.1.0]$ file actool rkt
actool: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
rkt:    ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
[vagrant@localhost rocket-v0.1.0]$ ldd actool rkt
actool:
        linux-vdso.so.1 =>  (0x00007fff2a8ec000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f37ed06a000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f37eccd6000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f37ed28d000)
rkt:
        linux-vdso.so.1 =>  (0x00007fff347ff000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f88bdc38000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f88bd8a4000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f88bde5b000)
```

actoolは、アプリケーションをパッケージングしたものであるACI (App Container Image)を取り扱うためのもの、rktはRocketで実際にACIから実稼働コンテナを動作させたりするもの、という役割分担に見えます。それぞれのヘルプから可能なコマンドを抜粋。

```lang:shell-session
rkt - rocket, the application container runner

rkt fetch   Fetch image(s) and store them in the local cache
rkt status  Check the status of a rkt job
rkt run     Run image(s) in an application container in rocket
```
```lang:shell-session
actool - actool, the application container tool

actool build     Build a Fileset ACI from the target directory
actool discover  Discover the download URLs for one or more app container images
actool validate  Validate that one or more images or manifests meet the AppContainer specification
```

## Fetch & Run

手順に従いACIをfetchしてrunしてみます。

```lang:shell-session
[vagrant@localhost rocket-v0.1.0]$ ./rkt fetch https://github.com/coreos/etcd/releases/download/v0.5.0-alpha.4/etcd-v0.5.0-alpha.4-linux-amd64.aci
fetch: error creating image directory: mkdir /var/lib/rkt: permission denied
```

Rocketの動作バイナリを一般ユーザでダウンロードしただけなので、/var/lib/rktが作れませんでした。Rocket自身のパッケージングと配布がうまくいきはじめたらこのへんの正しいやり方が整備されてくるのだと思うのですが、とりいそぎ今はsudoしてみます。

```lang:shell-session
[vagrant@localhost rocket-v0.1.0]$ sudo ./rkt fetch https://github.com/coreos/etcd/releases/download/v0.5.0-alpha.4/etcd-v0.5.0-alpha.4-linux-amd64.aci
sha256-f9215c18b86f406c7cec4c7b45fd8752b5bfd1a492507d647821c2ce593fbf31
```

fetchによりダウンロードされたファイルは以下の2つ。cas/blobとcas/remoteのそれぞれの1つずつ。casってのは、[Contents Addressable Storage](http://en.wikipedia.org/wiki/Content-addressable_storage)ですね。

```lang:none
/var/lib/rkt/cas/blob/sha256/f9/sha256-f9215c18b86f406c7cec4c7b45fd8752b5bfd1a492507d647821c2ce593fbf31
/var/lib/rkt/cas/remote/sha256/8e/sha256-8eac4158f86ae7d7e78f52e48ab18dcefdbc56104010de295b4c637e7ff4f02a
```

cas/blobの方はtarアーカイブで、以下のディレクトリ構成でetcdのバイナリがパッケージングされています。

```
[vagrant@localhost rocket-v0.1.0]$ tar xvf /var/lib/rkt/cas/blob/sha256/f9/sha256-f9215c18b86f406c7cec4c7b45fd8752b5bfd1a492507d647821c2ce593fbf31
rootfs/
rootfs/Documentation
rootfs/Documentation/0_4_migration_tool.md
rootfs/Documentation/admin_guide.md
rootfs/Documentation/api.md
rootfs/Documentation/clustering.md
rootfs/Documentation/configuration.md
rootfs/Documentation/glossary.md
rootfs/Documentation/other_apis.md
rootfs/Documentation/proxy.md
rootfs/Documentation/runtime-configuration.md
rootfs/README-etcdctl.md
rootfs/README.md
rootfs/etc
rootfs/etc/hosts
rootfs/etcd
rootfs/etcd-migrate
rootfs/etcdctl
app
```

いっぽうcas/remoteの方はJSONファイルです。blobのSHA256ハッシュ値と、どこから取ってきたかが含まれています。

```
[vagrant@localhost rocket-v0.1.0]$ cat /var/lib/rkt/cas/remote/sha256/8e/sha256-8eac4158f86ae7d7e78f52e48ab18dcefdbc56104010de295b4c637e7ff4f02a | python -m json.tool
{
    "Blob": "sha256-f9215c18b86f406c7cec4c7b45fd8752b5bfd1a492507d647821c2ce593fbf31",
    "ETag": "",
    "Mirrors": [
        "https://github.com/coreos/etcd/releases/download/v0.5.0-alpha.4/etcd-v0.5.0-alpha.4-linux-amd64.aci"
    ],
    "Name": "https://github.com/coreos/etcd/releases/download/v0.5.0-alpha.4/etcd-v0.5.0-alpha.4-linux-amd64.aci"
}
```

それでは、fetchしたetcdをrkt runしてみます。/var/lib/rkt/containers配下に書き込みむようなので、安直にsudoで。

```lang:shell-session
[vagrant@localhost rocket-v0.1.0]$ sudo ./rkt run sha256-f9215c18b86f406c7cec4c7b45fd8752b5bfd1a492507d647821c2ce593fbf31
stage1/usr/bin/systemd-nspawn: error while loading shared libraries: libseccomp.so.2: cannot open shared object file: No such file or directory
```

[seccomp](http://en.wikipedia.org/wiki/Seccomp)がいるらしいので、yumで入れてから再チャレンジ。

```lang:shell-session
[vagrant@localhost rocket-v0.1.0]$ sudo yum install libseccomp
(snip)
[vagrant@localhost rocket-v0.1.0]$ sudo ./rkt run sha256-f9215c18b86f406c7cec4c7b45fd8752b5bfd1a492507d647821c2ce593fbf31
Timezone UTC does not exist in container, not updating container timezone.
2014/12/04 00:19:29 no data-dir provided, using default data-dir ./default.etcd
2014/12/04 00:19:29 etcd: listening for peers on http://localhost:2380
2014/12/04 00:19:29 etcd: listening for peers on http://localhost:7001
2014/12/04 00:19:29 etcd: listening for client requests on http://localhost:2379
2014/12/04 00:19:29 etcd: listening for client requests on http://localhost:4001
2014/12/04 00:19:29 etcdserver: name = default
2014/12/04 00:19:29 etcdserver: data dir = default.etcd
2014/12/04 00:19:29 etcdserver: snapshot count = 10000
2014/12/04 00:19:29 etcdserver: advertise client URLs = http://localhost:2379,http://localhost:4001
2014/12/04 00:19:29 etcdserver: initial advertise peer URLs = http://localhost:2380,http://localhost:7001
2014/12/04 00:19:29 etcdserver: initial cluster = default=http://localhost:2380,default=http://localhost:7001
2014/12/04 00:19:29 etcdserver: start member ce2a822cea30bfca in cluster 7e27652122e8b2ae
2014/12/04 00:19:29 etcdserver: added local member ce2a822cea30bfca [http://localhost:2380 http://localhost:7001] to cluster 7e27652122e8b2ae
2014/12/04 00:19:30 raft: elected leader ce2a822cea30bfca at term 1
2014/12/04 00:19:30 etcdserver: published {Name:default ClientURLs:[http://localhost:2379 http://localhost:4001]} to cluster 7e27652122e8b2ae
```

無事にetcdが動き始めたようです。

sudo ps auxから関係しそうなプロセスを抜粋。

```lang:shell-session
root     10416  0.0  0.5 189372  2700 pts/0    S+   00:19   0:00 sudo ./rkt run sha256-f9215c18b86f406c7cec4c7b45fd8752b5bfd1a492507d647821c2ce593fbf31
root     10417  0.3  0.1  21660   872 pts/0    S+   00:19   0:00 stage1/usr/bin/systemd-nspawn --boot --register false --quiet --uuid=0d5bee94-3158-4cc2-a1ef-3
root     10422  0.0  0.4  22564  2140 ?        Ss   00:19   0:00 /usr/lib/systemd/systemd --default-standard-output=tty --log-target=null --show-status=0
root     10423  0.0  0.0   4108   388 ?        Ss   00:19   0:00 /usr/bin/sleep 9999999999d
root     10424  1.3  1.3  12024  6272 ?        Ssl  00:19   0:02 /etcd
```

根幹となるコンテナの起動はsystemd-nspawnに任せているようです。Rocketの概念として、stage0, stage1という段階を経ているよう模様。

## Remarks

まずはチュートリアルどおりコンテナを動かすところまでは無事いきました。この浅さではまだなんともいえないですが、DockerにしろRocketにしろ、アプリケーションのパッケージング方式がミソなので、「Dockerイメージ」と「Rocket ACI」のどちらがスジがいいのか、多くのアプリケーションが配布・流通するようになるのか、というのを見極める必要があります。

最近いわれているDockerコンテナのセキュリティ、横にちょっかいを出せる系の課題にたいして、Dockerやその下回りにきちんとした隔離機能を追加する方向でガンバルのか、Rocketのように再発明する方向でガンバルのか、どちらがよいのかは、サンドボックス化そのものを担うコンポーネントたち(libcontainer (←これは抽象化層だけれど), systemd-nspawn)のスジのよさも含めて見る必要があります。

CoreOSの人たちが痛烈に指摘している、Dockerのプロセスモデルの課題(中央集権的なデーモン経由ですべてが動作する)に対して、Rocketがどのようにアプローチしている、していくのか。そこは、Rocketの内部構造を紐解きながら見ていきたいところです。

そんじゃーね！！！