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



2013/07/21

VirtFS で Qemu ゲストホスト間ファイル共有

はじめに

Qemu/KVM 環境において、ホストゲスト間でのファイル共有ができると、とても便利です。例えば、開発中の Linux Kernel をテストする時には、ホストのコンパイル済み Kernel ソースディレクトリをゲストでマウントし、Kernel のインストールができると捗ります。ファイル共有方法には NFS、CIFS、SSHFS などがありますが、Qemu にはより効率的な "VirtFS" という仕組みがあります。
VirtFS は、ゲストの Linux マシンと virtio-9p デバイスを通じてファイル共有する仕組みです。ゲストホスト間で共有するリングバッファへの読み書きでデータをやり取りするため、他のネットワークファイルシステムなどより効率が良いのです。
今回は virt-manager での VirtFS を使ったファイル共有設定方法についてご紹介します。
Fedora 18で検証しています。(Fedora 15以上であれば Qemu が対応しています。)

virt-manager でのホストゲスト間ファイル共有設定

仮想マシン詳細を開き、「ハードウェアを追加」で 「FileSystem」を選択します。

ファイルシステム・パススルーの各項目を設定していきます。
上記の設定のように設定し、ゲストを起動しましょう。

「ターゲットパス」に指定したワードが、ゲスト上で 9pfs をマウントする際のマウントタグになります。

ゲストでのマウント
マウントタグの確認
# cat /sys/bus/virtio/drivers/9pnet_virtio/virtio<n>/mount_tag
source_tag
マウント
# mkdir source
# mount -t 9p -o trans=virtio source_tag ./source/
これでゲストから /path/to/source_dir がみえるようになりました。

注意

上記の設定では、ゲストからの共有ディレクトリへの書き込みが Permisson Denied になってしまいます。これは、qemu 起動ユーザが共有ファイルへのアクセス権を持っていない時に生じます。書き込みできるようにするためには、qemu を root で起動する必要があります。

qemu を root で起動するための libvirt 設定
/etc/libvirtd/qemu.conf
user = "root"
group = "root"
をコメントアウトし、libvirtd を再起動。

参考文献

Documentation/filesystems/9p.txt
libvirt: Domain XML format
Qemu Wiki : 9psetup
VirtFS LPC 2010
KVM日記 : Rootfs over Virtfsでゲストを起動する

2013/07/18

virsh で 仮想マシンのスナップショットを取る

はじめに

仮想マシン上で頻繁に環境構築・破壊を繰り返す場合、仮想マシンのスナップショットを利用し、素早くディスク状態をもとに戻せると便利です。libvirt, Qemu/KVM は仮想マシンのスナップショット機能を実装しており、とても有用です。今回は virsh コマンドでのスナップショットの扱い方をご紹介します。検証環境は Fedora 18です。

スナップショットの種類

libvirt, Qemu が実装している仮想マシンスナップショットの種類には、以下の2種類がありあます。

- 1. 内部スナップショット
- 2. 外部スナップショット

1. 内部スナップショットは仮想マシンのスナップショットを一つの qcow2 ファイルで管理する方式です。スナップショット取得中は仮想マシンは一時停止状態になります。仮想マシンのディスクのスナップショットのみならず、RAM 状態やデバイス状態などの仮想マシン状態も保存できます。

2. 外部スナップショットは仮想マシンのスナップショットを外部の qcow2 ファイルで管理します。なんと、仮想マシンを停止することなくスナップショットを取得できます。仮想マシンディスク以外の仮想マシン状態を保存することは、今のところできません。また、今のところ、仮想マシン停止中にはスナップショットを取ることができません。
現状動作が安定しておらず、非常に実験的な機能です。

以下、仮想マシンの名前を vm1 として、virsh コマンドの使い方を説明します。

内部スナップショット

内部スナップショットの作成
# virsh snapshot-create-as vm1 snap1 "snap1 description"
ドメインのスナップショット snap1 が作成されました
内部スナップショットは仮想マシン稼働中でもスナップショットを作成できます(ただし、安定していません)。作成している間は、仮想マシンは一時停止状態になります。ストレージ性能や仮想ディスク容量にもよりますが、作成時間は数分かかります。

内部スナップショット確認
# virsh snapshot-list vm1
 名前               作成時間              状態
------------------------------------------------------------
 snap1                2013-07-18 16:43:11 +0900 running

内部スナップショット復元
# virsh snapshot-revert vm1 snap1
スナップショットの復元についても、仮想マシン稼働中に実行可能です。ただし、復元中、仮想マシンは一時停止状態になります。

内部スナップショット情報の取得
指定のスナップショット情報を取得する際のコマンドは以下です。
# virsh snapshot-info vm1 snap1
名前:         snap1
ドメイン:   vm1
カレント:   はい (yes)
状態:         running
親:            -
子:            0
子孫:         0
メタデータ: はい (yes)

スナップショット復元後は下記コマンドで現時点でどのスナップショットを使用しているか確認できます。
# virsh snapshot-info vm1 --current
名前:         snap1
ドメイン:   vm1
カレント:   はい (yes)
状態:         running
親:            -
子:            0
子孫:         0
メタデータ: はい (yes)

スナップショット XML ファイルのダンプ
仮想マシンに関する設定情報(XML ファイル)を含んでいます。下記コマンドで設定情報を出力できます。
# virsh snapshot-dumpxml vm1 snap1

スナップショットの削除
# virsh snapshot-delete vm1 snap1


外部スナップショット

外部スナップショット作成
# virsh snapshot-create-as vm1 disksnap1 "disksnap1 description" --disk-only --atomic
ドメインのスナップショット disksnap1 が作成されました
外部スナップショットは仮想マシン実行中のみ取得可能です。内部スナップショットとは異なり、仮想マシンを停止(一時停止)することなく取得可能(Live Snapshot)です。つまり、仮想マシン無停止での Live Backup が可能です。
外部スナップショット作成後はディスクスナップショットイメージが作成され、current snapshot が作成したスナップショットになります。
# virsh snapshot-info vm1 --current
名前:         disksnap1
ドメイン:   vm1
カレント:   はい (yes)
状態:         disk-snapshot
親:            -
子:            0
子孫:         0
メタデータ: はい (yes)

外部スナップショット確認
# virsh snapshot-list vm1
 名前               作成時間              状態
------------------------------------------------------------
 disksnap1            2013-07-18 17:39:44 +0900 disk-snapshot
 snap1                2013-07-18 16:43:11 +0900 running

外部スナップショットが作成されると、仮想マシンイメージファイルを格納してあるディレクトリ(デフォルトでは /var/lib/libvirt/images)にスナップショットファイル(vm1.disksnap1)が新たに作成されます。
仮想マシンは新たに作成されたスナップショットファイルを使用するようになります。
# virsh domblklist vm1
ターゲット ソース
------------------------------------------------
vda        /home/eiichi/vmimg/vm1.disksnap1
hdc        -

外部スナップショット復元
外部スナップショットの復元は、virsh edit で仮想マシン設定 XML ファイルを開き、disk タグの source タグのfile 属性を復元したいディスクスナップショットに指定します。現状では ディスクスナップショットへの snapshot-revert は対応していないようです。
# virsh snapshot-revert vm1 disksnap2

エラー: サポートされない設定: 外部ディスクスナップショットへの復元はまだサポートされていません

注意
外部スナップショットはまだまだ開発段階の機能です。無停止でスナップショットが取れますが、動作が安定しないのが難点です。また、内部スナップショット機能についても、仮想マシン起動中のスナップショット取得はやはり安定して動作しないことがあります。
安定した動作を希望する場合、一番安全な、"仮想マシン停止時"  の "内部スナップショット" をおすすめします。

参考文献
fedoraproject : Features/Virt Live Snapshots
QEMU wiki : Features/Snapshot
libvirt : Snapshot XML Format
kashyapc fedorapeople : snapshot handout

2013/07/16

Linux で VXLAN を使う

はじめに

VXLAN は VMware、Cisco、Redhat などが推進している VLAN に替わるネットワーク論理分割のための規格です。従来、IaaSなどのクラウド環境において、マルチテナントを実現するためには 802.1Q VLAN を用いるのが一般的な解決策でしたが、この VLAN には VLAN ID が 12bit しかないため、最大 4096 セグメントの分離しかできない、という問題があります。
VXLAN はこの問題を解決します。VLAN ID に対応する VNI(VXLAN Network Identifier) に 24bit を設け、 1,677万セグメントの論理分割を実現します。

VXLAN 類似の技術には Microsoft、Intel、Dell などが推進している NVGRE(Network Virtualization using Generic Routing Encapsulation) があります。実装の進み具合で判断すると、やはり VXLAN のほうが勢いがあるため、今後 L2 over L3 を実現するネットワーク論理分割の主流は VXLAN になる、と個人的には思っています。

今回はこの VXLAN の Linux での使い方をご紹介します。

環境

Qemu/KVM を利用した仮想環境で実施しています。
下記のような簡単な環境です。
|VM A(192.168.10.2/24)| --- | vbr | --- |VM B(192.168.10.3/24)|
VM A、VM B の二台が物理マシン上に作った仮想ブリッジに接続されています。
VM A、VM B の間に VXLAN で 仮想ネットワークを構築します。

使い方

VXLAN の実装はユーザ空間版もあるのですが、ここでは Linux Kernel での実装を使います。前準備として、Linux Kernel のバージョンが 3.7 以上である必要があります。
関連コミット:
vxlan: virtual extensible lan

iproute2 コマンドスイートのバージョンが低いと、VXLAN がサポートされていません。その場合は最新版をソースからコンパイルして入れましょう。VM A および VM B で実施します。
# git clone git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git
# cd iproute2
# ./configure
# make
# make install
# ip link help
(snip)
TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can |
          bridge | ipoib | ip6tnl | ipip | sit | vxlan }
TYPE に vxlan が含まれていれば、OKです。
もし、iproute2 のコンパイル時に "db_185.h がない" というエラーがでた場合は libdb-devel をインストルしましょう。
# yum install libdb-devel

さて、VXLAN を張る作業に入ります。
VM A、 B 上で下記のコマンドを入力します。
# ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev eth0
VXLAN は多くのトンネリング技術とことなり、1対Nでのトンネリングを行います。そのため、マルチキャストアドレスを指定します。
# ip -d link show vxlan0
4: vxlan0:  mtu 1450 qdisc noop state DOWN mode DEFAULT
    link/ether ba:ea:4d:a8:72:82 brd ff:ff:ff:ff:ff:ff promiscuity 0
    vxlan id 42 group 239.1.1.1 dev eth1 port 32768 61000 ageing 300
# ip link set up vxlan0
# ip maddr
1:  lo
(snip)
2:  eth0
(snip)
    inet  239.1.1.1
3:  vxlan0
(snip)
ip maddr で 239.1.1.1 が表示されていれば、適切にアドレス設定できていると確認できます。

VXLAN デバイスに アドレスを振って、疎通確認をします。
On VM A
ip a add 192.168.42.2/24 dev vxlan0

On VM B
ip a add 192.168.42.3/24 dev vxlan0

疎通確認
On VM A
ping 192.168.42.3
あなたの予想に反せずに pong が返っているでしょうか。pong が返らないようであれば、VM A,B および物理マシンの firewall 設定を確認してみてください。

下記に ping を送った際の wireshark 通信ダンプ結果を載せておきます。

最初に UDP Multicast で 239.1.1.1 に送信され、VM A の VXLAN トンネル終端がMAC アドレス学習後は Unicast で通信していることがわかります。

fdb は下記コマンドで確認できます。
On VM A
# bridge fdb show dev vxlan0
9e:03:cd:ab:b2:91 dst 192.168.10.3 self

参考文献

Documentation/networking/vxlan.txt
VXLAN: A Framework for Overlaying Virtualized Layer 2 Networks over Layer 3 Networks draft-mahalingam-dutt-dcops-vxlan-04
IPA : VXLAN/NVGREによるネットワーク分離

2013/07/15

iproute2 コマンドでルーティングテーブル/アドレス設定の保存/復元

はじめに

iproute2 はLinuxでネットワーク関係の設定を変更するためのコマンドスイートです。もはや非推奨となった ifconfig の代替として利用が推奨されており、ifconfig コマンドではでは行えない設定も可能です。iproute2 コマンドスイートの中には、ルーティングやアドレス設定を行う ip、トラフィック制御を行う tc、ネットワーク統計情報を取得する lnstat, ifstat コマンドなどが含まれます。iproute2 には非常に多くの機能が含まれているのですが、linux-net に併せて開発がとても早く、ドキュメントの整備が追いついていないのが難点です。

今回は ip コマンドを用いてルーティングテーブルおよび、アドレス設定の保存/復元を行う方法についてご紹介します。仮想ネットワークの構築を行うとき等に、ネットワーク設定を素早く手軽に復元できるこれらの機能が有用です。

設定の保存

設定のルーティングテーブル/アドレス設定の保存はそれぞれ ip route/ ip addr コマンドにより行います。
設定はバイナリ形式です。
ルーティングテーブルの保存:
% ip route save > iproute.conf.bin

アドレス設定の保存:
% ip addr save > ipaddr.conf.bin

設定の確認

設定はバイナリ形式なので、人手での確認は showdump オプションを使います。
% ip addr showdump < ipaddr.conf.bin
if1:
    inet 127.0.0.1/8 scope host lo
if2:
    inet 192.168.122.132/24 brd 192.168.122.255 scope global eth0
if2:
    inet 192.168.122.133/24 scope global secondary eth0
if1:
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
if2:
    inet6 fe80::5054:ff:fe30:28ac/64 scope link 
       valid_lft forever preferred_lft forever
% ip route showdump < iproute.conf.bin 
default via 192.168.122.1 dev if2  proto static 
192.168.122.0/24 dev if2  proto kernel  scope link  src 192.168.122.132

設定の復元

restoreコマンドで復元します。
% ip addr restore < ipaddr.conf.bin
% ip route restore < iproute.conf.bin

余談

iproute2 はドキュメント整備が不足していると述べましたが、これはかなりのつらみを感じます。今回もコマンドの使い方を学ぶためにソースコードとコミットログを参照しました。
コミットログによると、今回紹介した ip addr save/restore や ip route save/restore はcheckpoint-restartでも用いられているようです。
Add ip route save/restore
iproute: Add ability to save, restore and show the interfaces' addresses (resend)

参考文献

iproute2 - official
kernel/git/shemminger/iproute2.git
git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git

2013/06/23

OSSの開発活動を可視化するWebサービスをつくりました

Linux Foundation の報告書 "Linux Kernel Development: How Fast it is Going, Who is Doing It, What They are Doing, and Who is Sponsoring It" には Linux Kernle 開発の統計情報が数多く含まれており、開発の規模やスピード感が読み取れます。こういった統計情報は Linux Kernel のみならず、他の多くの OSS 開発プロジェクトにおいても有用です。今回作成した Web サービス "OSS Development Statistics" は git レポジトリのログから、月ごとのコミット数やディベロッパ数、コミット数ランキング、ホットな開発キーワードを表示します。

現在登録してあるプロジェクトは、自分が独断で選んだ以下のものです。要望があれば他にも追加いたします。@Etsukataまでお気軽にどうぞ。git レポジトリであれば簡単に追加できます。
  • libvirt 
  • linux 
  • mongo 
  • node 
  • openstack_nova
  • openstack_quantum 
  • openstack_swift 
  • qemu 
  • riak
現在、一日に一回 git pull してデータベースを更新しています。

サービスの作成にあたっては、MEAN Stack を使用しました。
MEAN Stack とは M : Mongodb, E : Express, A : Angular JS, N : Node.js を指し示します。クライアントサイド、サーバサイドともにJavaScriptで記述できる生産性の高いフレームワーク群です。MEAN Stackについては、mongodb blog に寄稿されている記事: The MEAN Stack: MongoDB, ExpressJS, AngularJS and Node.js が参考になります。

類似のことをするソフトウェアとしては、LWN.net の Jonathan Corbet 氏が主に開発している git-dm があります。Corbet 氏は 新しい Linux Kernel がリリースされる度に開発の統計情報をまとめた記事を投稿しますが、git-dmはその記事を書くために用いられているようです。
Linux Kernel 3.9 リリース時の記事:

今回本サービスを使ってみて、OpenStack-Nova の開発規模が QEMU を上回っていることに驚きました。OpenStack 全体では Linux Kernel の開発規模に迫りつつあるかもしれません。
今後は OSS プロジェクト相互の開発者の乗り入れ状況や、プロジェクト同士の開発規模、開発加速度の比較をしたいと考えています。

参考情報