runCをひと通り使ってみた
runCが発表されてから1ヶ月ほど経ちました。 使ってみた系の記事が幾つか出ていますが、ぼくもとりあえずひと通り触ってみたのでやってみたことを簡単にまとめます。
runCとは
そもそもrunCとは、Open Container Project によって定義された Open Container Format のランタイムです。 詳しくは下記の記事をご参照頂くとわかるかと思います。
runC を動かしてみる
https://runc.io/ に Getting started が載ってますので、こちらに沿ってとりあえず動かしてみます。 なお今回は物理マシン上の trusty で検証しています。
インストール
2015/08/01 時点で v0.0.2 までタグが打たれていますが、runc checkpoint
でコケたので master ブランチにチェックアウトします。
ここで試したのは
https://github.com/opencontainers/runc/tree/602e8331a0cad8e8aef18e2c962dea77a696f04d になります。
$ git clone [email protected]:opencontainers/runc.git $ cd runc $ make $ make install
バイナリでは提供されていませんが、make
は数秒で終わるのでインストールは簡単です。
rootfs の準備
debootstrap などを利用してもいいのですが、既存のDockerイメージを利用すると楽なので、こちらを使います。
$ docker pull memcached $ docker export memcached > memcached.tar $ mkdir -p memcached/rootfs $ tar xvf memcached.tar -C memcached/rootfs
runc 起動
$ cd memcached $ runc spec > config.json $ sudo runc # ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 4328 764 ? Ss 18:35 0:00 sh root 7 0.0 0.0 17492 2080 ? R+ 18:36 0:00 ps aux
runc spec
で生成されるデフォルトの設定ファイルを利用すると、コンテナで sh コマンドが実行されてterminalに入ります。
中で ps コマンドを実行すると、PID 1 のプロセスが sh になっていることが確認できます。
memcachedコンテナを固定IPで起動してみる
ネットワークの準備
こちらを参考に必要なものを用意します。
$ sudo brctl addbr runc0 $ sudo ip link set runc0 up $ sudo ip addr add 192.168.10.1/24 dev runc0 $ sudo ip link add name veth-host type veth peer name veth-guest $ sudo ip link set veth-host up $ sudo brctl addif runc0 veth-host $ sudo ip netns add runc $ sudo ip link set veth-guest netns runc $ sudo ip netns exec runc ip link set veth-guest name eth1 $ sudo ip netns exec runc ip addr add 192.168.10.101/24 dev eth1 $ sudo ip netns exec runc ip link set eth1 up $ sudo ip netns exec runc ip route add default via 192.168.10.1
細かい説明は省きますが、ここで出来上がるものは下記のものです(Dockerと同じ構成ですね)。
- runc0(bridge)
- veth-host(interface)
- runc(network namespace)
- eth1(interface)
なお、runc0 と eth0 のルーティングの設定は行っていないので、コンテナからホスト外のネットワークには出れませんが、今回はそこの検証はしないのでよしとします。
runc 設定変更
config.jsonを下記のように修正します。 起動時に memcached のコマンドを実行するようにし、さらに上記で作った netns を割り当てています。
@@ -5,14 +5,16 @@ "arch": "amd64" }, "process": { - "terminal": true, + "terminal": false, "user": { "uid": 0, "gid": 0, "additionalGids": null }, "args": [ - "sh" + "/usr/local/bin/memcached", + "-u", + "root" ], "env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", @@ -22,7 +24,7 @@ }, "root": { "path": "rootfs", - "readonly": true + "readonly":false }, "hostname": "shell", "mounts": [ @@ -113,7 +115,7 @@ }, { "type": "network", - "path": "" + "path": "/run/netns/runc" }, { "type": "ipc",
runc 起動 / 確認
$ sudo runc
別のターミナルを起動して接続確認をしてみます。
$ telnet 192.168.10.101 11211 Trying 192.168.10.101... Connected to 192.168.10.101. Escape character is '^]'. set test 0 0 4 test STORED get test VALUE test 0 4 test END quit Connection closed by foreign host.
無事に起動しているようです。
memcachedコンテナをホストIPで起動してみる
namespace の network
の設定を削除します。
@@ -122,10 +122,6 @@ "path": "" }, { - "type": "network", - "path": "/run/netns/runc" - }, - { "type": "mount", "path": "" }
runc を起動して
$ sudo runc
別ターミナルで確認してみます。
$ telnet localhost 11211 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. set test 0 0 4 test STORED get test VALUE test 0 4 test END quit Connection closed by foreign host.
問題ないようです。
checkpoint / restore を試す
runC は 最初から CRIU( http://criu.org/Main_Page ) に対応している点が特徴的と言えます。 CRIUはプロセスの停止/再開を行うためのツールであり、コンテナの状態を保持しつつ停止/再開(別ホストで起動など)が出来るようになります。
コンテナ(Docker) と CRIU の組み合わせの事例としては下記のようなものがあります。
www.slideshare.netcheckpoint
checkpoint をするにあたって下記の設定に注意してください(デフォルトではどちらも true
になっています)。
- process の terminal が
false
になっているか - root の readonly が
false
になっているか
とりあえず runc を起動します。
sudo runc
checkpoint/restoreの検証用に別ターミナルにてmemcachedにデータを保存しておきます。
$ telnet localhost 11211 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. set test 0 0 4 test STORED quit Connection closed by foreign host.
checkpoint を実行します
$ sudo runc checkpoint $ tree checkpoint checkpoint/ ├── cgroup.img ├── core-1.img ├── core-5.img ├── core-6.img ├── core-7.img ├── core-8.img ├── core-9.img ├── creds-1.img ├── descriptors.json ├── eventpoll.img ├── fdinfo-2.img ├── fs-1.img ├── ids-1.img ├── inetsk.img ├── inventory.img ├── ipcns-var-8.img ├── mm-1.img ├── mountpoints-10.img ├── pagemap-1.img ├── pages-1.img ├── pipes-data.img ├── pipes.img ├── pstree.img ├── reg-files.img ├── sigacts-1.img ├── tmpfs-dev-48.tar.gz.img ├── tmpfs-dev-50.tar.gz.img ├── tmpfs-dev-51.tar.gz.img ├── unixsk.img └── utsns-9.img 0 directories, 30 files
checkpoint ディレクトリに状態が保持されていることが確認できます。
restore
$ sudo runc restore
別ターミナルにて確認します。
$ telnet localhost 11211 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. get test VALUE test 0 4 test END quit Connection closed by foreign host.
データを保持した状態で restore されたことが確認できました。
コンテナの状態を監視する
events コマンドで状態を確認できます。
sudo runc events
下記のような情報が取れます。
{ "type": "stats", "id": "memcached", "data": { "Interfaces": null, "CgroupStats": { "cpu_stats": { "cpu_usage": { "total_usage": 58612299, "percpu_usage": [ 22633414, 12378373, 22347540, 1252972 ], "usage_in_kernelmode": 10000000, "usage_in_usermode": 20000000 }, "throttling_data": {} }, "memory_stats": { "cache": 40960, "usage": { "usage": 40960, "max_usage": 1953792, "failcnt": 0 }, "swap_usage": { "failcnt": 0 }, "kernel_usage": { "failcnt": 0 }, "stats": { "active_anon": 40960, "active_file": 0, "cache": 40960, "hierarchical_memory_limit": 18446744073709552000, "inactive_anon": 0, "inactive_file": 0, "mapped_file": 0, "pgfault": 608, "pgmajfault": 0, "pgpgin": 525, "pgpgout": 515, "rss": 0, "rss_huge": 0, "total_active_anon": 40960, "total_active_file": 0, "total_cache": 40960, "total_inactive_anon": 0, "total_inactive_file": 0, "total_mapped_file": 0, "total_pgfault": 608, "total_pgmajfault": 0, "total_pgpgin": 525, "total_pgpgout": 515, "total_rss": 0, "total_rss_huge": 0, "total_unevictable": 0, "total_writeback": 0, "unevictable": 0, "writeback": 0 } }, "blkio_stats": {}, "hugetlb_stats": { "2MB": { "failcnt": 0 } } } } }
コンテナのデータ/情報はどこにあるか
/run/oci/<コンテナID> にあります。
/run/oci/memcached ├── criu.work │ ├── dump.log │ ├── restore.log │ ├── stats-dump │ └── stats-restore └── state.json 1 directory, 5 files
ちなみにコンテナIDには runc を起動したディレクトリ名がデフォルト値として使われるようです。 何かしらのバグを踏んだり、正しく終了しないとここにデータが残り、再度起動するときにこけたりするので、その場合はここのディレクトリごと削除すればうまくいきます。
コンテナの中に入る
docker exec
のようなコマンドは用意されていないので、nsenter( https://github.com/jpetazzo/nsenter ) を利用します。
nsenter は各 namspace に入るためのツールで、runc でも問題なく利用できます。
まず起動しているコンテナの namespace を確認します。
$ sudo cat /run/oci/memcached/state.json | jq '.namespace_paths' { "NEWIPC": "/proc/4107/ns/ipc", "NEWNET": "/proc/4107/ns/net", "NEWNS": "/proc/4107/ns/mnt", "NEWPID": "/proc/4107/ns/pid", "NEWUSER": "/proc/4107/ns/user", "NEWUTS": "/proc/4107/ns/uts" }
PID 4107 で起動しているのでここに入ります。
$ sudo /usr/bin/nsenter --target 4107 --mount --uts --ipc --net --pid root@shell:/# ps auxf USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 17 0.0 0.0 20256 3200 ? S 13:52 0:00 -sh root 20 0.0 0.0 17488 2064 ? R+ 13:52 0:00 \_ ps auxf root 1 0.0 0.0 322152 1664 ? Ssl 13:30 0:00 /usr/local/bin/memcached -u root
無事に入れました。
さいごに
現時点でrunCでできる事をひと通り試してみました。 今回検証するにあたってもバグを幾つか踏んだので、本格的に利用できるのはまだまだ先になるかと思います(出て来たばかりなので当たり前ですが)。 ここまでシンプルなものであれば、既存のツールとの連携や、新規ツール等を開発するなど容易にできそうです。 コンテナを利用するためのひとつの選択肢にはなり得るので今後もチェックしていこうかと思います。