「Docker」と新コンテナランタイム「rkt」をサクっと比較してみる

#概要

この記事では、アプリケーションコンテナのランタイムであるDockerとrktについて、
2つの大まかな違いを説明し、Dockerとrktそれぞれを使用してコンテナの取得・作成・起動・破棄など基本的なコンテナ操作を実施してみます。
記事全体として「Dockerはある程度知ってるけど、それに比べてrktってどうなんだ？」というトーンで書いています。

# Dockerとは

公式：https://www.docker.com/

Docker社が開発しているオープンソースのコンテナランタイムです。
コンテナ技術自体は決して新しくはないですが、その使いやすさからある種のコンテナブーム（?）を巻き起こしたともいえるかと思います。
詳しい情報は、ググれば良い記事が山ほど出てくるので割愛します。

# rktとは

公式：https://coreos.com/rkt/docs/latest/

CoreOS社が開発している、アプリケーションコンテナのランタイムとなります。
同社が2014年12月に発表したApp Container(appc) Specという標準的なコンテナの仕様を実装したもので、「an alternative to the Docker runtime」として注目を集めています。
現時点での最新バージョンは0.6.1とまだまだプロトタイプの位置づけです。
ちなみにこれまで「rocket」や「Rocket」といった表記が混在していましたが、今後は「rkt」に統一されるらしいです。

# Dockerとrktの違い

appcのテーマである「構成の柔軟性」「セキュリティ」「イメージ配布の容易さ」「オープンさ」の切り口で説明してみます。

1. **構成の柔軟性**
Dockerは、クラウドサーバやクラスタ向けシステムを構築するためののツールや幅広い機能(イメージの作成・動作・アップロード・ダウンロード)が、rootで動作するmonolithicなバイナリで提供されています。
一方でrktにおいては、コンテナをダウンロード、インストール、起動するための全てのツールは統合されてはいますが、それぞれは独立した構成となっています。

2. **セキュリティ**
Dockerはクラサバ型の設計であり、Dockerクライアントは Dockerデーモンに対して各種コマンドを発行するアーキテクチャとなっています。
Dockerデーモンはroot権限で動作する必要があるため、常にrootプロセスがDockerホスト側で動き続けることになります。
このことを「全てが中央のデーモンを通じて実行されるDockerの処理モデルには、セキュリティと再利用性において根本的な欠陥がある。」とし、root権限デーモン⇔クライアントのアーキテクチャを廃止して新たに生まれたのがrktとなります。

3. **コンテナイメージの配布**
Dockerは「Docker Hub」や「Docker Private Registry」などのレジストリを通じてコンテナイメージをpull/pushする必要があります。
rktにおいては専用のレジストリは存在せず、WEBの標準的な仕様（HTTPS）を用いてイメージを配布することができます。

4. **オープンさ**
rktはオープンなコンテナ仕様を実装しています。

コマンドレベルでの細かな違いについては、下記で順を追って見ていきましょう。

# 環境

coreos(alpha v717.0.0) on vagrant（1.7.2）

# インストール

coreosは、アプリケーションをコンテナ上でのみ動作させるように設計されており、
取得したbox（coreos-alpha v717.0.0）ではDocker, rkt共にインストール済みとなっています。

## Docker

```bash
$ docker -v
Docker version 1.5.0, build a8a31ef-dirty
```

## rkt

```bash
$ rkt version
rkt version 0.5.5
appc version 0.5.1+git
```

ちなみに現時点（2015/6/21）でのrktの最新バージョンは0.6.1となっており、下記の通り最新版をインストールすることもできます。

```bash
wget https://github.com/coreos/rkt/releases/download/v0.6.1/rkt-v0.6.1.tar.gz
tar xzvf rkt-v0.6.1.tar.gz
```

# コンテナの取得・作成・起動から破棄まで

## コンテナイメージの取得

### Docker

Dockerは「Docker Hub」や「Docker Private Registry」などのレジストリを介してイメージを取得・公開することができます。
ここではCentOSの最新イメージを取得しています。

```bash
$ docker pull httpd
```

### rkt

rktでは、appcのディスカバリ仕様に従って、インターネット上からコンテナイメージを取得することができます。
ここでは「coreos.com/etcd」というACIを取得する例を示します。

① rkt trust
rktではACIの署名を検証するために公開鍵を取得する必要があります。

```bash
$ sudo rkt trust --prefix=coreos.com/etcd
```

② rkt fetch
次にrkt fetchコマンドでACIの取得、署名の検証を行います。

```bash
$ sudo rkt fetch coreos.com/etcd:v2.0.4
```

なお、Dockerイメージを利用したい場合にDocker Registryからイメージダウンロードし、ACIに変換することもできます。

```bash
$ sudo rkt --insecure-skip-verify fetch docker://httpd
```

※Dockerイメージのsignature verificationはサポートされないため、`--insecure-skip-verify`オプションを付けます。

## コンテナイメージ一覧

### Docker

`docker images`コマンドを使用します。

```bash
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
httpd               latest              de94ed779434        5 days ago          161.8 MB
```

### rkt

`rkt images`コマンドを使用します。

```bash
$ sudo rkt images
KEY                                                                     APPNAME                 IMPORTTIME                              LATEST
sha512-1eba37d9b344b33d272181e176da111ef2fdd4958b88ba4071e56db9ac07cf62 coreos.com/etcd:v2.0.4  2015-06-21 07:38:58.396 +0000 UTC       false
sha512-fb95bcc29323607aafcca362bd6beb073c85e34f4cd63db8fe5570b603d33838 httpd:latest    2015-06-21 10:47:48.825 +0000 UTC       false
```

取得した「coreos.com/etcd」と「httpd」イメージが表示されています。

## コンテナの起動

### Docker

`docker run`コマンドを使用します。
`-p`オプションでコンテナの80番ポートをホストの8080ポートをマッピングします。

```bash
$ docker run -p 8080:80 httpd:latest
```

ホスト側からコンテナへアクセスしてみます。

```bash
$ curl http://localhost:8080/
<html><body><h1>It works!</h1></body></html>
```


### rkt

rktでは`rkt run`コマンドでイメージ名を指定することでコンテナを起動します。

```bash
$ sudo rkt run -local httpd
```

`-local`：ローカルのイメージを利用する

rktのネットワーキングはdockerと異なりデフォルトでホストモードとなっており、ネットワークはホストのものを使用します。

```bash
$ curl "http://localhost:80/"
<html><body><h1>It works!</h1></body></html>
```

プライベートネットワークを利用する場合は、`rkt metadata-service`コマンドで[Metadata Service](https://coreos.com/rkt/docs/0.5.4/metadata-service.html)を起動したのちに`--private-net`オプションを使用してコンテナを起動します。

```bash
$ sudo rkt metadata-service
```

後述の`rkt list`コマンドでコンテナのIPを確認し、アクセスしてみます。
ここでは試しませんが、イメージのビルド時にポートのエントリを定義しておけば、`rkt run`時に`--port`オプションを使用することでポートフォワーディングも実現できます。

```bash
$ curl "http://172.16.28.3:80"
<html><body><h1>It works!</h1></body></html>
```

コンテナを落としたいときは、`Ctrl-]`を3回押します。


## コンテナ一覧

### Docker

```bash
$ docker ps
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                  NAMES
21763385c6bc        httpd:latest        "httpd-foreground"   5 seconds ago       Up 4 seconds        0.0.0.0:8080->80/tcp   trusting_yalow
```

### rkt

```bash
$ sudo rkt list -full
UUID                                    ACI     STATE   NETWORKS
26804adf-6394-469d-bb0c-ff7168202368    httpd   running default:ip4=172.16.28.3
```

## コンテナの作成（ビルド）

### Docker

コンテナの構成内容を記載したDockerfileを指定して`docker build`コマンドでビルドを行います。

① dockerfile作成
下記は簡単な例となりますが、dockerfileは`命令 引数`の形式で記載をします。

```dockerfile:dockerfile
# ベースイメージ
FROM centos
# 作成者
MAINTAINER sample
# 各種コマンドの実行
RUN yum -y install httpd
# コンテナの実行コマンド
CMD /bin/bash
```

② ビルド
dockerfileのディレクトリを指定して`docker build`コマンドを実行します。

```bash
$ docker build -t myimage ./
```

③ イメージ確認

```bash
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
myimage             latest              df2ca7e6d2e1        About a minute ago   263.5 MB
```

### rkt

rktのコンテナイメージはACIフォーマットで管理され、appcで提供されているツール「actool」を使用することでビルドできます。
actoolではコンテナの構成内容を記載したJSON形式のMANIFESTファイルとルートファイルシステムを元にACIをビルドします。

① MANIFEST作成
今回は、公式リファレンスを参考にGO言語製サンプルアプリケーションを実行するACIを作成します。
https://github.com/coreos/rkt/blob/master/Documentation/getting-started-guide.md

```json:manifest
{
    "acKind": "ImageManifest",
    "acVersion": "0.6.0",
    "name": "myimage",
    "labels": [
        {"name": "os", "value": "linux"},
        {"name": "arch", "value": "amd64"}
    ],
    "app": {
        "exec": [
            "/bin/hello"
        ],
        "user": "0",
        "group": "0"
    }
}
```

② ディレクトリ整備

```bash
$ mkdir myimage
$ mkdir myimage/rootfs
$ mkdir myimage/rootfs/bin
# manifest配置
$ mv manifest myimage/
```

③ GOアプリケーション作成

```go:hello.go
package main

import (
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        log.Printf("request from %v\n", r.RemoteAddr)
        w.Write([]byte("hello\n"))
    })
    log.Fatal(http.ListenAndServe(":5000", nil))
}
```

```bash
$ CGO_ENABLED=0 GOOS=linux go build -o hello -a -tags netgo -ldflags '-w' .
$ mv hello myimage/rootfs/bin/
```

④ ビルド

```bash
actool build myimage my-app.aci
```

⑤ コンテナ起動

```bash
$ sudo rkt --insecure-skip-verify run myimage.aci
```

⑥ ホストからアクセス確認

```bash
$ curl "http://localhost:5000"
hello
```

## コンテナの削除

### Docker

CONATINER IDを指定して`docker rm`コマンドを実行します。

```bash
$ docker rm 01fdf4b430b9
01fdf4b430b9
```

### rkt

現時点でコンテナの削除コマンドは存在しないみたいです。

## コンテナイメージの削除

### Docker

IMAGE IDを指定して`docker rmi `コマンドを実行します。

```bash
$ docker rmi de94ed779434
Untagged: httpd:latest
```

### rkt

現時点でコンテナイメージの削除コマンドは存在しないみたいです

# まとめ

簡単ではありますが一通りDockerとrktを触ってみました。
冒頭にも述べたとおりrktはプロトタイプの段階であるため、機能的にも使いやすさ的にもDockerにまだまだ劣ると感じています。
実用という点について現段階では多く語れませんが、Dockerのセキュリティ面での問題を解消したというのは言わずもがな、何よりオープンかつシンプルなrktの思想は素敵だと思います。
2015年5月に「Kubernetes」がrktを通じappcサポートすることを明らかにしたこともあるように、今後の可能性に期待したいと思います。

# 参考

* https://coreos.com/blog/rocket/
* https://github.com/coreos/rkt/blob/master/README.md
* https://github.com/coreos/rkt/blob/master/Documentation
* http://deeeet.com/writing/2015/02/17/docker-bad-points/
* http://deeeet.com/writing/2015/03/12/rocket/
* http://blog.kubernetes.io/