最近Dockerをいろいろ触ってみていて以下の様な記事を書いたりしました。
- Dockerで立てたコンテナにsshで接続する - $shibayu36->blog;
- serfとDockerでクラスタを組んでみる - $shibayu36->blog;
- 本番環境のBlue-Green Deploymentの仕組みのプロトタイプを作っていた - $shibayu36->blog;
- Docker, Mesos, Sensu等を利用したBlue-Green Deploymentの仕組み - $shibayu36->blog;
- 社内用Docker Registryを立てる - $shibayu36->blog;
- docker commitでCMDやENVなどを指定する - $shibayu36->blog;
- docker inspectでDockerコンテナの情報を取得する - $shibayu36->blog;
とは言え僕自身は仮想化やファイルシステムについて全く知識がなくて、LXCとAUFSっていうのを使っていい感じになってるんでしょっていう雑な知識だけでDockerを使っていたので、もうちょっと調べようと思って調べてみました。
このレイヤーに関してはあまり詳しくないので、間違いがあったら指摘していただけると嬉しいです。
Docker
https://github.com/dotcloud/docker/blob/master/README.md#under-the-hood この辺を見ると以下のように書いてあります。
Under the hood, Docker is built on the following components:
・The cgroup and namespacing capabilities of the Linux kernel;
・AUFS, a powerful union filesystem with copy-on-write capabilities;
・The Go programming language;
・lxc, a set of convenience scripts to simplify the creation of Linux containers.
なるほど、とりあえずdockerは
- Goで動いてて
- cgroupとかnamespaceみたいなLinux kernel機能使って
- AUFSというunion filesystemを使ってて
- LXCっていうやつでLinux containersを作ってコンテナを立ちあげている
という感じなんですね。まあよくわからないけどまあなんかいい感じにしてるんでしょう。
というわけでとりあえずAUFSとLXCあたりを調べてみることにします。
AUFSとDocker
AUFSってなんなの?
まずAUFS(というかunion filesystem)ってなんなのかというところですが、AUFS | 日経クロステック(xTECH)や、Docker = LXC + aufs + GitHub Culture - teppeis blogあたりに書いてあったことがとりあえずは導入にはわかりやすかったです。
あと、実際にAUFSを利用している例としてはOSのLiveCDとかが分かりやすいですね。
- CD-ROMに記録してあるOS部分をreadonlyとして利用
- メモリ空間にwritableなfile systemを用意して、それを重ねあわせる
- ファイルシステムに変更があった場合はメモリ空間の方を変更して、書き込みができているように見せる
その他にも仙石浩明の日記: NFS と AUFS (Another Unionfs) を使って、ディスクレス (diskless) サーバ群からなる低コスト・高可用な大規模負荷分散システムを構築するのような使い方をされています。
さらにちゃんと調べるにはhttp://aufs.sourceforge.net/あたりを読んでおくと良さそうだけど、難しそうなので読んでません。
DockerでのAUFS利用
とはいえこれを使ってDockerではどうやってcommitとか差分管理を実現しているの?よくわからん!とか言ってたら、r7kamuraくんが参考情報をくれました。
AUFSによるレイヤーの重ね合わせによって、なぜDockerがファイルシステムを差分管理しているのかがよく分かってない
— 柴崎優季 (@shiba_yu36) 2013年12月30日
差分管理ができている
— 柴崎優季 (@shiba_yu36) 2013年12月30日
@shiba_yu36 この図見れば分かった気になれませんか http://t.co/XrQr8dpCN9
— r7kamura (@r7kamura) 2013年12月30日
@shiba_yu36 次はこれを読んでみましょう http://t.co/4P131Ljwx4
— r7kamura (@r7kamura) 2013年12月30日
@r7kamura 一回ごとのファイルシステムの変更(commit)を一つのレイヤーにしてその後は変更しない、どこからの変更かという親のsha1を持っておいてそれが変わったら作りなおす、というイメージですかね
— 柴崎優季 (@shiba_yu36) 2013年12月30日
@shiba_yu36 そうすね。commitというのは、最上段のread+writeなdiskをread-onlyに変更して、上にまたread+writeなdiskをmountする行為だと思います
— r7kamura (@r7kamura) 2013年12月30日
まずこの図を見ることでわかったような気持ちになりました。http://docs.docker.io/en/latest/terms/layer/参照。
- それぞれのImageやcommitの階層がAUFSの一つの層として扱われている
- コンテナを立ち上げるとその層(最上段の層)がread+writeな層になり、その下の層はread-onlyな層になる
- どの層の上に乗っているかはcommit sha1とかで管理している?
- commitはコンテナの一番上のread+write層をread-onlyにする行為に見える。さらにそこからコンテナを使うとその上にread+writeの層を一層かぶせるように見える
こういう感じで差分管理を実現しているのかなーと思いました。docker pullするとdependency layerとか出てきたり、並列にダウンロード出来るのもこの辺のlayerがあるおかげっぽく見えますね。
更にunion filesystemの仕組みを知るにはUnionMountとUnion-type Filesystem(1) - O'Reilly Japan Community Blogあたりが非常にわかりやすそうでした。
LXCとDocker
LXCってなんなの?
まずLXCってなんなのと思って、一次ソースっぽいところを見ると、
LXC is a userspace interface for the Linux kernel containment features.
Through a powerful API and simple tools, it lets Linux users easily create and manage system or application containers.
って書いてありました。まだなんなのかよくわからないですね。ユーザ空間のインターフェースで、なんかアプリケーションのコンテナとかを作ったり管理したりするのを簡単にできるみたいです。
まあとりあえず日本語の解説見たいと思っていたら、仮想化やLXCについて分かりやすいスライドがありました。Lxc で始めるケチケチ仮想化生活?!
仮想化についてはp5が分かりやすく、p6以降にLXCについて分かりやすい説明がされています。
それによると以下のようなイメージでした。
- LXCはコンテナ型仮想化
- 複数のユーザ空間を作ることで、ユーザプロセスから見えるリソースを分割する
- namespaceの仕組みであるコンテナから他のコンテナのリソースへのアクセス制限
- chrootによるファイルシステム分割
- cgroupsによるコンテナ単位のCPU/メモリ/デバイス制限
- cgroupsについては Control Groups (cgroups) - めもめも
- ホストOSとゲストOSのkernelは共通
- ホストOSからはゲストOSのプロセスがそのまま見える
さらに詳しく知りたい場合はLXC入門 - Osc2011 nagoyaあたりのスライドが分かりやすいです。また実例としてさくらVPSでLXCを動かすみたいなのもありました(さくらVPSでLXCを使って安価に複数台構成を実現する - orangain flavor)。
DockerでのLXC利用
Dockerの場合は結局管理しているAUFSのファイルシステムとLXCを組み合わせて、Dockerコンテナを作っているように見えます。LXCを使えば、ユーザ空間の分離ができるので、コンテナごとにシステムの分離が出来るというイメージなのかな。あとcgroupもできるのでリソース制限とかも簡単に出来るっぽい?
さらに詳しく知るにはDocker を読む - Kato Kazuyoshiあたりを読んだり、ソースを読んだほうが良さそうでした。
疑問
一つ疑問として、kernelをホストOSとゲストOSで共有するなら、それらのOSでkernelのversionが違った場合にどうなるのかというものがありました。
@y_uuk1 LXCってホストOSとゲストOSでkernel自体は共有してるんでしょうか
— 柴崎優季 (@shiba_yu36) 2013年12月30日
@shiba_yu36 はい それは間違いないと思います。カーネルの中で、リソース管理構造体みたいなのをコンテナごとに作ることで、リソースを隔離してるみたいなイメージを持ってます
— yuuk1 (@yuuk1t) 2013年12月30日
@y_uuk1 その場合、例えばホストOSとゲストOSでkernelのversionが違う場合、動かないってことなんですかね
— 柴崎優季 (@shiba_yu36) 2013年12月30日
@y_uuk1 LXC is a userspace interface for the Linux kernel containment features. って書いてあるから、インターフェースが決まっていてそれを満たす限り別のversionでも動くのではないかというイメージ
— 柴崎優季 (@shiba_yu36) 2013年12月30日
@shiba_yu36 ゲストOSのユーザランドが使うシステムコールがホストOSのカーネルで使えてたらOKなイメージなんですかね(よくわかってない
— yuuk1 (@yuuk1t) 2013年12月30日
感覚的にはlxc-startとかその辺がインターフェースとして使われているので、その仕様を満たせばkernelのversionが違っても問題ないという感じなんですかね。
まとめ
今回はDockerで使われているAUFSやLXCなどについて調べてまとめてみました。このへんの内容初めて調べたので間違っているところも多いかもしれません。おかしなところがあれば指摘いただけると嬉しいです。
あともしかしたら id:y_uukiがさらにゲストOSがLXCでどうやって作られるかとか書いてくれるかもしれません。
@shiba_yu36 たぶん、/usr/lib/lxc/templates とかにある LXCのテンプレートとやらを理解すればゲストOSがどうやって作られるかを理解できるような気がしています
— yuuk1 (@yuuk1t) 2013年12月30日
@y_uuk1 すごい
— 柴崎優季 (@shiba_yu36) 2013年12月30日
@shiba_yu36 難しいシェルスクリプトが入ってて、体調悪化しました
— yuuk1 (@yuuk1t) 2013年12月30日