AppcとCoreOS/Rocket
Rocketはリリースした直後にちょっと触ってそのまま放置していた.App containerの一連のツールとRocketが現状どんな感じかをざっと触ってみる.まだまだ全然使えると思えないが今後差分だけ追えるようにしておく.
なお,今回試した一連のツールをすぐに試せるVagrantfileをつくったので触ってみたいひとはどうぞ.
https://github.com/tcnksm/vagrant-appc
概要
App Container SpecやRocketが登場の経緯は前回書いたのでここでは省略し,これらは一体何なのかを簡単に書いておく.
まず,App Container(appc)Specはコンテナで動くアプリケーションの"仕様"である.なぜ仕様が必要かというと,コンテナという概念は今まで存在したが曖昧なものだったため.namespaceやcgroupを使った..という何となくのものはあったが,統一的なものは存在しなかったため.appc specはOpenかつSecure,Composable,Simpleであることを理念に掲げて作成されている.
appcには仕様だけではなくいくつかのツールも提供されている.例えば,appcの元になるApp Container Image (ACI)の構築と検証を行うactool
や,DockerイメージからACIをつくるdocker2aci
,Go言語のバイナリからACIをつくるgoaci
などがある.
では,Rocketは何かというと,そのappcを動かすruntimeの実装の1つである.つまりappcとRocketは別のものであり実装は他にも存在する.例えば,現時点ではFreeBSDのJail/ZFSとGo言語で実装されたJetpackや,C++のライブラリとしてlibappcとそれを使ったruntimeであるNose Coneなどがある.
今回はこれらのappc関連ツールとRocketを実際に触ってみる.
Appc tools
まず,https://github.com/appcにあるAppcの一連のツールを触ってみる.
actoolによるイメージのbuild
actool
はRootファイルシステムとjsonで既述されるmanifestファイルを基にACIをビルドするツール.ビルドだけではなく,manifestやACIが仕様通りであるかの検証を行うこともできる.
例として,以下のGo言語で書かれてサンプルWebアプリケーションを動かすためのACIを作成する.
package main
import (
"log"
"net/http"
)
func main()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Printf("Request from %vn", r.RemoteAddr)
w.Write([]byte("Hello from App Container"))
})
log.Fatal(http.ListenAndServe(":5000", nil))
}
ルートファイルシステムを準備する.
$ mkdir hello
$ mkdir hello/rootfs
$ mkdir hello/rootfs
サンプルアプリケーションを静的リンクでビルドする(go1.4の場合は-installsuffix
が必要).
$ CGO_ENABLED=0 GOOS=linux go build -a -tags netgo -ldflags '-w' -o hello-web
$ file hello-web
hello-web: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
バイナリをRootFS内に配置する.
$ mv hello-web hello/rootfs/bin/.
次にイメージのmanifestファイルを作成する.
$ cat << EOF > hello/manifest
{
"acKind": "ImageManifest",
"acVersion": "0.3.0",
"name": "hello",
"labels": [
"name": "os", "value": "linux",
"name": "arch", "value": "amd64"
],
"app":
"exec": [
"/bin/hello-web"
],
"user": "0",
"group": "0"
}
EOF
actool validate
を使うと仕様通りにmanifestファイルが作成されているかを検証することができる.
$ actool -debug validate hello/manifest
hello/manifest: valid ImageManifest
また同じくactool validate
を使いイメージのレイアウトが仕様通りであるかを検証する.
$ actool -debug validate hello
hello: valid image layout
actool build
でイメージをビルドする.
$ actool build hello/ hello.aci
actool validate
でACIが仕様通りであるかを検証する.
$ actool -debug validate hello.aci
hello.aci: valid app container image
ちなみにACIはただのtarファイルである.
$ tar xvf hello.aci
rootfs
rootfs/bin
rootfs/bin/hello-web
manifest
またappc/specにはACIを実行するApp Container Executor(例えばRocket)を検証するためのACIも提供されている.
$ EXECUTOR run ace_validator.aci
actoolによるイメージのdiscovery
DockerはDockerHubやDocker registryによりコンテナのイメージを配布するということを当たり前にした.が,イメージを配置するだけなのに専用のregistryプロトコルを使わないといけなかったり,イメージがちゃんとダウンロードされたかを検証する方法をちゃんと提供していないなど問題がいくつかある.
appc specでは,インターネット上に配置したACIとその検証を行うための署名のURLをACIの名前から解決する方法も仕様として定めている(この仕様はなかなか面白いので後で別途記事を書く予定).
actool discover
を使うとACIの名前から適切にACIとその署名,公開鍵のURLを見つけられるかを確認することができる(ASC
は署名でKeys
は公開鍵).
$ actool discover -insecure coreos.com/etcd
ACI: https://github.com/coreos/etcd/releases/download/latest/etcd-latest-linux-amd64.aci
ASC: https://github.com/coreos/etcd/releases/download/latest/etcd-latest-linux-amd64.aci.asc
Keys: https://coreos.com/dist/pubkeys/aci-pubkeys.gpg
docker2aci
actoolによるイメージのbuildは現時点では正直しんどい.それに対してDockerはDockerfileで簡単にイメージを作ることができるし,DockerHubには既に多くの良いDockerイメージが存在している.この利点を活かすためにdocker2aciというツールが提供されている.
例えば,Dockerhubにあるcrosbymichael/redis
からACIを作成するには以下を実行すれば良い.crosbymichael-redis-latest.aci
が作成される.
$ docker2aci crosbymichael/redis:latest
Dockerのイメージはやはり便利らしく,CloudFoundryのコンテナruntimeであるGardenもDocker imageをRootFSとして利用できるようにしているらしい(参考: 新しいDiegoの仕組み入門).
ちなみに,逆にDockerでACIを使えるようにするというPRもある(https://github.com/docker/docker/pull/10776).
$ docker pull --format aci coreos.com/etcd:v2.0.0
$ docker run --format aci coreos.com/etcd
が,DockerのCTOのSolomon Hykes氏が「ユーザに何のメリットがあるの?」とかコメントしていて感慨深い.
goaci
上では自分でRootFSを作ってGo言語のサンプルアプリケーションをビルドするなどしたが,go get
のようにGo言語のアプリケーションをACIに変換するツールも提供されている.
$ acigo github.com/coreos/etcd
$GOPATH
を書き換えてgo get
を実行している.そして,静的リンクでコンパイルを実行し,デフォルト値で基本的なmanifestファイルを準備してACIのビルドを行っているだけ.
Rocket
現時点(v0.4.0)のRocketでやれることを一通りやってみる.
trust, fetch
上述したappcのdiscoveryと署名の仕様に従ってインターネット上からイメージを取得することができる.ここでは例としてcoreos.com/etcd
というACIを取得する.
まず,rkt trust
コマンドで取得するACIの公開鍵を取得する.
$ sudo rkt trust --insecure-allow-http --prefix coreos.com/etcd
...
Added key for prefix "coreos.com/etcd" at "/etc/rkt/trustedkeys/prefix.d/coreos.com/etcd/8b86de38890ddb7291867b025210bd8888182190"
次にrkt fetch
コマンドでACIを取得する.署名も同時にダウンロードして上で取得した公開鍵をつかって署名の検証も行う.
$ sudo rkt fetch coreos.com/etcd:v2.0.0
もちろんイメージのURLを知っていればそれをそれを直接指定することもできる.
$ sudo rkt fetch https://github.com/coreos/etcd/releases/download/v2.0.0/etcd-v2.0.0-linux-amd64.aci
run
rkt run
でコンテナを起動する.上でactool
を使って作成したhello.aci
を動かすには以下のようにする.
$ sudo rkt run hello.aci &
コンテナにアクセスしてみる.
$ curl localhost:50000
Hello from App Container
コンテナを殺すには普通にプロセスをkillすればよい(フォアグランドで実行した場合は,^]
を3回叩けば死ぬ).
Dockerと比較した場合のRocketの大きな特徴は中央集権デーモンが存在しないことである.Rocketでコンテナをつくればそれは1つのプロセスとして存在することになる.そのためupstartやsystemdといった既存のツールで個々のコンテナプロセスを管理することができる.
ちなみに署名の検証を無視すればDockerHub上のイメージを使うこともできる
$ sudo rkt --insecure-skip-verify run docker://redis
list, status
rkt list
でコンテナの一覧を確認できる.
$ sudo rkt list
UUID ACI STATE
2bbe6aaa-a41d-43cd-b5b2-ff8058662bb6 hello active
b1c946f5-bd54-43e6-b241-289077adf12f coreos.com/etcd inactive
rkt status
でコンテナの状態(PIDと終了状態)を確認できる.
$ sudo rkt status 2bbe6aaa-a41d-43cd-b5b2-ff8058662bb6
pid=21967
exited=false
gc
rkt gc
で古いinactiveなコンテナを破棄することができる.-grace-period
でinactiveからどれだけ時間の経過したものを破棄するかを指定できる.
$ sudo rkt gc -grace-period=10s
これをsystemdのOnCalendar
で定期実行してゴミ捨て場にならないようにする.
enter
rkt enter
でコンテナのnamespace内に入ることができる.
$ sudo rkt enter 29d47fda-23f5-423f-9457-708d775ee9d9
No command specified, assuming "/bin/bash"
root@rootfs:/#
Rocketのアーキテクチャ
Rocketの内部について簡単にまとめておく.Rocketはrkt
コマンドのみで構成され,Dockerのようなデーモンはない.そのため,既に起動しているコンテナに影響を与えることなくRocketそのものをアップデートすることができる.
Rocketの起動はstage0 -> stage1 -> stage2の3つのstageに分けられる.各stageはモジュラーな構成になっている.これらが具体的に何をしているのかを簡単に説明する.
Stage0
Stage0はrkt
がコンテナを動かすための初期設定を行う.
- ACIの取得
- もし
--stage1-image
が指定されたらStage1のACIの取得(デフォルトはrkt
と同じディレクトリのstage1.aci
) - コンテナのUUIDの生成
- コンテナのRuntime Manifestの生成
- コンテナのためのファイルシステムの作成
- Stage1とStage2用のディレクトリの作成
- Stage1のACIのコンテナファイルシステムへの展開
- ACIの展開とアプリケーションのStage2ディレクトリへのコピー
Stage1
Stage1では,cgroupやnamespaceの設定やプロセスの起動,ホストのルートとしての各種オペレーションを実行する.
- コンテナのRuntime ManifestからsystemdのUnitファイルの生成
- 外部Volumeの準備
- root systemdの起動
以下のようなコマンドを実行している.
stage1/rootfs/usr/lib/ld-linux-x86-64.so.2 \
stage1/rootfs/usr/bin/systemd-nspawn \
--boot \
--register false \
--quiet \
--uuid=81387c20-df38-4e17-9bab-985269148fbb \
--directory=stage1/rootfs \
-- \
--default-standard-output=tty \
--log-target=null \
--show-status=0
systemd-nspawn
を使っているのがわかる.これについては以下が詳しい.
Stage2
アプリケーションの起動.例えば上で作成したhello.aci
の場合は以下のプロセスが起動する.
/bin/hello-web
まとめ
今週末の日曜日(3/15)にrebuild.fmで喋ります.