2013/08/29

FedoraでLXCを使う

はじめに

LXCはLinux上で複数の仮想的なLinuxを動作させることのできるOSレベルの仮想化技術です。一言で言えば、chroot に cgroups でのリソース管理を追加して強化したようなものです。Qemu/KVMのようなエミュレーションを行う仮想化よりもオーバヘッドが少なく、軽量です。Heroku のような PaaS 業者は、LXCの利点を生かし高集約なサービスを提供しています。さらにはJoe's Web Hostingのように、VPSをLXCで提供しているサービスすらあります。しかし、LXCではQemu/KVMとは違い、ホストとゲストで異なるOSを動作させることができません。仮想化による性能劣化が低い一方、柔軟性は一歩劣ると言えます。
今回は Fedora 19 で手軽に LXC を使う方法をまとめました。(Fedora18でも可能ですが、virt-manager が安定しないので、Fedora19以降がお勧めです)

環境構築

LXCでコンテナを構築する方法は大きく2つ有ります。一つは LXC 公式ツールキットを使う方法です。これは、最近はやりのdockerでも使われている方法なのですが、Fedora との相性がいまいちです。もう一つは、Qemu/KVMの管理でもおなじみのlibvirtを使う方法です。libvirtを使うと、仮想ネットワークの管理(DHCP, NAT)を含む仮想マシンの操作を、Qemu/KVMと同じ感覚で扱えるので、Qemu/KVMに慣れた方にはとてもおすすめです。今回はlibvirtでLXCを扱う方法について説明します。

root 環境のインストール:
yum で installroot を指定することで、init を実行する rootfs を構築できます。debootstrap と同じようなものです。
# yum -y --releasever=19 --nogpg --installroot /home/eiichi/lxc/base install systemd passwd yum fedora-release vim-minimal openssh-server procps-ng iproute dhclient
# chroot /home/eiichi/lxc/base /bin/passwd root

LXCは標準で/dev/pts/0 を使うので、securettyに追記して root でログインできるようにしておきます。
# echo "pts/0" >> /home/eiichi/lxc/base/etc/securetty

libvirtで使う:
virt-manager 経由で使うのが便利です。
ホストマシンに接続した後、新規仮想マシンの作成ボタンを押し、"コンテナーの種類"で"オペレーティングシステムコンテナ"を選択します。"既存のOSルートディレクトリを指定してください"ダイアログで、root環境を指定します。上記の例では、"/home/eiichi/lxc/base"となります(画像参照)。

あとは流れに沿って進めば、コンテナが起動します。

ネットワーク設定:
無事ログインできたら、ネットワーク設定を確認します。下記コマンドで veth ネットワークデバイスがみえるはずです。
-bash-4.2# ip -d l
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
13: eth0:  mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
    link/ether 00:16:3e:0b:ea:1c brd ff:ff:ff:ff:ff:ff
    veth 
さらに、libvirtのdefaultネットワークを用いているのであれば、dhcpでipをもらえるはずです。
# dhclient eth0
これで、NATで外部に接続することが出来るようになります。
全て完了しました。とても簡単です。

おまけ: btrfs での運用
上記では一つのコンテナを作る際の手順について説明しましたが、複数のコンテナを作る場合はどうでしょう。rootfs をコピーして使うのでは、各コンテナでrootfsの内容がほとんど変わらないのでディスク容量の無駄です。こんな時は、COW(Copy on Write)なbtrfsのsnapshotを使って無駄を省きましょう。内容としては、dockerがUnion FS(AUFS)を用いてやっていることとほぼ同等です。

btrfsの準備:
普段btrfsを使っていない場合は、loopback device で btrfs を利用する準備をしましょう。
# dd if=/dev/zero of=./btrfs.img bs=1M count=10k
# losetup /dev/loop0 btrfs.img
# mkfs.btrfs /dev/loop0
# mount -t btrfs /dev/loop0 ./mnt
これで./mnt で btrfs が使えるようになりました。base となるコンテナとして、subvolumeを切り、そこに rootfs を構築します。
# cd mnt
# btrfs subvolume create base
# yum -y --releasever=19 --nogpg --installroot /home/eiichi/lxc/mnt/base install systemd passwd yum fedora-release vim-minimal openssh-server procps-ng iproute dhclient
コンテナを新しく作る際には、base subvolume の snapshot を作成して構築します。
# btrfs subvolume snapshot base f19-1
あとは、作ったsnapshot directoryをvirt-managerでOSコンテナとして登録すればO.K.です。

まとめ
Fedoraにおいて、libvirtを使ってLXCを使う方法について説明しました。LXCは軽量な仮想化技術であり、とても簡単に管理できます。また、btrfs の snapshot と組み合わせることで、非常に効率的な運用が可能になります。

参考文献
Daniel P. Berrangé: Running a full Fedora OS inside a libvirt LXC guest
Stefan Hajnoczi: Thoughts on Linux Containers (LXC)
btrfs wiki: btrfs(command)

2013/08/03

SystemTap 埋め込みC関数のAPI変更について

はじめに

SystemTapはスクリプト内にC言語の関数を埋め込む機能を備えています。カーネル内の変数について詳しく調査したり、変数の内容を変更したりする際に埋め込みC関数がとても便利です。SystemTap 1.8 で埋め込みC関数内でのローカル変数アクセス方法が変更になりましたので、まとめておきます。さらに詳しい情報はSystemTap の NEWS に記載されています。

従来API(1.7以前)

従来、ローカル変数にアクセスする際、"THIS->var" 、"THIS->__retvalue" を用いていました。例えば以下のような感じです。
function add_one:long (val:long) %{
        THIS->__retvalue = THIS->val + 1;
%}

 新API(1.8以後)

新APIでは "THIS->var" ,"THIS->__retvalue" の代わりにマクロ "STAP_ARG_var", "STAP_RETVALUE" を用います。
function add_one:long (val:long) %{
        STAP_RETVALUE = STAP_ARG_val + 1;
%}
APIが変更された理由は、tapset によりインクルードされたヘッダとの変数名の衝突を防ぐためです。詳しくはSources Bugzilla – Bug 10299をご覧ください。

移行方法

1.7以前のAPIで書かれたstpスクリプトを1.8以後のSystemTapで実行すると、下記のようなエラーが起きるため、SystemTap のバージョンを1.8以後に移行する際には、なんらかの対処が必要になります。
/tmp/stapHdE3nB/stap_1f8c58b66994d073c51471dcf3f703ba_1070_src.c: In function 'function_add_one':
/tmp/stapHdE3nB/stap_1f8c58b66994d073c51471dcf3f703ba_1070_src.c:112:25: error: 'struct function_add_one_locals' has no member named 'val'
make[1]: *** [/tmp/stapHdE3nB/stap_1f8c58b66994d073c51471dcf3f703ba_1070_src.o] Error 1
make: *** [_module_/tmp/stapHdE3nB] Error 2
WARNING: kbuild exited with status: 2
Pass 4: compilation failed.  [man error::pass4]

移行方法1.  --compatible=1.7 オプションの利用

systemtap を実行する際に、--compatible=1.7 オプションをつけることで、スクリプトを変更せずに済みます。

移行方法2. /* unmangled */ pragma の利用

systemtap スクリプトの埋め込みC関数に /* unmangled */ プラグマを付与することで、従来APIと新APIを混在させることができます。
function add_one:long (val:long) %{ /* unmangled */ 
        THIS->__retvalue = THIS->val + 1;
%}

余談

この件、実はTwitter上で埋め込みC関数APIの変更を嘆いていた時に、SystemTap 主要開発者の Frank Ch. Eigler さん(@fche)から教えていただきました。 Frank さん、どうもありがとうございました。

参考文献

SystemTap Language Reference: 3.5 Embedded C
systemtap/NEWS