2013/02/27

qemu VNC on WebSocket

はじめに

qemu 1.4 では Websocket プロトコル上で VNC を実現する機能が実装されました。
Qemu/Changelog/1.4
従来、ブラウザ(Websocket)でVNCを利用する場合、Websockify などを用いて、Websocket 通信を通常のソケット通信に変換しなければなりませんでした。今回 qemu VNC がWebsocket 対応したことにより、従来のような面倒な変換なしにブラウザ経由で VNC を利用できます。HTML5対応のブラウザであれば、Java プラグインなど無しに気軽に VNC を利用できるようになるのでとても便利です。

関連コミット:
http://git.qemu.org/?p=qemu.git;a=commit;h=7536ee4bc3da7e9b7fdadba5ba6ade63eaace430

使い方

qemu の configure 段階で vnc-ws を有効にします。
$ ./configure --enable-vnc-ws
以下のようなエラーがでるかもしれません。
ERROR
ERROR: User requested feature vnc-ws
ERROR: configure was not able to find it
ERROR
その時は、gnutls-devel パッケージをインストールします。
# yum install gnutls-devel
無事にインストールが済んだら、libvirt XML ファイルを編集しましょう。
コンパイルした qemu を使用するため、emulator タグで qemu の path を変更します。
また、qemu に "-vnc :1,websocket" オプションを渡すためのタグを追加します。ここで、"1:" はディスプレイポート番号です。"0:" だと既存のディスプレイと重なるかもしれないので、ずらしてあります。
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
...
  <devices>
    <emulator>/usr/local/bin/qemu-system-x86_64</emulator>
    ...
  </devices>
  <qemu:commandline>
    <qemu:arg value='-vnc'/>
    <qemu:arg value=':1,websocket'/>
  </qemu:commandline>
</domain>

仮想マシンを起動し、novnc.com にアクセスします。
noVNC.com は HTML5(Canvasなど) と Websocket で実装された、Web ベースの VNC クライアントです。 ブラウザは必ず HTML5 対応の新し目のブラウザを使いましょう。
右上の "Connect" ボタンをクリックし、
Host : 127.0.0.1
Port : 5701
を入力します。ポート番号は、上記で設定したディスプレイ番号に 5700 を足したものになります。今の場合だと、ディスプレイ番号 "1" + 5700 = 5701 です。
接続できると、こんな感じで、画面が表示されます。Chrome の中で Firefox を動かして見ました。

せっかくなので、iphone と Nexus 7 でも試して見ました。
Nexus 7


iphone



自宅の WiMAX環境では、iphone は重くて使い物になりませんでした。Nexus 7 もキーボードがいまいち使いにくかったです。

ともあれ、VNC on Websocket ではブラウザさえあれば、どこからでも VNC で画面転送できるのは便利です。家の仮想マシンを VPN 接続で iphone から操作する、なんてもこともできます。

余談

SPICE の HTML5 クライアントはないのかな?と思ったらありました。
HTML5 - SPICE
qemu が Spice on Websocket に対応していないので、 Websockify が必要です。

2013/02/25

qemu cache mode まとめ

はじめに

qemu の cache mode には "none", "direcsync", "writeback", "writethrough", "unsafe" の5つがあります。名前からでは、cache mode のそれぞれの仕組みがよくわかりません。そのために、どのようなケースでどの cache mode を使えばいいのか、判断に困ってしまいます。
ここでは、raw ディスクイメージについて、それぞれの cache mode の違いをまとめます。(qcow2 など他のディスクイメージフォーマットでは仕組みが若干ことなります)

Cache Mode 性質比較
それぞれの cache mode ではHost page cache や仮想的な Disk write cache を用いるかどうかで性質が変わってきます。表でまとめると以下のようになります。


cache mode Host page cache Disk Write cache NO Flush
directsync



writethrough


none


writeback

unsafe
✓ : on

Host page cache とは、qemu が 仮想マシンイメージファイルを open するときに O_DIRECT フラグをつけるか否かです。"none" と"directsync" では O_DIRECT フラグを"つける" ので、Host page cache を使いません。

Disk Write Cache とは、virtio-blk デバイスの持つ揮発性 cache です。現在では通常のHDDなどは32MB や 64MB のcache を持っていますが、Disk write cache は virtio-blk が持つ仮想的な cache です。qemu の中では Disk Write Cache を使うか否かで flush(fdatasync) のタイミングが異なります。使う場合はOSから flush 要求があった時に flush(fdatasync) しますが、使わない場合は disk write の度に flush(fdatasync)します。qemu が fdatasync を発行することで、Host の Disk にデータが書きだされますが、頻繁に fdatasync すると性能が劣化してしまします。

NO Flush とはqemuが保持している cache を disk へ flush するのを無効化します。無効化することで性能は上がりますが、Host クラッシュ時にゲストでファイル不整合が起こる可能性が増します。

以上を踏まえると、cache mode は以下のように分類できます。
こちらのほうがわかりやすいと思います。

Host page cache あり
Host page cache なし
Write 毎 にFlush
writethrough
directsync
Flush 要求時に Flush
writeback
none
Flush 無効
unsafe


性能比較
それぞれのcache mode のライト性能を、dd で測定しました。
環境は以下となっています。

  • HostOS: Fedora 18 (3.7.9-201.fc18.x86_64)
  • GuestOS : 同上
  • Host FS : ext4 (ordered)
  • Guest FS : 同上
  • Host memory : 4GB
  • Guest memory : 1GB
  • qemu : 1.4
  • qemu aio : threads
  • qemu disk type : raw

測定コマンドは
$ dd if=/dev/zero of=./zero bs=64K count=8K 
もしくは
$ dd if=/dev/zero of=./zero bs=64K count=8K oflag=direct
です。
Cache Mode の性質を比較するために、direct flag をつけた場合でも測定しています。

比較表

通常 write(MB/s)
oflag=direct(MB/s)
HOST
96.5
82.3
directsync
65.8
4.8
writethrough
62.8
4.6
none
88.3
54.2
writeback
80 ~ 400
80 ~ 400
unsafe
80 ~ 400
80 ~ 400

"writeback" と "unsafe" ではブレが非常に大きくなっています。これは Host の Page cache の影響です。 "directsync" と "writethrough" では、oflag=direct 時に著しく性能が落ちています。これは qemu が write 毎に fdatasync を発行するためです。

Cache mode の選定
さて、cache mode には色々あり、性質も大きく異なりますが、結局どの mode を選べば良いのでしょうか。指標としては、"メモリ消費量"、"性能"、"安全性" が挙げられます。

"メモリ消費量" で mode に大小をつけると以下のようになります。
directsync = none < writethrough = write back = unsafe
"directsync" と "none" では Host page cache を使用しないため少なくなります。他の mode では Host page cache が効くため、Guest と Host で二重にキャッシュが効いていることになります。つまり、同じデータがメモリ上に重複して存在してしまします。KSM(Kernel Samepage Merging)の仕組みがあるとは言え、キャッシュ重複はメモリを消費するため、好ましいものではありません。

"性能" での優劣は以下となります。
directsync < writethrough < none < writeback < unsafe
"writeback" と "unsafe" は広大な Host page cache を効かせる分、性能は良くなります。write 毎に fdatasync を発行する "directsync" や "writethrough" は性能は劣ります。

"安全性" での優劣は以下となります。ここで、安全性とは、ファイル整合性がとれるまでの時間が短いものを優とします。
unsafe < writeback < none < writethrough = directsync
fdatasync を頻繁に発行する "writethrough" と "directsync" はファイル整合性を取りやすいです。cache を効かせ、Flush 頻度が少ないものほど、安全性は劣ります。

以上を踏まえて、cache mode を選定します。
要件により、選定する cache mode は異なります。

1. とにかく安全第一。物理環境と同程度の安全性が欲しい。
⇛ directsync
cache mode で一番安全なのは O_DIRECT で fdatasync を頻発する directsync です。そのかわり、性能はいまいちです。

2. メモリ消費量は程程に、性能もある程度欲しい。
⇛ none
"none" は Host page cache を使わないため、メモリ消費もそれほど大きくありません。性能も Host には劣りますが、まずまずです。

3. メモリをどれだけ消費しようが、性能が欲しい。
⇛ writeback
"writeback" は広大な Host page cache を使うため、性能が出ます。反面、メモリ消費量は大きいです。

普通に VM をいじる分には "writeback" で。メモリ が多くない環境では "none" にするのがいいと思います。
ちなみに、自分はいつも "none" にしています。

参考文献
An Updated Overview of the QEMU Storage Stack
QEMU Emulator User Documentation
[Qemu-devel] Is cache=writeback safe yet

2013/02/18

qemu virtio-blk data plane の使い方

はじめに
qemu virtio-blk data planeとは、virtio-blk デバイスごとに、専用のIO threadを作り、QBL(qemu big lock)の外部で動作させることでlock contentionによる性能劣化を抑える試みです。専用のthreadを作り性能を向上させる試みは、ネットワークではすでにvhost-netを用いて行われていました。

関連コミット:
http://git.qemu.org/?p=qemu.git;a=commit;h=392808b49b6aee066d0c1d200e72fc3dc11c9d0f

簡単に仕組みを説明すると、virtio-blk デバイスごとに作られたIO threadはLinux Native AIOシステムコールを使い、非同期にIOを発行します。ゲストからの通知(ioeventfd)、およびゲストへの割り込み通知(irqfd)もこのIO threadが担当します。従来はqemuが行なっていた作業をglobal mutex外へと分離し、専用のthreadに任せることでScalabilityと性能を向上させることができるわけです。

virtio-blk data planeを用いることで、IOPSが14万から60万に上がったという報告もあります。
http://comments.gmane.org/gmane.comp.emulators.qemu/184821

しかし、現状ではvirtio-blk data plane には以下のような制限があります。
  • raw format のみのサポート
  • live migration 非サポート
  • hot unplug 非サポート
  • qemu での IO throttling が効かない
  • Host は linux のみサポート(Linux Native AIO syscallを使うため)

使い方
virtio-blk x-data-plane を使うには、まず条件として以下が必要です。

  • raw image formatでなければならない
  • wce(write cache enable: Guestのwrite cache設定を起動中に on/off 変更する機能) off
  • scsi off

「raw image なんてないよ、qcow2(or 3) しかないよ!」という方は以下のコマンドでraw image を作ってVMを新規作成しまししょう。
# qemu-img convert -O raw qcow.img raw.img
qemu を直接起動しても良いのですが、ここでは利便性のためにlibvirtを経由してqemuを扱うことにします。
上記の条件に合うように、libvirt XML file を作りましょう。
例えば、以下のようにします。

<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
...
  <devices>
    <emulator>/usr/local/bin/qemu-system-x86_64</emulator>
    <disk type='file' device='disk'>
      <driver name='qemu' type='raw' cache='none' io='native'/>
      <source file='/home/eiichi/vmimg/fraw.img'/>
      <target dev='vda' bus='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
    </disk>
    ...
  </devices>
  <qemu:commandline>
    <qemu:arg value='-set'/>
    <qemu:arg value='device.virtio-disk0.config-wce=off'/>
    <qemu:arg value='-set'/>
    <qemu:arg value='device.virtio-disk0.scsi=off'/>
    <qemu:arg value='-set'/>
    <qemu:arg value='device.virtio-disk0.x-data-plane=on'/>
  </qemu:commandline>
</domain>

diskタイプをraw、AIOはLinux Nativeを指定します。
また、qemuの-setオプションを使うことで、x-data-plane=on オプションを有効にします。

評価
以下の環境で、bonnie++による書き込み性能評価を行いました。
  • Host OS: Fedora 18
  • Host Memory : 4G
  • Host FileSystem : btrfs
  • Guest OS: Fedora 18
  • Gues VCPU: 2
  • Guest Memory : 1G
  • Guest FileSystem : btrfs
  • Qemu : qemu v1.3.0-rc2
  • 測定コマンド : bonnie++
Guest data plane off:
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
fraw             2G   875  84 74157  13 21666   6  3128  78 50335   6 263.5  15
Latency             31893us     632ms    6507ms   83291us     299ms     728ms
Version  1.96       ------Sequential Create------ --------Random Create--------
fraw                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16 21634  57 +++++ +++ +++++ +++ 22124  60 +++++ +++ +++++ +++
Latency               241us     593us     443us     108us      30us     138us

Guest data plane on:
Version  1.96       ------Sequential Output------ --Sequential Input- --Random-
Concurrency   1     -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
fraw             2G   900  87 61252  12 24206   7  3296  82 91454  12 242.6  15
Latency             54769us    1189ms    5273ms   36272us     275ms     546ms
Version  1.96       ------Sequential Create------ --------Random Create--------
fraw                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16  6949  18 +++++ +++ +++++ +++ 19625  52 +++++ +++ 32179  21
Latency              1069us    1450us     448us     112us     449us     173us

残念ながら、自分の環境だと性能に大きな違いは見受けられないようです。。
高性能なストレージを使ったり、qemu_global_mutex contention が大量に発生する環境で測定すると、また違った結果がでてくると思われます。

参考文献

An Updated Overview of the QEMU Storage Stack
Optimizing the QEMU Storage Stack
[Qemu-devel] [PATCH v2 0/8] virtio: virtio-blk data plane
Features/WriteCacheEnable


2013/02/02

手軽にqemuのトレースを採る

qemu内部のロジックを追ったり、性能解析をするにはトレース採取が有効です。qemuのトレースを手軽に採る方法についての投稿です。

qemuのトレースを採る方法については、いくつかありますが、標準エラー出力にトレースを書き出す "stderr" trace-backend がもっとも簡単な方法です。
"stderr" backend の仕組みは qemu 内部のトレースイベントを標準エラー出力にデバッグprintfをするという非常に単純なものです。
qemu-kvm 0.15よりサポートされています。

関連コミット:
http://git.qemu.org/?p=qemu.git;a=commit;h=320fba2a1f384e17db150d74540a2cf005eb47b5


使い方
qemuのトレースは採取しない設定でもオーバヘッドがあるため、デフォルトではオフになっています。トレースを有効にするには、コンパイル時にトレースを有効にする必要があります。
コンパイルオプションは以下のようになります。
./configure --prefix=/usr/local/ --enable-trace-backend=stderr

トレースイベント一覧は以下のファイルにかかれています。
qemu/trace-events

イベント一覧から、取得したいイベントを選びファイルに書き出します。 例えば、以下のような内容のファイルを作ります。ワイルドカード指定が可能です。
g_malloc
g_free
qemu*
virtio*

qemu を起動する際に、このイベントファイルを-traceオプションに指定します。
sudo ./qemu-system-x86_64 -enable-kvm -hda ~/vmimg/f1.img -m 512 -trace events=/path/to/events-file

得られる出力は以下のようになります。
g_malloc size 16 ptr 0x7f3f40004270
qemu_co_mutex_lock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_lock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_coroutine_yield from 0x7f3f54a7faa0 to 0x7f3f400009d8
qemu_coroutine_enter from 0x7f3f54a7fbb8 to 0x7f3f54a7faa0 opaque (nil)
qemu_co_mutex_lock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_lock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_entry mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
qemu_co_mutex_unlock_return mutex 0x7f3f54a80898 self 0x7f3f54a7faa0
g_free ptr 0x7f3f40004270


libvirtとの連携
qemuを利用する際は、通常libvirt経由で利用することが多いと思います。ここではlibvirtから"stderr" trace-backend を利用する際のtipsを記載します。

・libvirtのログに出力
virsh start domain コマンドでドメインを起動した場合、stderrの出力先は、以下になります。
/var/log/libvirt/qemu/domain.log

・動的なイベントのオン・オフ
qemu monitor command を利用することで、稼働中のドメインで採取するイベントを動的にon/offできます。

利用可能なトレースイベント一覧の取得
# virsh qemu-monitor-command --hmp domain info trace-events

イベントのon・off
# virsh qemu-monitor-command --hmp domain trace-event qemu_vmalloc on
# virsh qemu-monitor-command --hmp domain trace-event qemu_vmalloc off
参考情報

Observability using QEMU tracing
qemuのtrace機能
docs/tracing