TenForward

技術ブログ。はてなダイアリーから移転しました

bind mount を使ったお気軽 LXC コンテナのススメ

お手軽に軽量な隔離環境を作るための kazuho さんの jailing とか、それにリソース管理の仕組みを加えた matsumotory さんの virtualing いいですね。

コンテナほどの隔離性は不要だし、一々イメージを落としてきて構築とかやるにはちょっと重たいなというシーンもあるわけで、そういう時に chroot、bind mount はお気軽で良いですし、それに cgroup でのリソース管理を加えてもそんなに重くはなりません。(docker だと他にも色々考えないとダメだし)

こういうのを思いついてサクッと作れてしまう才能がうらやましいわけですが、LXC を使えば才能がない私でも、何かをサクッと作る能力も努力もなく、もう少し隔離度を上げつつある程度の軽さを残した環境を LXC を使って作れますので、ここで紹介しましょう。

LXC 使っちゃったら、前述の軽さがなくなるから意味ないやんって話ですが、LXC ならコンテナ用のファイルシステムを作らずに bind mount を使ってコンテナを起動するのも簡単ですので、環境を作る手間のお手軽さと、bind mount を使ってホストのシステムを共用するというお手軽さは残して環境構築が可能です。

そもそもコンテナって、単に clone システムコールを使ってプロセス起動するだけなので、そんなに重くはならないでしょう、というお話です。もちろん LXC はそれ以外に色々やるので chroot に比べたら重いんですけどね。

bind mount を使った軽量(?)コンテナ

LXC にはコンテナ起動時にコンテナ用に何かをマウントするための設定があります。ここでホストのディレクトリやファイルを bind mount するだけで、ホストのシステムの一部をコンテナにエクスポートできます。

例えばホストの /usr をコンテナでそのまま使う場合、以下のように書けばそれでコンテナ起動時にマウントしてくれます。

lxc.mount.entry = /usr usr none ro,bind 0 0

つまり

  1. コンテナ用 rootfs 以下にディレクトリを作る (bind mount するにはマウントポイントないとダメですね)
  2. LXCの設定を書く
  3. lxc-startコマンドでコンテナ起動

これで jailing と同じような隔離環境が得られます。

LXC から cgroup を使うのも設定ファイルに制限値を設定すれば良いので、virtualing 相当の操作も可能ですね。

テンプレートを使って bind mount を使ったコンテナを作成する

言葉で書くと簡単ですが、ディレクトリを作ったり、設定ファイルを作ったりを手でやってると面倒で、jailing のお手軽さにかなうわけもありません。

そこで作ってみました。

LXC でコンテナを作る時は lxc-create コマンドにテンプレートを指定して作成します。その lxc-create に指定できるテンプレートファイルです。

$ sudo lxc-create -t bind -n (コンテナ名)

みたいにすれば、必要なディレクトリと設定ファイルを作成してくれます。

私の作った lxc-bind は決め打ちのところが多いです。そこはシェルスクリプトで書かれてるお気軽さですので、適当に変えてもらうということで。

もともと LXC には lxc-sshd というテンプレートが付属していて、これはまさしくコンテナのファイルシステムのほとんどをホストのディレクトリを bind mount して、sshd だけが起動するコンテナを作るテンプレートです。lxc-bind はこれを少し変えただけです。

jailing の例にあった /usr/local/apache/httpd を起動するコンテナを作るのも、

$ sudo lxc-create -t bind -n apache01 \
    -- --bind=/usr/local/apache \
    /usr/local/apache/bin/httpd \
    -c /usr/local/apache/conf/httpd.conf
$ sudo lxc-start -n apache01

ってな感じで簡単です (試してませんが :-p)。もちろん、作成したあとは起動させないとダメですので、jailing よりは実行するコマンドは増えます :-p

テンプレートに渡す引数なしで

lxc-create -t bind -n test01
みたいに実行すると bash を実行するコンテナを勝手に作ります。他に dhclient を実行して勝手に eth0 にアドレスも割り当てます (lxc.network.ipv4 みたいな設定で静的に割り当てることも可能)。
実行例
$ sudo lxc-create -t bind -n test01
$ sudo cat /var/lib/lxc/test01/config
lxc.network.type=veth
lxc.network.link=lxcbr0
lxc.network.flags=up
lxc.rootfs = /var/lib/lxc/test01/rootfs
lxc.utsname = test01
lxc.pts = 1024
lxc.cap.drop = sys_module mac_admin mac_override sys_time
lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
lxc.mount.entry = /etc/rc.d etc/rc.d none ro,bind 0 0
lxc.mount.entry = /etc/ssl/certs etc/ssl/certs none ro,bind 0 0
lxc.mount.entry = /dev dev none ro,bind 0 0
lxc.mount.entry = /run run none ro,bind 0 0
lxc.mount.entry = /bin bin none ro,bind 0 0
lxc.mount.entry = /sbin sbin none ro,bind 0 0
lxc.mount.entry = /usr usr none ro,bind 0 0
lxc.mount.entry = /lib lib none ro,bind 0 0
lxc.mount.entry = /lib64 lib64 none ro,bind 0 0
lxc.mount.entry = /var/lib/lxc/test01/init sbin/init none ro,bind 0 0

作成するとこんな感じに。

$ sudo lxc-start -n test01 
$ sudo lxc-ls -f test01
NAME    STATE    IPV4         IPV6  GROUPS  AUTOSTART  
-----------------------------------------------------
test01  RUNNING  10.0.100.41  -     -       NO
$ pstree
  :(略)
     |-lxc-start---init.lxc-+-bash
     |                      `-dhclient
  :(略)
$ sudo lxc-attach -n test01 -- ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0  17272   596 ?        S    11:28   0:00 /usr/sbin/init.lxc -- /bin/bash
root        18  0.0  0.0  18984  6852 ?        Ss   11:28   0:00 /usr/sbin/dhclient eth0 -cf /dhclient.conf -v
root        19  100  0.0  20264  3144 ?        R    11:28  12:00 /bin/bash
root        21  0.0  0.0  16612  2624 ?        R+   11:40   0:00 ps aux

起動してますね。コンテナ内は bash と dhclient が動いています。

コンテナ内のマウントの様子を見てみると

$ sudo lxc-attach -n test01 -- cat /proc/mounts
  :(略)
/dev/root /etc/rc.d ext4 ro,relatime,data=ordered 0 0
/dev/root /etc/ssl/certs ext4 ro,relatime,data=ordered 0 0
/dev /dev tmpfs ro,relatime,mode=755 0 0
/dev/root /run ext4 ro,relatime,data=ordered 0 0
/dev/root /bin ext4 ro,relatime,data=ordered 0 0
/dev/root /sbin ext4 ro,relatime,data=ordered 0 0
/dev/root /usr ext4 ro,relatime,data=ordered 0 0
/dev/root /lib ext4 ro,relatime,data=ordered 0 0
/dev/root /lib64 ext4 ro,relatime,data=ordered 0 0
/dev/mapper/LXCVG01-LXCLV01 /sbin/init btrfs ro,relatime,space_cache 0 0
  :(略)

lxc.mount.entry で設定した辺りは上記でしょうかね。

ruby-lxc を使って同じようなことをやる

LXC には ruby-lxc というものがあって、これでスクリプトを書けば同じようなことが比較的簡単にできます。これをやるのが

です。シャレで作っただけなのでこれを使おうと思わないでください。^^;

こっちは --bind みたいなオプションも未実装なので使えないと思います。コンセプト作と思ってください。(ruby-lxc を使ってみたかっただけ)

$ sudo ruby lxcing /bin/bash

まとめ

延々とわけのわからないことを書きましたが、何が言いたいかというと、LXC お手軽だからもっとみんな使いましょう、ってことです。

おまけ

LXC 使っちゃうとまあ色々プロセス起動したり、ファイルシステム色々扱ったりするので、声を大にして「軽いぞ!!」とは言えない気もするのですが、libcontainer とか libct とか使ったら bind mount して気軽に隔離環境作るプログラムってすぐ作れないかな? 誰か作るといいですね。