tag:blogger.com,1999:blog-90259851087525106612024-09-06T08:08:19.627+09:00Etsukata blogEtsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]Blogger48125tag:blogger.com,1999:blog-9025985108752510661.post-82124628497098844952016-06-28T23:35:00.000+09:002016-08-30T16:47:38.157+09:00Memcached 1.4.19 to 1.4.28: Eviction 不具合による Slab OOM<h4 style="height: 0px;"> はじめに</h4> <div> <br /> <br /></div> <div> Memcached を運用中に、Request の傾向は変わっていないにもかかわらず、徐々に Item 数が増加し始め、 ある時を境に<b>&nbsp;Item が 一切 Eviction/Expire されなくなり、Memory が枯渇し Slab OOM Error</b> が起こる、という不具合に遭遇しました。不具合の原因については特定し、1.4.29 で修正がマージされました (Pull-Request:<a href="https://github.com/memcached/memcached/pull/152"> fix zero hash items eviction</a>&nbsp;, <a href="https://github.com/memcached/memcached/wiki/ReleaseNotes1429">ReleaseNote1.4.29</a>) 。不具合が発生する条件、原因、回避策を簡単にまとめておきます(Pull-Request にはより詳しく書いてあります)。</div> <div> <br /></div> <h4> 不具合が発生する条件</h4> <div> <br /></div> <div> <ul> <li>Memcached Version : 1.4.19 から 1.4.28</li> <li>SET した Key を GET しない場合がある &nbsp;</li> <li>Item を入れ替える Command (APPEND, PREPEND, INCR, DECR) を使用していない</li> </ul> <div> <br /></div> </div> <h4> 不具合の原因</h4> <div> <br /></div> <div> Memcached は Item を双方向リストで管理しています。GET, SET などの Command を処理するタイミングで各 Slab Class の双方向リストの末尾から 5 つの Item を捜査し、 Eviction/Expire させます(1.4.26 時点&nbsp;<a href="https://github.com/memcached/memcached/blob/master/items.c">[email protected]</a>)。この Item Eviction/Expire 処理に不具合があり、 <b>Item の Key の hash 値が偶然に 0 の時</b>に、Item が Eviction/Expire されず、双方向リストの末尾に滞留し続けます。&nbsp;</div> <div> <br /></div> <div> 再現スクリプト<br /> <br /></div> <div> <pre class="prettyprint"># set the items whose jenkins hash(ENDIAN_LITTLE) values are zero with exptime 1 sec printf "set 0wYuLiaUdfgTZCUsz8mRR1WJVk 0 1 4\r\ndata\r\n" | nc localhost 11211 printf "set 9NxjWkBnSfD0LShqUBZAqv3jKI 0 1 4\r\ndata\r\n" | nc localhost 11211 printf "set wRL2lvUMyPnbiImZdi9CTXbsJ6 0 1 4\r\ndata\r\n" | nc localhost 11211 printf "set vOgpzFNo2pGpYLKxUWXfI6PDXq 0 1 4\r\ndata\r\n" | nc localhost 11211 printf "set aTogASrWNRQSA1uh2ySgv6HwaU 0 1 4\r\ndata\r\n" | nc localhost 11211 # wait for exptime sleep 2 # another innocent key set # normally, the five items expire here printf "set innocentkey 0 1 4\r\ndata\r\n" | nc localhost 11211 echo "stats items" | nc localhost 11211 </pre> <br /></div> <div> スクリプト実行結果 <br /> <br /></div> <pre class="prettyprint">STORED STORED STORED STORED STORED STORED STAT items:1:number 6 STAT items:1:age 2 STAT items:1:evicted 0 STAT items:1:evicted_nonzero 0 STAT items:1:evicted_time 0 STAT items:1:outofmemory 0 STAT items:1:tailrepairs 0 STAT items:1:reclaimed 0 STAT items:1:expired_unfetched 0 STAT items:1:evicted_unfetched 0 STAT items:1:crawler_reclaimed 0 STAT items:1:crawler_items_checked 0 STAT items:1:lrutail_reflocked 0 END </pre> <div> <br /></div> <div> 本来であれば、6度目の SET が完了した時点で、 Expire すべき Item は全て回収されるはずですが、滞留してしまっています。<br /> さらに後続の SET が続くと、 Item が一切回収されないまま Memory を消費し続け、-M option で指定した上限にあたるところで OOM Error(SET 不能) となります。<br /> <br /></div> <h4> 不具合特定の方法</h4> <div> <br /></div> <div> Coredump を取得し、コードを読んで可能性を絞りつつ gdb で追いました。<br /> <br /></div> <h4> 現状の回避策</h4> <div> <ul> <li>Memcached 1.4.19 - 1.4.28 の使用を避ける</li> <li>[再起動不要, コード理解している人向け] Item 滞留を起こしうる全ての Slab Class を対象に、時折 Item を入れ替える Command (APPEND, PREPEND, INCR, DECR) を発行する</li> <ul> <li>lru_pull_tail の引数 cur_hv が 0 では無くなるため</li> </ul> </ul> <div> <br /></div> </div> <div> <h4> まとめ</h4> <br /> <ul> <li>Memcached 1.4.19 以降には突然 Slab OOM を引き起こす不具合が存在します</li> <ul> <li><strike>最新の upstream でも未修正です(<a href="https://github.com/memcached/memcached/pull/152">修正提案中</a>)</strike></li> <li>[8/30 追記] 1.4.29 で修正がマージされました</li> <ul> <li><a href="https://github.com/memcached/memcached/wiki/ReleaseNotes1429">Release Note 1.4.29</a></li> </ul> </ul> </ul> <br /></div> Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-1818322536887966112015-09-27T18:28:00.001+09:002015-09-27T18:29:23.674+09:00Hadoop : CPU system 使用率高騰 "zone_reclaim_mode = 1" 編<h4> はじめに</h4> <div> <br /></div> <div>  会社で PB 級の Hadoop クラスタを運用していますが、ある日から Datanode の CPU system (Kernel 内での CPU 使用率) が高騰し、Job が遅延するという症状が発現しました。Hadoop で CPU system 高騰というと、 Transparent HugePage 設定が有名ですが、そちらについては既に特定し、対策済みでした。 THP と Hadoop に関係については下記 Blog が詳しいです。<br /> <a href="http://structureddata.org/2012/06/18/linux-6-transparent-huge-pages-and-hadoop-workloads/">Transparent Huge Pages and Hadoop Workloads</a></div> <div> <br />  今回は THP ではなく、 "zone_reclaim_mode" の設定による性能劣化について、現象から原因特定に至るまでの経緯と、推奨する設定について解説します。<br /> <br /> <h4> 現象</h4> <div> <br /></div> <div>  観測された現象について簡単に箇条書きします。</div> <ol> <li>CPU user が 5% 程度の時でも <b>CPU system が30%</b> を超えるなど、 Kernel 内での CPU 使用率が異常に高かった</li> <li>CPU 使用率高騰により、いくつかの Job 実行時間が、問題発生前と比較して 1.5 倍に増えた</li> <li>一部のマシンで発生し、他のモデルのマシンでは発生しなかった</li> </ol> <br /> <h4> perf による原因調査</h4> <div> <br /></div> Kernel 内での CPU 使用率が高騰した際には perf と呼ばれる Linux Profiling Tool が非常に有用です。特別な準備をする必要なく、簡単に Profiling を取得できます。<br />  今回は Profiling により、 "どの関数で CPU を使用しているのか" 、 "どの処理で問題の関数が呼ばれるのか(Call Graph)" を調査します。<br /> <br /> <u>perf での Profiling 取得</u><br /> <br />  CPU system が高騰したタイミングを見計らい、下記コマンドでプロファイルを取得します。<br /> <pre class="prettyprint">perf record -F 99 -a -g -- sleep 30 </pre> 取得した結果を表示。Call Graph が取得できます。<br /> <pre class="prettyprint">perf report </pre> <br /></div> <div> 結果の一部:</div> <div> <pre class="prettyprint">- 33.01% java [kernel.kallsyms] [k] _spin_lock_irq - _spin_lock_irq - 98.89% shrink_inactive_list shrink_mem_cgroup_zone shrink_zone zone_reclaim get_page_from_freelist - __alloc_pages_nodemask - 89.56% alloc_pages_current - 79.46% __page_cache_alloc - 99.76% grab_cache_page_write_begin ext4_da_write_begin generic_file_buffered_write __generic_file_aio_write generic_file_aio_write ext4_file_write do_sync_write vfs_write sys_write system_call_fastpath + 0x3fda40e6fd + 17.54% tcp_sendmsg + 1.75% __get_free_pages + 1.25% pte_alloc_one + 9.29% alloc_pages_vma + 1.15% kmem_getpages + 14.24% java [kernel.kallsyms] [k] _spin_lock + 4.75% java libjvm.so [.] SpinPause + 4.03% java perf-1947.map [.] 0x00007fd9550209cd + 2.64% java libsnappy.so.1.1.3 [.] snappy::internal::CompressFragment(char const*, unsigned long, char*, + 2.01% java libjvm.so [.] ParallelTaskTerminator::offer_termination(TerminatorTerminator*) + 1.84% java [kernel.kallsyms] [k] __isolate_lru_page + 1.58% init [kernel.kallsyms] [k] intel_idle ... </pre> <br /></div> <div>  ちなみに、上記の Call Graph を可視化した <a href="https://github.com/brendangregg/FlameGraph">FlameGraph</a> の画像は以下です。<br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1rjrPkKWXKg2GYYNY5S6c0VnrIe3Mwan6lWj3ZLqQeMn4MAgm72fPShJaS0vlVR1YsA9xmalDrPQR2SJaWGNkqjjvKKmHEhDESrA3Hbxb2b93PWRhRKv9GGZbAiPJPQ0B8_54XoJxhG33/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2015-09-27+17.10.41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="146" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1rjrPkKWXKg2GYYNY5S6c0VnrIe3Mwan6lWj3ZLqQeMn4MAgm72fPShJaS0vlVR1YsA9xmalDrPQR2SJaWGNkqjjvKKmHEhDESrA3Hbxb2b93PWRhRKv9GGZbAiPJPQ0B8_54XoJxhG33/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2015-09-27+17.10.41.png" width="320" /></a></div> <br /></div> <u>perf 結果からわかること</u><br /> <br /> <ol> <li>CPU system を高騰させているのは "spin_lock*" 関数であること</li> <li>"spin_lock*" 関数は "メモリ回収処理" の延長で呼ばれていること</li> </ol> <br />  つまり、メモリが足りなくなったために、メモリ回収処理があまりに頻繁に呼ばれ、spin_lock のオーバヘッドが高騰したことが予想されます。さて、<b>メモリ使用量について改めて観測すると、半分程度</b>しか使用していません。にもかかわらずメモリ回収処理が頻繁に呼ばれるのは何故でしょうか...<br /> <br />  Linux のメモリ解放に関連するパラメータを洗い出してみると、一つの気になるパラメータがありました。<b>"zone_reclaim_mode"</b> です。有効になっている場合、NUMA 環境で zone 毎のメモリ回収が積極的に行われるようになります。デフォルトでは無効のはずですが、今回の該当マシン(CentOS 6系)で調べてみると、なんと有効になっていました。<br /> zone_reclaim_mode について詳細:&nbsp;<a href="https://www.kernel.org/doc/Documentation/sysctl/vm.txt">https://www.kernel.org/doc/Documentation/sysctl/vm.txt</a><br /> <br /> <h4> zone_reclaim_mode 無効設定の結果</h4> <div> <br /></div> /proc/sys/vm/zone_reclaim_mode に 0 を設定したところ、問題の CPU system 高騰は収まりました。Job の実行時間も元の水準に戻りました。<br /> <br /> <h4> なぜ zone_reclaim_mode が有効になっていたか</h4> <div> <br /></div> zone_reclaim_mode は一部のマシンで有効になっており、 CPU system が高騰していない別のマシンでは無効になっていました。同じ OS を使っていたのに、なぜ設定に違いが出たのでしょうか。Kernel のソースコードを読んで調べてみましょう。Kernel の Version は CentOS6(2.6.32-431.11.2.el6) とします。<br /> <br />  zone_reclaim_mode は default = 0 ですが、NUMA 環境における Node 間の距離(RECLAIM_DISTANCE) の値によっては、Kernel 起動時に 1 に修正されてしまうようです。<br /> &nbsp; mm/pagealloc.c<br /> <pre class="prettyprint"><pre>3096 /* 3097 * If another node is sufficiently far away then it is better 3098 * to reclaim pages in a zone before going off node. 3099 */ 3100 if (distance &gt; RECLAIM_DISTANCE) 3101 zone_reclaim_mode = 1; </pre> </pre> &nbsp;その閾値は "20" とあります。<br /> <pre class="prettyprint"> 57 /* 58 * If the distance between nodes in a system is larger than RECLAIM_DISTANCE 59 * (in whatever arch specific measurement units returned by node_distance()) 60 * then switch on zone reclaim on boot. 61 */ 62 #define RECLAIM_DISTANCE 20 </pre> <br />  有効になっていたマシンの NUMA Node 距離を調べると... 21<br /> <pre class="prettyprint">$ numactl --hardware ... node distances: node 0 1 0: 10 21 1: 21 10 </pre> <br />  無効になっていたマシンでは 20 でした。<br />  要するに、ハードウェア構成によっては zone_reclaim_mode が自動的に有効になってしまう様です。<br /> <br /> <h4> 最新の Kernel ではどうなっているか</h4> <div> <br /></div> ちなみに最新の Kernel では 下記 Commit により、 "ハードウェア構成によっては zone_reclaim_mode を自動で有効とする" 挙動が無効に変更されています。<br /> <a href="http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4f9b16a64753d0bb607454347036dc997fd03b82">http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=4f9b16a64753d0bb607454347036dc997fd03b82</a><br /> <br /> <h4> 他のソフトウェアでの推奨値</h4> <div> <br /></div> メモリを大量に使う DB 等のソフトウェアでの推奨値はやはり "無効" のようです。<br /> <br /> <ul> <li>PostgreSQL:&nbsp;<a href="http://frosty-postgres.blogspot.jp/2012/08/postgresql-numa-and-zone-reclaim-mode.html">PostgreSQL, NUMA and zone reclaim mode on linux</a></li> <li>MongoDB: <a href="http://docs.mongodb.org/manual/administration/production-notes/#configuring-numa-on-linux">Configuring NUMA on Linux</a></li> </ul> <h4> </h4> <h4> まとめ</h4> <div> <ul> <li>Hadoop Datanode での CPU system 高騰原因を perf を使って調査した</li> <li>原因: zone_reclaim_mode = 1 によるメモリ回収処理多発</li> <li>ハードウェア構成によってい zone_reclaim_mode のデフォルト設定は変わる</li> <li>Hadoop 含め、NUMA 環境でメモリを大量に使うソフトウェアで CPU system が高騰していた場合、 zone_reclaim_mode 設定を確認する</li> </ul> </div> <div> <br /></div> Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-32582751682207457532015-07-16T01:49:00.000+09:002015-07-16T01:49:38.783+09:00FreakOut DSP 入札サーバの CPU 使用率を 30% 削減する Performance Tuning<h3> はじめに</h3> <div> <br /></div> <div>  勤務先の FreakOut 社では RTB で広告枠を買い付ける DSP の開発・運用を行っています。RTB とは、インターネット広告のインプレッションが生じる毎に、広告枠の競争入札を行う仕組みです。 DSP とは、 RTB において、競争入札をする側のシステムになります。広告枠/広告を見ている人 に対し、最適な広告を、最適なタイミングで届ける機能を広告主に提供する仕組みです。</div> <div>  FreakOut DSP は最適な広告探索・入札価格調整のため、非常に多くのデータを参照し、沢山の演算処理を行います。広告を見ている人が過去にアクセスした Web ページの情報や検索ワード、さらに 広告がクリックされる予測確率(過去のログから機械学習で算出) などを参照し、入札価格を決定するのです。そのため、DSP で入札を担当するサーバは CPU がボトルネックになっており、台数も数百台に嵩んでいます。<b>インフラコストの大部分を占めるのが入札サーバ</b>なのです。</div> <div> <br /></div> <div>  今回の記事では、入札サーバの CPU の使用率を 30% 程度削減した Performance Tuning 手法についてお伝えします。</div> <div> <br /></div> <div> <br /></div> <h3> 入札サーバ実装概要</h3> <div> <br /></div> <div>  ユーザに広告を素早く届けるため、入札サーバではレスポンスタイム <b>50ms</b> 程度が求められます(<b>50ms or die</b>)。高速に動作する必要があるシステムですが、FreakOut DSP は Perl で実装されています。リクエストは Nginx + Starlet で処理されます。詳しくは下記のリンク先、 <a href="https://twitter.com/myfinder">@myfinder</a> さんの資料をご参照ください。3年ほど前の資料ですが、大きくは変化していません。</div> <div> <br /></div> <div> <a href="http://www.slideshare.net/myfinder/50msperl">平均レスポンスタイム50msをPerlで捌く中規模サービスの実装/運用</a></div> <br />  Starlet は Prefork 型の Web App サーバであり、多数の worker process がリクエストを順次捌く構造を持っています。<br /> <br /> <h3> Tuning の要点</h3> <div> <br /></div> htop、mpstat -P ALL 等のコマンドで Starlet の worker process の挙動を観察すると、すべてのコアを公平に使えていないことが分かりました。また worker process が CPU コア間を移動する(migration) 頻度も多いのです。これらは Linux Kernel の Process Scheduler の仕様によるもので、多くのユースケースでは特にチューニングせずとも良好な動作をするのですが、 入札サーバのように、Prefork 型でかつ CPU バウンドなワークロードでは、チューニングを施す方が性能が改善するケースがあります。<br />  CPU コアを公平に使えず、コア間の負荷に偏りがあると、負荷の高いコアではプロセスの Context Switch が頻繁に発生してしまいます。<b>Context Swtich が頻発すると、 CPU Cache を有効に使えない</b>ため、オーバヘッドが嵩みます。<br />  また、<b>プロセスが CPU コア間を移動すると、これまた CPU Cache を有効に使えない</b>ため、オーバヘッドが同上、となってしまうのです。<br /> <br />  上記の観測から、CPU Cache を最大限有効活用する Performance Tuning を下記2点、実施しました。<br /> <br /> <h3> Tuning1. worker process のコア固定化</h3> <div> <br /></div> <div>  worker process が生成された直後、プロセス内で <a href="http://linuxjm.osdn.jp/html/LDP_man-pages/man2/sched_setaffinity.2.html">sched_setaffinity</a> システムコールを発行し、動作する CPU コアが一様に分かれるよう CPU コアを固定化しました。これにより、worker process が CPU コア間を移動することは無くなります。</div> <div> <br /></div> <h3> Tuning2. Linux Kernel Scheduler Parameter 調整</h3> <div> <br /></div> <div>  Linux Kernel にはプロセス Scheduler の挙動を調整する Parameter があります。</div> <div>  代表的なものに&nbsp;<span style="white-space: pre-wrap;"><b>sched_min_granularity_ns</b> があります。これは、ざっくり言ってしまうと Process Scheduler が1つのプロセスを動作させ、次のプロセスに切り替えるまでの時間の最小単位です。つまり、sched_min_granularity_ns が小さいと、頻繁に Context Switch が起こり、逆に大きいと、Context Switch の頻度が少なくなります。</span></div> <div>  類似の Parameter で重要なものに&nbsp;<b>sched_wakeup_granularity_ns</b> があります。プロセスが頻繁に wakeup と sleep を繰り替えすケースで Context Switch の頻度を調整する Parameter で、小さくすると Context Switch が頻繁になり、逆に大きくすると、Context Switch の頻度が少なくなるのは sched_min_granularity_ns と同様です。入札サーバは CPU バウンドとはいえ、memcached へのアクセスもある程度生じるため、wakeup/sleep による Context Switch も考慮する必要があります。</div> <div> <br /></div> <div> <span style="white-space: pre-wrap;"> 一般にリアルタイム性の求められるシステムでは、プロセスの応答時間を短くするため、 これらの値を小さくします。</span></div> <div> <span style="white-space: pre-wrap;"><br /></span></div> <div> <span style="white-space: pre-wrap;"> <b>今回実施した Tuning では これらの値を大きく</b>しています。</span></div> <div> <span style="white-space: pre-wrap;">「"50ms or die" なんだったらリアルタイム性が要求されるのでは?」 と思う方もいらっしゃるかもしれませんが、逆です。"50ms" は OS Scheduler から見れば非常長い時間であり、Context Switch が頻発すればそのオーバヘッドがバカにならないのです。オーバヘッドは切り替えのみにかかる時間だけではなく、一度 Context Switch が発生すると、CPU Cache Hit Rate が落ち、多くの CPU 時間を無為に使ってしまうのです。 </span></div> <div> <br /></div> <div>  Tuning の実施にあたっては<a href="https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Power_Management_Guide/Tuned.html"> tuned</a> を活用しました。<b>tuned-adm の Profile: throughput-performance</b> で設定しました。tuned-adm は 上記の Scheduler Parameter を含め、諸々良しなに設定してくれます。上記の Parameter は以下です。</div> <div> <br /></div> <div> &nbsp; sched_min_granurarity_ns : 10000</div> <div> &nbsp; sched_wakeup_granurarity_ns : 15000</div> <div> <br /></div> <div> Scheduler Parameter について詳しくは <a href="https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">doc/Documentation/scheduler/sched-design-CFS.txt</a>&nbsp;、<a href="http://www.atmarkit.co.jp/flinux/rensai/watch2009/watch09c.html">Linux Kernel Watch の記事</a>、及び Kernel のソースコードをご参照ください。</div> <div> <br /></div> <div>  他にも細かいところで Tuning している箇所がありますが、今日のところはこのあたりで。</div> <h3> <br /></h3> <h3> Tuning 結果</h3> <div> <br /></div> <div>  Tuning 1. の実施により 20%、Tuning 2. の実施により 10% 、合計 30% 程度の CPU 使用率を削減することができました。</div> <div>  また、入札のレスポンスタイムも、平均 30ms 程度のものを 20ms まで低減することが出来ました。</div> <div> <br /></div> <h3> 余談: Tuning 1. の着想 Erlang VM(BEAM)</h3> <div> <br /></div> <div>  Starlet の worker process コア固定化のチューニングは実は <a href="http://www.erlang.org/doc/man/erl.html#+sbt">Erlang VM(BEAM) のオプションパラメータ</a>に着想を得ています。Erlang VM には Scheduler Thread をコア固定するオプション(+sbt) があり、以前このパラメータの有無で何らかのベンチマークを取ったところ、5% 程度性能が向上した経験が元になっています。(何のベンチマークだったかは忘れました)</div> <div> <br /></div> <h3> 補足</h3> <div> <br /></div> <div>  「この記事で紹介した Tuning を実施するこどで Starlet が動作するサーバの CPU 使用率を 30% 削減できる」ということは保証されません。入札サーバのように、極端に CPU バウンドなサーバに対して効果のある Tuning です。</div> <div> <br /></div> <h3> まとめ</h3> <div> <br /></div> <div> - 入札サーバがインフラコストの大部分を占めていた</div> <div> - CPU Cache を有効活用するため、プロセスの CPU コア間の移動や Context Switch によるオーバヘッドを削減する Tuning を施した</div> <div> - 入札サーバの CPU 使用率を 30% 程度削減出来た</div> <br /> <div> <br /></div> Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-33078472039010540682014-07-24T22:58:00.000+09:002014-07-24T22:58:58.039+09:00Linux システムコールのブロック・ノンブロックまとめ<b>はじめに</b><br /> <br /> Linux にはブロックするシステムコールとノンブロックなシステムコールがあります。さて、システムコールが「ブロックする」とはどういうことでしょうか。よく、ブロックするシステムコールとは「処理が完了するまでプロセスの動作が中断され待たされること」という説明を見ますが、より詳細に、どういう処理の場合に待たされるのか、整理してみましょう。<br /> <br /> <b>「ブロックする」とは</b><br /> <br /> Linux において、システムコールがブロックするとは、「プロセスが、システムコール呼び出しの延長で待状態(TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE) に遷移し、CPU時間を消費せずにあるイベントが完了するのを待つようになる」、ことを指します。ブロックするシステムコールのうち代表的なものと、完了待ち対象イベントをまとめると、以下のようになります。<br /> <br /> <style> /* テーブル全体 */ table.table-01 { width: 100%; border: 1px #888888 solid; border-collapse: collapse; border-spacing: 0; } /* ヘッダー行 */ table.table-01 th { padding: 5px; border: 1px #888888 solid; border-width: 0 1px 1px 1px; /* ヘッダーの背景色 */ background: #bbbbbb; font-weight: bold; line-height: 120%; text-align: center; } /* 通常行 */ table.table-01 td { padding: 5px; border: 1px #888888 solid; border-width: 0 0 1px 1px; background: #ffffff; /* text-align: center; */ } </style> <br /> <table border="1" class="table-01" style="width: 100%px;"> <tbody> <tr><th>システムコール</th><th>待ち対象イベント</th></tr> <tr><td>read, write, fsync</td><td>ディスク I/O</td></tr> <tr><td>read, write, send, recv, accept, connect, select, poll, epoll </td><td>FIFO読み書き可能</td></tr> <tr><td>futex, flock </td><td>ロック取得</td></tr> <tr><td>nanosleep </td><td>タイマ発火</td></tr> </tbody></table> <br /> 待ち対象イベントをそれぞれ解説します。<br /> <br /> <b>ディスクI/O</b><br /> <br /> ディスクをバックエンドとするファイルへの読み書きで、ディスクアクセスが生じる場合にはディスクへのI/O発行が完了するまでプロセスは待ち状態になります。例えば、読み込み対象のファイルがページキャッシュに乗っていない場合や、キャッシュを介さないダイレクトI/Oを発行した場合、fsync(2) でメモリ上の内容をディスクと同期させた場合、です。<br /> なお、細かい話ですが、この場合の待ち状態は TASK_UNINTERRUPTIBLE という、シグナルを受け付けない待ち状態です。ディスクI/O以外の待ち対象イベントでは、TASK_INTERRUPTIBLEで待ちます。<br /> <br /> <b>キュー読み書き可能</b><br /> <br /> パイプやソケットなど、キュー(FIFO)の構造を持つファイルを読み書きしようとした時に、キューが空で読み取れるデータがない場合と、キューが満杯でこれ以上書き込めない場合には、読み書きできる状態になるまでプロセスは待ち状態になります。キューに新しくデータが到着すると、キューが読み込み可能になります。キューに空きが出来ると、キューは書き込み可能状態になります。<br /> read(2)、write(2)、select(2) については上記の説明で良いのですが、accept(2) は少し状況が違うので補足して説明します。<br /> accept(2) はクライアントからの接続要求が キューにない場合にプロセスが待状態に遷移します。複数のプロセスが同じファイルに対し accept(2) を発行している時にクライアントからの接続要求があった場合、待状態にあるプロセスたちのうち、<b>一つのプロセスのみが起床され</b>、accept(2) 処理を実行します。一つのプロセスのみが起床されるのは所謂 <a href="http://www.citi.umich.edu/projects/linux-scalability/reports/accept.html">Thundering Herd 問題</a>への対処です。日本語の解説では、id:naoya さんによる<a href="http://d.hatena.ne.jp/naoya/20070311/1173629378">prefork サーバーと thundering herd 問題</a>&nbsp;が詳しいです。<br /> <br /> <b>ロック取得</b><br /> <br /> futex(2)は指定したアドレスに対応するキューでプロセスを待状態にしたり(FUTEX_WAIT)、指定したアドレスに対して待ち状態にあるプロセスを起床する(FUTEX_WAKE)同期機構です。<br /> 分かりやすくいうと、pthread_mutex_lock(3) などを通してロックを取得しようとしたが、すでに他のプロセスがロックを取得していた場合に、プロセスはFUTEX_WAIT で待ち状態に遷移します。flock(2) も同様です。<br /> <br /> <b>タイマ発火</b><br /> <br /> 所謂タイムアウトです。指定時間が経過するとプロセスが起床します。<br /> <br /> <hr /> <b>「ノンブロック」とは</b><br /> <br /> ブロックと対をなす概念:ノンブロックについても触れておきます。<br /> ノンブロックなシステムコールとは、ブロックしないシステムコールのことです。システムコールをノンブロックにするには、対象とするファイルにfcntl(2) &nbsp;でノンブロッキングフラグ(O_NONBLOCK)を付与します。ノンブロッキングフラグを付与されたファイルに対して、完了待ち対象イベントが「キュー読み書き可能」なシステムコールを発行すると、キューが読み書き可能でない場合、システムコールは即座に失敗し(return -1)、errno に EAGAIN が設定されます。キューが読み書き可能になるのを待ちはしません。<br /> 完了待ち対象イベントが「ディスクI/O」なシステムコールについては、ノンブロックには出来ませんが、その代わり非同期I/Oシステムコール io_submit(2) が用意されています。<br /> <br /> <b>まとめ</b><br /> <br /> システムコールがどのような場合にブロックするのか、完了待ち対象イベント別に分けて説明しました。待ち対象イベントには大きくわけて「ディスクI/O」「キュー読み書き可能」「ロック取得」「タイマ発火」があります。「キュー読み書き可能」なシステムコールについては、ノンブロッキングフラグを付与することで、ノンブロックにできます。<br /> <br /> ブロックするシステムコールは上記ですべてではありません。他にもちょくちょくありますが、細かいので省略します。<br /> <br /> <b>参考文献</b><br /> <br /> 各システムコール Man Page<br /> Linux Kernel 3.15 ソースコードEtsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-32019819376475399602014-05-12T23:40:00.000+09:002014-05-12T23:40:35.409+09:00Docker を支える Linux Kernel の機能 (概要編)<b>はじめに</b><br /> <br /> <a href="https://www.docker.io/">Docker</a> はコンテナ型仮想化技術を使ってOSレベル仮想化を実現するコンテナ管理ソフトウェアです。類似のコンテナ管理ソフトとしては、Docker の他にも libvirt、 lxc-tools などがありますが、 Docker には以下の大きな特徴があります。<br /> <ul> <li><b>Infrastructure as Code の思想に基づき、コンテナをコード(Dockerfile) で管理できる</b></li> <li><a href="https://index.docker.io/">docker index</a>&nbsp; で、コンテナイメージを手軽に取得、共有できる</li> </ul> <div> Docker は上記のような特徴を持つため、アプリケーションのポータビリティを大きく向上させることができると期待されています。</div> <div> 参考:Naoya Ito 氏 "<a href="https://speakerdeck.com/naoya/dockerapurikesiyonfalsepotabiriteiwokao-eru-number-dockerjp">Dockerアプリケーションのポータビリティを考える</a>"</div> <div> <br /></div> <div> <br /></div> <div> 大変便利な Docker ですが、Docker によるコンテナ管理は、実は数多くの Linux Kernel の機能により実現されています。今回は Docker を支える Linux Kernel の機能についてご紹介します。</div> <div> 調査対象の Docker Version: 0.11</div> <div> <br /></div> <div> Docker を支える Linux Kernel の機能を、一枚絵にすると、以下のような図になります。</div> <div> <br /></div> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHcEReFt6e13BdLuY_CBJh1vpUoTYWhCfiVzEt_P623wvIKgH5HPViHoGLuCulSCrbUpBJnV8U6L16A1iOtCUKMD_B87cEY0s_A3qRO3RV1XlQy4UkE5FFVJz3LFqcaaQ8LrJRiKs9VwW-/s1600/Docker+and+Kernel+(1).png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHcEReFt6e13BdLuY_CBJh1vpUoTYWhCfiVzEt_P623wvIKgH5HPViHoGLuCulSCrbUpBJnV8U6L16A1iOtCUKMD_B87cEY0s_A3qRO3RV1XlQy4UkE5FFVJz3LFqcaaQ8LrJRiKs9VwW-/s1600/Docker+and+Kernel+(1).png" height="240" width="320" /></a></div> <div> <br /></div> <div> <br /></div> <div> 各機能は、大きく分けて、<b>Namespaces</b>、<b>Cgroups</b>、<b>Storage</b>、<b>Networking</b>、<b>Security</b> に大別できます。それぞれについて、概要を簡単に説明します。</div> <div> <br /></div> <div> </div> <div> <br /></div> <div> <b>Namespaces</b></div> <div> <br /></div> <div> ユーザプロセスが動作する空間を分離する Namespace はコンテナ型仮想化を実現する上で、核となる機能です。これらの機能の多くはParallels 社の OpenVZ チームを中心として開発されました。Docker が利用する Namespace には PID、MNT、IPC、UTS、NETの 5種類があります。</div> <div> <br /></div> <div> <u>PID Namespace(Kernel 2.6.24)</u></div> <div> <br /></div> <div> PID(Process ID) Namespace は、プロセスが動作する空間を生成・分離します。あらたに作られたプロセス空間からは、親空間で動作するプロセスが見えなくなります。 "見えなくなる" というのは、kill() システムーコール等、PIDを指定するシステムコールで親空間で動作するプロセスと通信できなくなる、ということです。親空間からは、生成した空間で動作するプロセスは見えます。</div> <div> <br /></div> <div> <u>MNT Namespace(Kernel 2.4.19)</u></div> <div> <br /></div> <div> MNT(Mount) Namespace は、プロセスに見えるファイルシステムのマウント空間を分離する機能です。mount コマンドで見えるファイルシステムのマウント情報を分離します。</div> <div> <br /></div> <div> <u>IPC Namespace(Kernel 2.6.30)</u></div> <div> <br /></div> <div> IPC(Inter-Process Communication) Namespace は System V IPC(メッセージ・キュー、 セマフォ、共有メモリ) と Posix メッセージキュー の空間を分離します。IPC 関連の識別子が、他の空間からは見えなくなります。</div> <div> <br /></div> <div> <u>UTS Namespace(Kernel 2.6.19)</u></div> <div> <br /></div> <div> UTS(Unix Time-sharing System) Namespace は、uname() システムコールで取得できる情報を分離します。Namespace のうち、最も単純で理解しやすいです。</div> <div> <br /></div> <div> <u>NET Namespace(Kernel 2.6.29)</u></div> <div> <br /></div> <div> NET(Network) Namespace は、ネットワークデバイス、IPアドレス、ルーティングテーブル、iptables 情報を分離します。</div> <div> <br /></div> <div> Docker は、これらの機能により、プロセス間に仕切りを設け、空間を分離することでコンテナを構成します。</div> <div> 上記以外にも、ユーザおよびグループを分離する User Namespace(Kernel 3.8)という機能もあります。将来的に Docker が利用するかもしれません。</div> <div> <br /></div> <div> <br /></div> <div> <b>Cgroups</b></div> <div> <br /> Cgroups はプロセス群に割り当てる計算資源(CPU、メモリ、I/O帯域)を管理する機能です。コンテナに割り当てる資源を調整するために使用します。<br /> <br /> <u>cpu</u><br /> <br /> 指定した通常優先度(Nice -19 ~ 20) プロセスが使用するCPUの利用割合です。<br /> <br /> <u>cpuset</u><br /> <br /> 指定したプロセスが動作するCPUを制限します。<br /> <br /> <u>memory</u><br /> <br /> 指定したプロセスが使用するメモリ量を制限します。<br /> <br /> <u>device</u><br /> <br /> 指定したプロセスが使用できるデバイス(/dev/*)を制限します。<br /> <br /> Docker はこれらの機能を使い、コンテナに割り当てる計算資源を制限します。<br /> 上記以外にも ブロックI/O 帯域を制限する blkio subsystem や、プロセス群を停止する freeze subsystem 等があります。将来的には Docker でも使われるでしょう。</div> <div> <br /></div> <div> <b>Storage</b></div> <div> <br /></div> <div> Docker は CoW(Copy on Write) と呼ばれる方式でコンテナイメージ間の差分を扱うことで、無駄なくコンテナイメージを管理します。Docker のコンテナイメージを管理する Storage プラグインでは、以下の Kernel の機能を使っています。<br /> <br /> <u>Device Mapper</u><br /> <br /> Device Mapper は、ファイルシステム等が発行するブロック I/O とデバイスのマッピング関係を管理します。Docker は Device Mapper の提供する thin-provisioning の機能と、snapshot 機能を活用しています。個別のファイルシステムに依存しないため、幅広い環境で使用することができます。ファイルシステムより下層にあり、ファイル差分を管理できないため、docker diff コマンドの実行速度は Btrfs と比べ遅くなります。<br /> <br /> <u>Btrfs</u><br /> <br /> Btrfs は Linux Kernel に取り込まれているファイルシステムの一つで、先進的な機能を持ちます。Docker は Btrfs の subvolume / snapshot 機能を使い、コンテナイメージの差分を管理します。差分はファイルシステム層で管理されるため、docker diff コマンドの実行速度はとても速いです。使用するには、docker のホームディレクトリが btrfs 形式でないといけません。現在 Docker の Btrfs Plugin は、200行ちょっとなのでとても簡単に読めます。<br /> <br /> <u>Aufs</u><br /> <br /> Aufs は union ファイルシステムの一種で、ファイルシステム層で差分を管理できる機能を持ちますが、Linux Kernel のメインラインに入っていないため、今後はあまり使われないでしょう。<br /> <br /></div> <div> <b>Networking</b></div> <div> <br /></div> <div> <u>veth</u><br /> <br /> 仮想的なネットワークデバイスのペアを作る機能です。Network Namespace と組み合わせ、ホストとコンテナ間での通信に使います。これも OpenVZ チームが中心に開発したものです。<br /> <br /> <u>bridge</u><br /> <br /> 仮想的なブリッジを作る機能です。上述の veth と組み合わせ、コンテナ間の通信に使います。QEMU /KVM でおなじみと思います。<br /> <br /> <u>iptables</u><br /> <br /> コンテナ間の通信を制御(Drop/Accept)するために使います。<br /> <br /> <br /> <br /></div> <div> <b>Security</b></div> <div> <br /> <u>Capability</u><br /> <br /> プロセスが持つ特権を細かい粒度で管理する機能です。コンテナ内から、ホストに悪影響を及ぼさないよう制御します。Docker では、例えば、カーネルモジュールのロード、OS時刻の変更などができないよう、デフォルト設定でコンテナ内プロセスの特権を落としています。デフォルト設定:<a href="https://github.com/dotcloud/docker/blob/master/daemon/execdriver/native/template/default_template.go">default_template.go</a><br /> <br /> <u>SElinux</u><br /> <br /> 強制アクセス制御機能です。Docker ではコンテナ起動時に SElinux MCS ラベルをコンテナに付与し、コンテナ内プロセスの動作をコンテナ内に制限します。<br /> <br /> <u>seccomp</u></div> <div> <br /> プロセスが発行できるシステムコールの種類を制限する機能です。Docker では、--lxc-conf でシステムコールリストファイルを指定してコンテナ内プロセスのシステムコール発行を制限できます。<br /> <br /> <br /> <b>まとめ</b><br /> <br /> Docker は数多くの Linux Kernel の機能により実現されていることがわかりました。今後、Cgroups、User Namespace、Checkpoint/Restart In Userspace などの実装が進むと思われます。<br /> <br /> <b>参考文献</b><br /> <b><br /></b> <a href="http://lwn.net/Articles/531114/#series_index">[LWN.net] Namespaces in operation, part 1: namespaces overview</a><br /> <a href="https://github.com/dotcloud/docker/blob/dbf249e23925decac52c0176d716c0ce205d4326/contrib/mkseccomp.sample">[LWN.net] &nbsp;LSS Secure Linux container</a><br /> <a href="https://speakerdeck.com/tenforward/linuxkontenaru-men-kontenafalseji-chu-tozui-xin-qing-bao-2014-04-12">Linux コンテナ入門</a><br /> <br /> <br /></div> Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-83382741491771871372014-05-08T00:55:00.000+09:002014-05-08T19:57:57.989+09:00Docker のビルド方法に見る Golang の利点以前、Docker をビルドしていて、以下の事実に気づきました。<br /> <br /> <h4> <b><u>事実: Docker は自身をビルドするのに Docker を用いてコンテナ内でビルドしている</u></b></h4> <br /> 実際、ソースコード直下に、以下の <a href="https://github.com/dotcloud/docker/blob/master/Dockerfile">Dockerfile</a> が置いてあります。中を参照すると、ubuntu のコンテナイメージをベースに、依存するソフトウェアを apt-get したり、git clone で取得したりしています。make コマンドで、依存するソフトウェアをインストールしたコンテナ内で、<a href="https://github.com/dotcloud/docker/blob/master/hack/make.sh">hack/make.sh</a>&nbsp;を実行し、バイナリを作成します。生成したバイナリをコンテナから取り出してビルド終了となります。<br /> <br /> Docker は Golang で書かれていますが、その理由の一つに、Golang の優れたポータビリティ (libc が入っている環境であればどこでも動作するバイナリを手軽に生成できること)があります。これにより、ビルドは Docker 内の固定した環境で行い、生成したバイナリだけを取得しインストールを済ませることができるのです。<br /> 参考:<a href="http://www.slideshare.net/jpetazzo/docker-and-go-why-did-we-decide-to-write-docker-in-go">Docker and Go: why did we decide to write Docker in Go?</a><br /> <br /> Docker を使ったビルドの利点は、<b>Docker さえ動く環境であれば、どこでもビルドできる</b>ため、ビルド環境の構築に手こずらなくて済むことにあります。<br /> 誰しも、ソフトウェアのビルド・インストール作業で以下のような苦い経験があるのでは無いでしょうか。<br /> <br /> <ol> <li>あるソフトウェアの最新のソースコードを取得してくる</li> <li>./configure がエラーを吐くので、エラーメッセージを参照し、依存するソフトウェアを yum install する</li> <li>まだ ./configure がエラーを吐くので、yum-builddep で依存するソフトウェアをまとめてインストールする</li> <li>まだ ./configure がエラーを吐く。yum-builddep でインストールしたソフトウェア X のバージョンが古いらしい。</li> <li>ソフトウェア X の最新バージョンをインストールすべく、1. に戻る</li> </ol> <div> Docker 内ビルドが広まると、上記のような作業で手こずることは稀になることでしょう。</div> <div> <br /></div> <div> Docker 内ビルドの欠点としては、Golang のようにポータビリティに優れた言語を採用しないといけないこと、と、実行バイナリが大きくなってしまうことがあります。</div> <div> <br /></div> <div> <b>まとめ</b></div> <div> <ul> <li>Docker は自身をビルドするのに Docker を用いている</li> <li>Docker が Golang で書かれている理由に、Golang の優れたポータビリティがある</li> <li>Docker 内ビルドでビルド・インストールにかかる手間が大幅に削減出来る&nbsp;</li> </ul> <div> <b>余談</b></div> </div> <div> <ul> <li>Docker をビルドする Docker はどうやってビルドするのか?? 典型的な<a href="http://ja.wikipedia.org/wiki/%E3%83%96%E3%83%BC%E3%83%88%E3%82%B9%E3%83%88%E3%83%A9%E3%83%83%E3%83%97%E5%95%8F%E9%A1%8C">ブートストラップ問題</a>ですね。</li> </ul> </div> Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-31836820919497407622014-02-26T21:27:00.001+09:002014-02-26T21:27:53.273+09:00Erlang VM(BEAM) スレッド構成Erlang の実行環境である BEAM の動作を理解するため、BEAM のスレッド構成を調査しました。<br /> <br /> BEAM は SMP(マルチコア) 環境と非 SMP 環境では動作が大きくことなります。SMP環境と非SMP環境に分けてスレッド構成を記載します。<br /> 調査対象の OTP のバージョンは&nbsp;R16B03-1です。<br /> <br /> <b>非SMP環境</b><br /> <br /> Erlang Interactive Shell を起動する際に、オプションとして '-smp disable' を付与すると、CPUはSMPでも、BEAMとしては非SMPモードで起動できます。<br /> 'erl -smp disable' で起動すると、11個のスレッドが見つかりました。11スレッドの内訳は以下のようになります。<br /> <table border="1" class="table-01" style="width: 100%px;"> <tbody> <tr><th>スレッド名</th><th>関数名</th><th>個数</th></tr> <tr><td>Main Thread</td><td>process_main</td><td>1</td></tr> <tr><td>Async Thread</td><td>async_main</td><td>10</td></tr> </tbody></table> <br /> <u>Main Thread</u><br />  BEAM byte-code を解釈し、実行するスレッドです。Erlang プロセスをスケジュールします。<br /> <br /> <u>Async Thread</u><br />  Erlang プロセスによるファイル操作を非同期に行います。プロセスが file モジュールを通じてファイルの読み書きや開閉を行うと、Main Thread に代わってAsync Threadがそれらの処理を請け負います。byte-code を解釈実行する Main Thread の動作を止めないために、処理を肩代わりしているのです。スレッドの起床は futex()システムコールでおこないます。<br />  Async Thread の個数は erl 起動時に '+A' オプションで変更できます。例えば、'erl +A 5' とすると、Async Thread は5個になります。ちなみに riak はデフォルトで64個起動します。<br /> <br /> <b>SMP環境</b><br /> <br />  SMP環境では些か構成が複雑になります。オプションなしで erl を起動すると、論理4core(物理2core)環境では19スレッドできました。内訳は以下になります。<br /> <br /> <table border="1" class="table-01" style="width: 100%px;"> <tbody> <tr><th>スレッド名</th><th>関数名</th><th>個数</th></tr> <tr><td>Main Thread</td><td>erts_sys_main_thread</td><td>1</td></tr> <tr><td>Signal Handling Thread</td><td>signal_dispatcher_thread_func</td><td>1</td></tr> <tr><td>System Message Handling Thread</td><td>sys_msg_dispatcher_func</td><td>1</td></tr> <tr><td>Async Thread</td><td>async_main</td><td>10</td></tr> <tr><td>Child Waiting Thread</td><td>child_waiter</td><td>1</td></tr> <tr><td>Scheduling Thread</td><td>sched_thread_func</td><td>4</td></tr> <tr><td>Aux Thread</td><td>aux_thread</td><td>1</td></tr> </tbody></table> <br /> <u>Main Thread</u><br />  非SMP環境とは異なり、Erlang プロセスの実行はしません。単にシグナルを受信して、pipe経由でSignal Handling Thread に通知するだけのスレッドです。select(0, NULL, NULL, NULL, NULL) で待ちぼうけです。<br /> <br /> <u>Signal Handling Thread</u><br />  シグナルハンドラ本体です。Main Threadが受信したシグナルに相当するハンドラを起動します。erl 起動時に '+B' オプションでシグナル受信時の挙動を変更できます。例えば 'erl +B i' でブレークシグナルを無視するようになります。<br /> <br /> <u>System Message Handling Thread</u><br />  システムメッセージのハンドラです。システムメッセージは、トレース情報の出力やプロセスの再開・中断等をリクエストする特殊なメッセージです。詳しくは <a href="http://www.erlang.org/doc/man/sys.html">sys module</a> のドキュメントを参照ください。<br /> <br /> <u>Async Thread</u><br />  非SMP環境と同様の非同期I/Oスレッドです。<br /> <br /> <u>Child Waiting Thread</u><br />  「<a href="http://www.erlang.org/documentation/doc-5.1/erts-5.1/notes.html">OTP-3906</a>&nbsp;: Solaris で子スレッドが大量に終了した際、 SIGCHLD がうまく伝わらない問題」を修正するため、子スレッドの終了を waitpid() で待ち受けます。<br /> <br /> <u>Scheduling Thread</u><br />  process_main() を実行し、 byte-code 解釈実行、プロセススケジューリングを行います。デフォルトでは論理コアと同じ数だけ生成されます。'+S' オプションでスレッド数を調整できます。他の Scheduling Thread と比較して負荷が偏らないようにバランシングとプロセスマイグレーションも行います。<br /> <br /> <u>Aux Thread</u><br />  若干時間のかかる処理を受け持つ補助的なスレッドです。メモリアロケーションやGCの情報を取得する際等に、Scheduling Thread から処理をオフロードされます。例えば、'elrang:statistics(garbage_collection)' でのGC統計情報取得は、aux_threadで行われます。<br /> <br /> 通常 erl 起動時に作られるスレッドは以上ですが、他にも、NIF や driver 関係のスレッドがあります。<br /> <br /> <b>まとめ</b><br /> <br />  ・Erlang VM(BEAM)のスレッド構成はSMP/非SMPで大きく異なる<br />  ・Scheduling Thread の動作を阻害しないために、一部処理が他スレッドにオフロードされる<br /> <br /> <b>参考文献</b><br /> <br /> <a href="http://erlang.org/doc/man/erl.html">Erlang User's Guide: erl</a><br /> Erlang/OTP のソースコード (otp/erts)<br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-41645487221763922832014-01-16T01:01:00.002+09:002014-01-16T01:01:55.490+09:00Erlang/OTP crypto モジュールエラー on Fedora 19Fedora 19 環境で自分でインストールした Erlang/OTP を使って、rebar を利用したところ、以下のようなエラーが出ました。<br /> <br /> <pre class="prettyprint">Uncaught error in rebar_core: {'EXIT', {undef, [{crypto,start,[]}, {rebar_core,run,1}, {rebar,main,1}, {escript,run,2}, {escript,start,1}, {init,start_it,1}, {init,start_em,1}]}} </pre> <br /> どうやら、crypto モジュールをstart()した時に問題が発生したようです。<br /> 調べてみると、bugzilla にエントリが在りました。<br /> <br /> <a href="https://bugzilla.redhat.com/show_bug.cgi?id=1023017">Bug 1023017 - Restore ECC support in Erlang's crypto library</a><br /> <br /> さらに詳しく調べると、ruby でも問題になっていました。<br /> <br /> <a href="https://bugs.ruby-lang.org/issues/9065">backport r41808(openssl build issue on fedora)</a><br /> <br /> 原因は、OpenSSL で OPENSSL_NO_EC が define されていないにも関わらず、OPENSSL_NO_EC2M が define されている環境においてビルドすると、定義されていないシンボル&nbsp;EC_GROUP_new_curve_GF2m (ガロア体GF(2m)上の楕円曲線暗号を扱う関数)を利用してしまうことにあるようです。<br /> 解決策として、Erlang/OTP の lib/crypto/c_src/crypto.c の&nbsp;EC_GROUP_new_curve_GF2m を利用している if 節を "ifndef OPENSSL_NO_EC2M" でくくってやれば問題ないことを確認しました。<br /> さて、パッチを送るかと思っていると、既に今より16日前に解決済みであることを知りました。<br /> <br /> 対策パッチ:<br /> <a href="https://github.com/RoadRunnr/otp/commit/8837c1be2ba8a3c123df3f5a87003daa9aac6539">crypto: selective support for GF2m curves</a><br /> <br /> 関連Pull Request:<br /> <a href="https://github.com/erlang/otp/pull/120">more EC curves</a><br /> <br /> 上記のパッチを当てれば、問題は解決されます。<br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-58231519777983965702014-01-08T19:57:00.000+09:002014-01-08T20:17:11.008+09:00BEAM(Erlang VM) 参考資料まとめ<b>はじめに</b><br /> <br /> Erlang/OTP で開発したアプリケーションは、通常 BEAM (Erlang VM)と呼ばれる仮想マシン上で動作させます。BEAMに関する資料は、今のところ世の中にあまり多くないようです。BEAMの情報が得やすくなるよう、ここにまとめておきます。<br /> <br /> <b>BEAM(Erlang VM) 参考資料</b><br /> <br /> <a href="http://www.erlang-factory.com/upload/presentations/708/HitchhikersTouroftheBEAM.pdf">Hitchhiker’s Tour of the BEAM</a><br /> <br /> Erlang Solutions Ltd. の Robert Virding 氏による BEAM の概要解説。Scheduler, Memory 管理, GC, Async Threads について小気味よくまとまっています。<br /> <br /> <span style="color: #0000ee; text-decoration: underline;"><a href="http://www.erlang-factory.com/upload/presentations/247/erlang_vm_1.pdf">The evolution of the Erlang VM</a></span><br /> <br /> 同氏による Erlang VM の歴史解説。Erlang VM ごく初期のProlog Interpreter や JAM(Joe's Abstract Machine) からBEAMに至るまでの経緯が書かれています。<br /> <br /> <a href="http://www.erlang-factory.com/upload/presentations/817/EngineTuningII_Beam.pdf">Erlang Engine Tuning, Know Your Engine – Part II: the BEAM</a><br /> <br /> ERTS本執筆中のErik (Happi) Stenman氏による BEAM解説。Erlang からコンパイルされたBEAM コードがどう解釈されて動作するのか説明されています。<br /> <br /> <a href="http://www.erlang.se/euc/08/euc_smp.pdf">Inside the Erlang VM</a><br /> <br /> Ericsson の Ludin 氏によるErlang VM のについての解説。 主に Scheduler の実装と SMP 対応について書かれています。<br /> <br /> <a href="http://jlouisramblings.blogspot.jp/2013/01/how-erlang-does-scheduling.html">How Erlang does scheduling</a><br /> <br /> Erlang VM の Scheduling 方法についての平易な解説。プロセス優先度設定や、プロセスコンテキストスイッチの契機、他の言語(実行環境)との差異について書かれています。<br /> <br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-23107975653314404302013-12-03T21:49:00.001+09:002013-12-06T09:04:44.029+09:00blktrace で block IO の分布を可視化する<b>はじめに</b><br /> <br /> <a href="http://git.kernel.dk/?p=blktrace.git;a=summary">blktrace</a> は block IO の発行をトレースする有名なツールです。blktrace で集めたトレースデータを解析する btt には、ディスク上での block IO の分布を可視化する bno_plot.py というツールが付属しています。bno_plot.py を使うと、gnuplot を用いて下記のような図を生成できます。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9JWA0oVFdnKY-MAzCAIfjQf_9w2k40DioLIrDINjbQVV4TYd18C9Z8bE5S-9MofS5npwi6kqZH73W65TrfltmuojwIQOdDFR8ohLBt1x6pMycKIX1uyxwnSxmIwlnMcmu8FWQ8lm1_OoS/s1600/bno_plot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9JWA0oVFdnKY-MAzCAIfjQf_9w2k40DioLIrDINjbQVV4TYd18C9Z8bE5S-9MofS5npwi6kqZH73W65TrfltmuojwIQOdDFR8ohLBt1x6pMycKIX1uyxwnSxmIwlnMcmu8FWQ8lm1_OoS/s320/bno_plot.png" width="320" /></a></div> <br /> 図では、x軸が時間、y軸が Block Number、z軸が Block per IO になっています。これから、いつ頃に、どのセクタに、どのくらいのIOが発行されたかがわかります。<br /> <br /> <b>使い方</b><br /> <br /> bno_plot.py の使い方をご説明します。<br /> blktrace と gnuplot をインストールします。<br /> <pre class="prettyprint"># yum -y install blktrace gnuplot </pre> <br /> blktrace でトレースを収集します。<br /> <pre class="prettyprint"># blktrace -w 30 -d /dev/sda -o sda </pre> <br /> 収集したトレース情報を、btt で解析します。解析したデータをファイルに出力します。<br /> <pre class="prettyprint"># btt -i sda.blktrace.2 -B sda_cpu2 </pre> サフィックスが r.dat, w.dat, c.dat になっているファイルが生成されます。それぞれ、Read, Write, Read and Write データに対応します。<br /> <br /> 解析データを bno_plot.py に与え、gnuplot で図を描画します。<br /> <pre class="prettyprint"># bno_plot.py sda_cpu2_8,0_c.dat </pre> これで、上記の画像のような図が得られます。<br /> <br /> Block IO の分布を可視化すると、どのように負荷がかかっているのかわかりやすいです。<br /> また、<a href="https://twitter.com/akiradeveloper">@akiradeveloper</a> 氏が開発している、<a href="https://github.com/akiradeveloper/dm-writeboost">dm-writeboost</a> のように、ランダムライトをシーケンシャルライトに変換する特殊なディスクキャッシュについて、IO分布を可視化すると特徴が見て取れて面白いかもしれません。<br /> <br /> <u><b>2013/12/6 追記</b></u>:<br /> 早速&nbsp;<a href="https://twitter.com/akiradeveloper">@akiradeveloper</a>&nbsp;氏がdm-writeboost の block IO 可視化をしてくれました!ありがとうございます。<br /> <a href="http://akiradeveloper.hatenadiary.com/entry/2013/12/06/013001">テストステ論:&nbsp;(writeboost report) writeboostのI/Oを可視化した</a><br /> 予想通りdmへのIOがランダムでも、その下のデバイスへのIOはシーケンシャルになっている様子が見て取れます。Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-85890854392973314372013-11-27T22:06:00.000+09:002013-11-27T22:06:49.614+09:00Erlang/OTP の systemtap トレース機能の使い方<b>はじめに</b><br /> <br /> Erlang/OTP には systemtap と連携したトレース機能が備わっています。Erlang/OTP で systemtap 連携を有効にすると、systemtap から Erlang のトレースポイントを扱うことができます。トレースポイントを使うことで、BEAM 仮想マシンの挙動をより正確に把握したり、性能解析しやすくなります。<br /> <br /> <b>使い方</b><br /> <br /> <u>Erlang/OTP の ビルド</u><br /> <br /> systemtap 連携を使うには、Erlang/OTP を configure --with-dynamic-trace=systemtap でビルドする必要があります。<br /> <pre class="prettyprint"># git clone git://github.com/erlang/otp.git # cd otp # ./otp_build autoconf # ./configure --with-dynamic-trace=systemtap # make </pre> <br /> Erlang Shell を起動して [systemtap] と表示されていれば、systemtap が有効になっていることが確認できます。<br /> <pre class="prettyprint"># ./bin/erl Erlang R16B03 (erts-5.10.4) [source-fb0006c] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [systemtap] Eshell V5.10.4 (abort with ^G) </pre> <br /> <u>stap スクリプト起動方法</u><br /> <br /> まず、stap -L コマンドで トレースポイントを確認しましょう。予め、beam バイナリファイルへの PATH を通しておいてください。<br /> <pre class="prettyprint"># PATH=/home/eiichi/git/otp/bin/x86_64-unknown-linux-gnu/:$PATH stap -L 'process("beam.smp").mark("*")' process("beam.smp").mark("aio_pool__add") $arg1:long $arg2:long process("beam.smp").mark("aio_pool__get") $arg1:long $arg2:long process("beam.smp").mark("bif__entry") $arg1:long $arg2:long process("beam.smp").mark("bif__return") $arg1:long $arg2:long process("beam.smp").mark("copy__object") $arg1:long $arg2:long process("beam.smp").mark("copy__struct") $arg1:long process("beam.smp").mark("dist__monitor") $arg1:long $arg2:long $arg3:long $arg4:long $arg5:long process("beam.smp").mark("dist__output") $arg1:long $arg2:long $arg3:long $arg4:long process("beam.smp").mark("dist__outputv") $arg1:long $arg2:long $arg3:long $arg4:long process("beam.smp").mark("dist__port_busy") $arg1:long $arg2:long $arg3:long $arg4:long ... </pre> <br /> 約60個のトレースポイント(user-probe 系を除く)が確認できます。Linux Kernel のトレースポイントは 約 1200個、Qemu のトレースポイントが 約 900個なのと比較すると、若干少ないですね。<br /> <br /> これらトレースポイントのうち、gc_major_start を例にトレースポイントの使い方を説明します。<br /> <br /> まず、下記のような stap スクリプトを用意しましょう。(otp/lib/runtime_tools/examples より抜粋)<br /> <pre class="prettyprint"># cat garbage-collection.systemtap probe process("beam.smp").mark("gc_major-start") { printf("GC major start pid %s need %d words\n", user_string($arg1), $arg2); } </pre> <br /> systemtap を有効にした Erlang Shell を起動し、下記のコマンドでsystemtap スクリプトを起動します。<br /> <pre class="prettyprint"># PATH=/home/eiichi/git/otp/bin/x86_64-unknown-linux-gnu/:$PATH stap garbage-collection.systemtap </pre> Erlang Shell で適当に &gt; "aaaaaaaaaa". などとコマンドを入力すると、stap スクリプトで下記のような出力が得られます。<br /> <pre class="prettyprint">GC major start pid <0 .33.0=""> need 9 words <!--0--><!--0--><!--0--><!--0--><!--0--><!--0--><!--0--></0></pre> <br /> GCの他にも、Message の送受信、プロセスの spawn、スケジュールなど、興味深く有用なトレースポイントがありあますので、お試しください。<br /> systemtap スクリプトの例は <a href="https://github.com/erlang/otp/tree/maint/lib/runtime_tools/examples">lib/runtime_tools/example</a> にあります。<br /> <br /> <u>dyntrace(user-probe)</u><br /> <br /> Erlang/OTP で systemtap 連携を有効にすると、dyntrace モジュールを使って、 Erlang コードから動的にsystemtap スクリプトに情報を出力することができます。<br /> Erlang/OTP に同伴されている、lib/runtime_tools/example/user-probe.systemtap を例にとって dyntrace モジュールの使いかたを説明します。<br /> <br /> まず、user-probe.systemtap は beam 向けになっていますので、beam.smp 向けに直しましょう。user-probe.systemtap を開き、process('beam') となっているところを process('beam.smp') に書き換えます。<br /> <pre class="prettyprint"># vim user-probe.systemtap </pre> <br /> 書き換えたら、Erlang Shell を起動し、user-probe.systemtap を起動します。(beam に PATHを通しておいてください。)<br /> <pre class="prettyprint"># stap user-proeb.systemtap </pre> <br /> この状態で、Erlang Shell にて、以下のように入力します。<br /> <pre class="prettyprint">2&gt; dyntrace:p(1, 2, 3, 4, "a", "b", "c"). true 3&gt; dyntrace:put_tag("test"). undefined 4&gt; dyntrace:p(1, 2, 3, 4, "a", "b", "c"). true </pre> <br /> すると、stap スクリプトでは、以下のような出力が得られます。<br /> <pre class="prettyprint"><0 .33.0=""> 1 2 3 4 'a' 'b' 'c' 'c' <0 .33.0=""> test 1 2 3 4 'a' 'b' 'c' 'c' <!--0--><!--0--><!--0--><!--0--></0></0></pre> <br /> dyntrace:p/nで各種情報を出力します。dyntrace:put_tag/1 でトレース出力のプレフィックスを設定することができます。<br /> 詳しくは<span style="color: #0000ee; text-decoration: underline;">Erlang User's Guide: dyntrace</span>&nbsp;をご覧ください。<br /> <br /> <b>参考文献</b><br /> <br /> <a href="http://www.snookles.com/scott/publications/dtrace.conf-2012.erlang-vm.pdf">Dtrace and Erlang: a new beginning</a><br /> <a href="http://www.erlang.org/doc/apps/runtime_tools/SYSTEMTAP.html">Erlang User's Guide: Systemtap and Erlang/OTP</a><br /> <a href="http://www.erlang.org/doc/man/dyntrace.html">Erlang User's Guide: dyntrace</a><br /> <a href="http://www.snookles.com/slf-blog/2011/11/19/systemtap-and-erlang-a-tutorial/">Systemtap and Erlang: a tutorial</a><br /> <a href="https://github.com/otphub/runtime_tools/blob/master/src/dyntrace.erl">runtime_tools/src/dyntrace.erl</a><br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-31753026275496625422013-11-22T00:25:00.001+09:002013-11-22T00:27:07.256+09:00OSS プロジェクト間の関連性を可視化してみました<b>はじめに</b><br /> <hr /> <br /> 以前、<a href="http://blog.etsukata.com/2013/06/ossweb.html">OSS の開発活動を可視化する WEB サービス</a> を作っていた時、 OSS プロジェクト相互の開発者の乗り入れ状況が気になりました。例えば 仮想マシンを管理するライブラリ libvirt と仮想マシンエミュレータ qemu では相当の共通開発者がいることが予想されます。また qemu と Linux Kernel も関係が深いはずです。<br /> これらの関係を可視化することで、OSS プロジェクト同士の関連性を読み取ってみましょう。関連性を探ることで、OSS 開発者のプロジェクト間の移動や、潮流まで理解できるかもしれません。<br /> <br /> <b>デモ</b><br /> <hr /> と、いうわけで、Chord Diagram を用いて OSS プロジェクト間の関連性を可視化してみました。対象としたOSSプロジェクトは、自分の趣味で選んだ以下のものです。<br /> "couchdb &nbsp;libvirt &nbsp;mongo &nbsp; &nbsp;node &nbsp;ocaml &nbsp;perl &nbsp; &nbsp; postgresql &nbsp;redis &nbsp;swift &nbsp; &nbsp; &nbsp;virt-manager<br /> cpython &nbsp;linux &nbsp; &nbsp;neutron &nbsp;nova &nbsp;otp &nbsp; &nbsp;php-src &nbsp;qemu &nbsp; &nbsp; &nbsp; &nbsp;riak &nbsp; systemtap"<br /> <br /> 下がその画像になります。<br /> <br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlnZWKtFam9Q_wLocnwGYaALqdNXg8ujRgg8XtAeuD6uzgyGu__PNfFZL2IsrJ8buOVRtfsM0c9suuYUGaanrFBO62j3phNes9rC79yMhcmsgHP09jbUolJk8vpeKu2vcK6UzvQCAtAR37/s1600/Screenshot_from_2013-11-21+22:58:28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlnZWKtFam9Q_wLocnwGYaALqdNXg8ujRgg8XtAeuD6uzgyGu__PNfFZL2IsrJ8buOVRtfsM0c9suuYUGaanrFBO62j3phNes9rC79yMhcmsgHP09jbUolJk8vpeKu2vcK6UzvQCAtAR37/s320/Screenshot_from_2013-11-21+22:58:28.png" width="320" /></a></div> 画像だけではいまいちわかりにくいので、インタラクティブなデモを用意しました。<br /> こちらです : <a href="http://etsukata.com/ossrel/">OSS Relationship Visualization</a><br /> コードは GitHub で公開しています : <a href="https://github.com/Etsukata/ossrel">Etsukata/ossrel</a><br /> カーソルを各プロジェクトの弧に乗せると、そのプロジェクトと他プロジェクトの関連性のみが表示されます。<br /> 観察してみると、以下のことが読み取れました。<br /> <br /> ・仮想化(qemu, libvirt, virt-manager, OpenStack) は互いに関連性が強い<br /> ・Linux Kernel と qemu は関連性が強い<br /> ・OpenStack(nova, swift, neutron) 同士は関連性が強い<br /> ・Erlang/OTP と riak, couchdb は関連性が強い<br /> ・データベース(mongo, redis, riak, ...) と 仮想化(qemu, libvirt, ...) は関連性が薄い<br /> ・Ocaml とその他のOSS は関連性が薄い<br /> <br /> などなど。<br /> あらかた予想通りであることがわかりましたが、予想していなかったことがありました。<br /> <br /> ・ほとんどのプロジェクト同士は、関連性がある<br /> <br /> 複数のプロジェクトに貢献するのは、幅広い興味と知識が要求されるため、難しいことです。しかし、世の中にはそれが出来る人が多数いるんだなぁ、と感心させられました。<br /> <br /> <b>実装</b><br /> <hr /> <br /> 実装について、簡単に記載します。<br /> 処理の流れは以下のようになっています。<br /> 1. 対象となるOSSの git commit logから author name を抜き出す<br /> 2. OSS プロジェクト同士(A, B)の関連性を以下の式で計算する<br /> 関連性 = sqrt(sum_{共通開発者} min(プロジェクトAへのコミット数, プロジェクトBへのコミット数))<br /> 平方根をとっているのは、Chord Diagram の見た目を整えるためです。<br /> 3. 関連性から隣接行列を生成する<br /> 4. 隣接行列を D3js に渡し、Chord Diagram で表示する。<br /> <br /> <br /> ここでは、自分が選択したプロジェクト間のみ対象に可視化しましたが、<b>可視化対象は自由に選択できます</b>。<a href="https://github.com/Etsukata/ossrel">ossrel </a>を clone し、repos ディレクトリに対象となる git レポジトリを置き、run.sh を走らせるだけで、可視化できます。やり方は <a href="https://github.com/Etsukata/ossrel/blob/master/README.md">README.md</a> に記載しました。<br /> 関連性が気になる OSS があったら、ぜひ可視化してみてください。<br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-72307289794652736932013-10-31T22:34:00.000+09:002014-10-16T23:36:15.625+09:00iostat -x の出力を Linux Kernel ソースコードから理解する<b>はじめに</b><br /> <hr /> <br /> iostat は IO の出力速度や待ち時間の計測によく使われるコマンドです。"-x" オプションをつけると、平均待ち時間(await)やリクエストキュー長(avgqu-sz)、サービスタイム(svctm)などの詳細な情報を出力することができ、とても便利です。データベースをはじめとし、各種アプリケーションのパフォーマンスを計測するための重要な指標となります。<br /> 今回は、これらの出力結果について、より詳細かつ正確な意味を、Linux Kernelのソースコードを読んで理解しましょう。かなり長くなってしまったので、意味を把握したい方は下の方の "iostat -x 出力結果まとめ" をご覧ください。<br /> <br /> <b>iostatの挙動</b><br /> <hr /> <br /> まず、iostatの挙動を調べます。iostatは、read_sysfs_file_stat()で指定したインターバルごとに /proc/diskstats の情報を読み取り、compute_ext_disk_stats()で各種統計情報の計算を行い、結果をwrite_ext_stat()で出力します。read_sysfs_file_stat()、compute_ext_disk_stats()、write_ext_stat()の一部を下記に示します。<br /> <br /> <u>read_sysfs_file_stat()</u>:<br /> <pre class="prettyprint">int read_sysfs_file_stat(int curr, char *filename, char *dev_name) { ... i = fscanf(fp, "%lu %lu %lu %lu %lu %lu %lu %u %u %u %u", &amp;rd_ios, &amp;rd_merges_or_rd_sec, &amp;rd_sec_or_wr_ios, &amp;rd_ticks_or_wr_sec, &amp;wr_ios, &amp;wr_merges, &amp;wr_sec, &amp;wr_ticks, &amp;ios_pgr, &amp;tot_ticks, &amp;rq_ticks); if (i == 11) { /* Device or partition */ sdev.rd_ios = rd_ios; sdev.rd_merges = rd_merges_or_rd_sec; sdev.rd_sectors = rd_sec_or_wr_ios; sdev.rd_ticks = (unsigned int) rd_ticks_or_wr_sec; sdev.wr_ios = wr_ios; sdev.wr_merges = wr_merges; sdev.wr_sectors = wr_sec; sdev.wr_ticks = wr_ticks; sdev.ios_pgr = ios_pgr; sdev.tot_ticks = tot_ticks; sdev.rq_ticks = rq_ticks; } ... }</pre> <br /> read_sysfs_file_stat() では、/proc/diskstatsの各種フィールドを記録しています。これらパラメータが、カーネル内でどのような意味を持つかを詳細に理解するのが本資料の目的です。<br /> <br /> <u>compute_ext_disk_stat():</u><br /> <pre class="prettyprint">/* *************************************************************************** * Compute "extended" device statistics (service time, etc.). * * IN: * @sdc Structure with current device statistics. * @sdp Structure with previous device statistics. * @itv Interval of time in jiffies. * * OUT: * @xds Structure with extended statistics. *************************************************************************** */ void compute_ext_disk_stats(struct stats_disk *sdc, struct stats_disk *sdp, unsigned long long itv, struct ext_disk_stats *xds) { double tput = ((double) (sdc-&gt;nr_ios - sdp-&gt;nr_ios)) * HZ / itv; xds-&gt;util = S_VALUE(sdp-&gt;tot_ticks, sdc-&gt;tot_ticks, itv); xds-&gt;svctm = tput ? xds-&gt;util / tput : 0.0; /* * Kernel gives ticks already in milliseconds for all platforms * =&gt; no need for further scaling. */ xds-&gt;await = (sdc-&gt;nr_ios - sdp-&gt;nr_ios) ? ((sdc-&gt;rd_ticks - sdp-&gt;rd_ticks) + (sdc-&gt;wr_ticks - sdp-&gt;wr_ticks)) / ((double) (sdc-&gt;nr_ios - sdp-&gt;nr_ios)) : 0.0; xds-&gt;arqsz = (sdc-&gt;nr_ios - sdp-&gt;nr_ios) ? ((sdc-&gt;rd_sect - sdp-&gt;rd_sect) + (sdc-&gt;wr_sect - sdp-&gt;wr_sect)) / ((double) (sdc-&gt;nr_ios - sdp-&gt;nr_ios)) : 0.0; } </pre> <br /> util, svctm, await, avgrq-sz パラメータについて、各種計算を行っています。<br /> <br /> <u>write_ext_stat()</u>:<br /> <pre class="prettyprint">/* *************************************************************************** * Display extended stats, read from /proc/{diskstats,partitions} or /sys. * * IN: * @curr Index in array for current sample statistics. * @itv Interval of time. * @fctr Conversion factor. * @shi Structures describing the devices and partitions. * @ioi Current sample statistics. * @ioj Previous sample statistics. *************************************************************************** */ void write_ext_stat(int curr, unsigned long long itv, int fctr, struct io_hdr_stats *shi, struct io_stats *ioi, struct io_stats *ioj) { ... /* rrq/s wrq/s r/s w/s rsec wsec rqsz qusz await r_await w_await svctm %util */ printf(" %8.2f %8.2f %7.2f %7.2f %8.2f %8.2f %8.2f %8.2f %7.2f %7.2f %7.2f %6.2f %6.2f\n", S_VALUE(ioj-&gt;rd_merges, ioi-&gt;rd_merges, itv), S_VALUE(ioj-&gt;wr_merges, ioi-&gt;wr_merges, itv), S_VALUE(ioj-&gt;rd_ios, ioi-&gt;rd_ios, itv), S_VALUE(ioj-&gt;wr_ios, ioi-&gt;wr_ios, itv), ll_s_value(ioj-&gt;rd_sectors, ioi-&gt;rd_sectors, itv) / fctr, ll_s_value(ioj-&gt;wr_sectors, ioi-&gt;wr_sectors, itv) / fctr, xds.arqsz, S_VALUE(ioj-&gt;rq_ticks, ioi-&gt;rq_ticks, itv) / 1000.0, xds.await, r_await, w_await, /* The ticks output is biased to output 1000 ticks per second */ xds.svctm, /* * Again: Ticks in milliseconds. * In the case of a device group (option -g), shi-&gt;used is the number of * devices in the group. Else shi-&gt;used equals 1. */ shi-&gt;used ? xds.util / 10.0 / (double) shi-&gt;used : xds.util / 10.0); /* shi-&gt;used should never be null here */ ... } </pre> <br /> compute_ext_disk_stat() で計算した結果と併せて、各種情報を出力しています。S_VALUEは差分をとってインターバルで割るマクロです。<br /> <br /> <b>/proc/diskstats 詳細</b><br /> <hr /> <b><br /></b> <br /> さて、iostat コマンドの挙動は把握できました。ポイントとなるのは、"/proc/diskstats では どのような情報を出力しているのか" です。これらを正確に理解するには、カーネルのソースコードを読む必要がでてきます。<br /> <br /> まず、ソースコードを読む前にドキュメントを調べましょう。カーネルソースディレクトリ以下に<a href="https://www.kernel.org/doc/Documentation/iostats.txt">/Documentation/iostats.txt</a>があります。以下に一部を抜粋します。<br /> <br /> <u>Documentaition/iostast.txt</u>:<br /> <pre class="prettyprint">Field 1 -- # of reads completed This is the total number of reads completed successfully. Field 2 -- # of reads merged, field 6 -- # of writes merged Reads and writes which are adjacent to each other may be merged for efficiency. Thus two 4K reads may become one 8K read before it is ultimately handed to the disk, and so it will be counted (and queued) as only one I/O. This field lets you know how often this was done. Field 3 -- # of sectors read This is the total number of sectors read successfully. Field 4 -- # of milliseconds spent reading This is the total number of milliseconds spent by all reads (as measured from __make_request() to end_that_request_last()). Field 5 -- # of writes completed This is the total number of writes completed successfully. Field 6 -- # of writes merged See the description of field 2. Field 7 -- # of sectors written This is the total number of sectors written successfully. Field 8 -- # of milliseconds spent writing This is the total number of milliseconds spent by all writes (as measured from __make_request() to end_that_request_last()). Field 9 -- # of I/Os currently in progress The only field that should go to zero. Incremented as requests are given to appropriate struct request_queue and decremented as they finish. Field 10 -- # of milliseconds spent doing I/Os This field increases so long as field 9 is nonzero. Field 11 -- weighted # of milliseconds spent doing I/Os This field is incremented at each I/O start, I/O completion, I/O merge, or read of these stats by the number of I/Os in progress (field 9) times the number of milliseconds spent doing I/O since the last update of this field. This can provide an easy measure of both I/O completion time and the backlog that may be accumulating.</pre> <br /> iostat.txtに加え、<a href="https://www.kernel.org/doc/Documentation/block/stat.txt">Documentation/block/stat.txt</a>にも有用な情報があります。 <br /> <pre class="prettyprint">Name units description ---- ----- ----------- read I/Os requests number of read I/Os processed read merges requests number of read I/Os merged with in-queue I/O read sectors sectors number of sectors read read ticks milliseconds total wait time for read requests write I/Os requests number of write I/Os processed write merges requests number of write I/Os merged with in-queue I/O write sectors sectors number of sectors written write ticks milliseconds total wait time for write requests in_flight requests number of I/Os currently in flight io_ticks milliseconds total time this block device has been active time_in_queue milliseconds total wait time for all requests </pre> Documentationを読むとフィールドの意味が大まかに理解できます。<br /> ではソースコードを読んでいきます。/proc/diskstats を read した時に呼ばれるのは diskstats_show() です。<br /> <br /> <u>diskstats_show()</u>:<br /> <pre class="prettyprint">tatic int diskstats_show(struct seq_file *seqf, void *v) { struct gendisk *gp = v; struct disk_part_iter piter; struct hd_struct *hd; char buf[BDEVNAME_SIZE]; int cpu; ... while ((hd = disk_part_iter_next(&amp;piter))) { cpu = part_stat_lock(); part_round_stats(cpu, hd); part_stat_unlock(); seq_printf(seqf, "%4d %7d %s %lu %lu %lu " "%u %lu %lu %lu %u %u %u %u\n", MAJOR(part_devt(hd)), MINOR(part_devt(hd)), disk_name(gp, hd-&gt;partno, buf), part_stat_read(hd, ios[READ]), part_stat_read(hd, merges[READ]), part_stat_read(hd, sectors[READ]), jiffies_to_msecs(part_stat_read(hd, ticks[READ])), part_stat_read(hd, ios[WRITE]), part_stat_read(hd, merges[WRITE]), part_stat_read(hd, sectors[WRITE]), jiffies_to_msecs(part_stat_read(hd, ticks[WRITE])), part_in_flight(hd), jiffies_to_msecs(part_stat_read(hd, io_ticks)), jiffies_to_msecs(part_stat_read(hd, time_in_queue)) ); } ... } </pre> part_stat_readマクロで hd_struct から各種情報を読み取っていることがわかります。<br /> キーとなる構造体は disk_stats です。下記には、// 以下に対応する iostat -x のフィールドを追記してあります。<br /> <br /> <u>disk_stats</u>:<br /> <pre class="prettyprint">struct disk_stats { unsigned long sectors[2]; /* READs and WRITEs */ // rsec/s wsec/s avgrq-sz unsigned long ios[2]; // r/s w/s unsigned long merges[2]; // rrqm/s wrqm/s unsigned long ticks[2]; // await r_wait w_wait unsigned long io_ticks; // %util svctm unsigned long time_in_queue; // avgqu-sz }; </pre> <br /> disk_statsのメンバが更新されるタイミングを追っていきましょう。<br /> <br /> hd_struct の各メンバが更新されるのは、blk_account_io_completion() および、 blk_account_io_done() 、drive_stat_acct() です。blk_account_io_completion() と blk_account_io_done() は、リクエスト完了時に呼ばれるblk_end_bidi_request() から呼ばれます。<br /> <br /> コールグラフはざっと以下のような感じです。<br /> blk_end_bidi_request()<br /> -&gt; blk_update_bidi_request()<br /> &nbsp; -&gt; blk_update_request()<br /> &nbsp; &nbsp; -&gt; blk_account_io_completion()<br /> -&gt; blk_finish_request()<br /> &nbsp; -&gt; blk_account_io_done()<br /> <br /> <u>blk_account_io_completion()</u>:<br /> <pre class="prettyprint">static void blk_account_io_completion(struct request *req, unsigned int bytes) { if (blk_do_io_stat(req)) { const int rw = rq_data_dir(req); struct hd_struct *part; int cpu; cpu = part_stat_lock(); part = req-&gt;part; part_stat_add(cpu, part, sectors[rw], bytes &gt;&gt; 9); part_stat_unlock(); } } </pre> blk_account_io_completion() では、IOが完了したバイト数をsectors に加算しています。<br /> <br /> <u>blk_account_io_done()</u>:<br /> <pre class="prettyprint">static void blk_account_io_done(struct request *req) { /* * Account IO completion. flush_rq isn't accounted as a * normal IO on queueing nor completion. Accounting the * containing request is enough. */ if (blk_do_io_stat(req) &amp;&amp; !(req-&gt;cmd_flags &amp; REQ_FLUSH_SEQ)) { unsigned long duration = jiffies - req-&gt;start_time; const int rw = rq_data_dir(req); struct hd_struct *part; int cpu; cpu = part_stat_lock(); part = req-&gt;part; part_stat_inc(cpu, part, ios[rw]); part_stat_add(cpu, part, ticks[rw], duration); part_round_stats(cpu, part); part_dec_in_flight(part, rw); hd_struct_put(part); part_stat_unlock(); } } </pre> <br /> blk_account_io_done() では、IO回数を表す ios をインクリメントし、 await の計算に使われる ticks に duration を加算しています。さらに実行中のリクエスト数(=リクエストキューの長さ)を part_dec_in_flight()でデクリメントしています。<br /> durationについては、後ほど詳述します。<br /> <br /> part_round_stats() では、その延長で、全てのリクエストがキューにいた時間の積算値を表すtime_in_queue と、デバイスがIOリクエストを発行して、未完了のIOが存在する時間を表す io_ticks を更新しています。<strike>io_ticksには前回のリクエスト完了から、今回のリクエスト完了までを加算し、</strike>訂正(<a href="https://twitter.com/yoheia">@yohei-a</a>さんのエントリ:「<a href="http://d.hatena.ne.jp/yohei-a/20141013/1413167192#c">iostat はどのように %util を算出しているか(3)</a>」を読んで間違いに気づきました。ありがとうございます!) io_ticksにはIO 発行中の時間(part_in_flightが0より大きい時間)を加算し、time_in_queueにはそれに実行中のリクエスト数を掛けたものを加算しているのがわかります。<br /> <pre class="prettyprint">static void part_round_stats_single(int cpu, struct hd_struct *part, unsigned long now) { if (now == part-&gt;stamp) return; if (part_in_flight(part)) { __part_stat_add(cpu, part, time_in_queue, part_in_flight(part) * (now - part-&gt;stamp)); __part_stat_add(cpu, part, io_ticks, (now - part-&gt;stamp)); } part-&gt;stamp = now; } </pre> <br /> blk_account_io_done() で ticks に duration を加えています。duration は submit_bio の延長で呼ばれる blk_rq_init()でカウントが開始されます。<br /> コールスタックは以下のような感じ。<br /> <br /> submit_bio()<br /> -&gt;&nbsp;generic_make_request()<br /> &nbsp; -&gt;&nbsp;blk_queue_bio()<br /> &nbsp; &nbsp; -&gt; get_request()<br /> &nbsp; &nbsp; &nbsp; -&gt; blk_rq_init()<br /> <br /> <u>blk_rq_init()</u>:<br /> <pre class="prettyprint">void blk_rq_init(struct request_queue *q, struct request *rq) { ... rq-&gt;start_time = jiffies; ... } </pre> <br /> 最後に、hd_struct の merges を更新する&nbsp;drive_stat_acct() についてです。これは、リクエストをつくる際に、bioをリクエストにマージしようと試みる関数<br /> bio_attempt_back_merge() または、リクエストを作成する blk_queue_bio() で呼ばれます。マージできたら merges カウントをインクリメントし、できなかったら実行中のリクエスト数をインクリメントし、io_ticksカウント用のstampを初期化(part_round_stat())します。<br /> コールスタックは以下のような感じ。<br /> <br /> submit_bio()<br /> -&gt; generic_make_request()<br /> &nbsp; -&gt; blk_queue_bio()<br /> &nbsp; &nbsp; -&gt; bio_attempt_back_merge()<br /> &nbsp; &nbsp; &nbsp; &nbsp;-&gt; drive_stat_acct()<br /> または<br /> submit_bio()<br /> -&gt; generic_make_request()<br /> &nbsp; -&gt; blk_queue_bio()<br /> &nbsp; &nbsp; &nbsp;-&gt; drive_stat_acct()<br /> <br /> <u>drive_stat_acct()</u>:<br /> <pre class="prettyprint">static void drive_stat_acct(struct request *rq, int new_io) { struct hd_struct *part; ... if (!new_io) { part = rq-&gt;part; part_stat_inc(cpu, part, merges[rw]); } else { ... part_round_stats(cpu, part); part_inc_in_flight(part, rw); ... } </pre> <br /> 以上でiostat -x の出力を読み解くカーネル内の情報は手にはいりました。<br /> <br /> <b>iostat -x 出力結果まとめ</b><br /> <hr /> <br /> カーネルのソースコードを読んで得た情報から、 iostat -x の出力フィールドそれぞれについて、詳細な意味をまとめます。<br /> <br /> <u>rrqm/s &nbsp; wrqm/s</u> :<br /> 一秒間にマージされたリード/ライト・リクエストの数。リクエストを作るgeneric_make_request()の延長で、bioをリクエストにマージしようと試み(bio_attempt_back_merge())、成功したらカウントされる。<br /> <br /> <u>r/s &nbsp; &nbsp; w/s</u>:<br /> 一秒間に発行したリード/ライト・リクエストの数。リクエストが完了した際に呼ばれるblk_end_bidi_request()の延長でカウント(blk_account_io_done())している。<br /> <br /> <u>rkB/s &nbsp; &nbsp;wkB/s</u>:<br /> 一秒間に発行したリード/ライト・リクエストが読み書きに成功したkB数。リクエストが完了した際に呼ばれるblk_end_bidi_request()の延長でカウント(blk_account_io_completion()) している。<br /> <br /> <u>avgrq-sz</u>:<br /> 発行が完了したリクエストの平均サイズ。<br /> <br /> <u>avgqu-sz</u>:<br /> 平均リクエストキューの長さの<b>指標となる数値</b>。正確にリクエストキューの長さというわけではなく、IOリクエストが作成されてから、発行が完了されるまでの総待ち時間値(time_in_queue)の平均。<br /> <br /> <u>await</u>:<br /> リード・ライトリクエストが作成(get_request())されてから、リクエスト完了(blk_finish_request())までにかかる時間の平均値。<br /> <br /> <u>r_await w_await</u>:<br /> それぞれ、awaitのリード版・ライト版。<br /> <br /> <u>svctm</u>:<br /> 一回のIO発行にかかる時間(<b>リクエストがキューに積まれている時間を含まない</b>)の平均値。awaitとは異なり、IOスケジューラでリクエストがqueueに積まれている時間は含まれない。この数値によりデバイスがIOを発行してから完了にかかるまでの時間がわかる。<br /> <br /> <u>%util</u>:<br /> IO発行にかかる時間(リクエストがキューに積まれている時間を含まない)の、インターバルに占める割合(%)<br /> <br /> 以上、長くなりましたが、(若干端折りつつ)iostat -x の出力を Kernel ソースコードから追ってみました。ソースコードを追うと、それぞれの出力結果の意味をより正確につかむことができます。また、avgqu-szが正確にリクエストキューの長さの平均を表すものではなく、その指標になる数値(総待ち時間の平均)であることなど、発見がありました。<br /> <br /> <b>参考情報</b><br /> <hr /> <br /> <a href="https://www.kernel.org/doc/Documentation/block/stat.txt">Documentation/block/stat.txt</a><br /> <a href="https://www.kernel.org/doc/Documentation/iostats.txt">Documentation/iostats.txt</a><br /> <a href="https://github.com/sysstat/sysstat">GitHub: sysstat/sysstat</a>Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-31878711814092804342013-10-29T21:44:00.000+09:002013-10-29T21:47:01.300+09:00トレースデータを可視化する d3js_trace をつくりました<b>はじめに</b><br /> <br /> ソフトウェアの性能解析をしている時、どこにCPUボトルネックがあるのか知りたくなることがあります。そういった場合、プロファイリングツールを使ってスタックトレースを採取し、CPUを消費している場所や割合に関する統計情報を解析するのが有効です。しかし、一般的に採取するデータ量は膨大になってしまい、欲しい情報を解析するのはなかなか骨の折れる作業です。<br /> そこで、今回はトレース情報を可視化するツール:<a href="https://github.com/Etsukata/d3js_trace.git">d3js_trace</a> を作ってみました。d3js_trace は、perf で取得したスタックトレース情報を、JavaScript ライブラリ : <a href="http://d3js.org/">D3js</a> を用いて可視化します。可視化により、人間が解析しやすい形で表現することで、より容易にトレースデータを解析できるようになります。<br /> <br /> コードについては GitHub に公開しています: <a href="https://github.com/Etsukata/d3js_trace">Etsukata/d3js_trace</a><br /> <br /> <b>例</b><br /> <br /> 以下に掲載した画像は、perf でシステム全体のプロファイリングをとったものです。コマンドは "perf record -a -g fp sleep 3 "。どこでどのくらいCPUを消費したかが、階層を組んだ放射型のグラフにより表現されています。<br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz9u4UjhmC4A34lc-aflKTc-WgrTEyUyQ8mEF-DiI8Xx6_d5SEh8U1Y_1bDWGr9nrRbcfAa7bZAiI67DQGhIsG0HKnOxuOoWf3nYPjUvYkwRA-nuRkGXfpddGxYVFeuazQR36Mbm_XtzrK/s1600/se.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz9u4UjhmC4A34lc-aflKTc-WgrTEyUyQ8mEF-DiI8Xx6_d5SEh8U1Y_1bDWGr9nrRbcfAa7bZAiI67DQGhIsG0HKnOxuOoWf3nYPjUvYkwRA-nuRkGXfpddGxYVFeuazQR36Mbm_XtzrK/s320/se.png" width="320" /></a></div> 画像をみるだけよりは、インタラクティブなデモをご覧いただけるほうが、理解しやすいと思います⇛<a href="http://etsukata.com/d3js_trace/sample1/">デモ</a><br /> 図にカーソルを合わせると、CPU使用割合とコールスタックが表示されます。<br /> <br /> <b>使い方</b><br /> <br /> d3js_trace は<a href="http://etsukata.com/d3js_trace/sample1/">上記デモ</a>のような解析ページを生成します。その使い方をご紹介します。<br /> <br /> まず git clone します。 <br /> <pre class="prettyprint"># git clone https://github.com/Etsukata/d3js_trace.git # cd d3js_trace</pre> <br /> perf でスタックトレースを収集します。今回は例として、-a でシステム全体を対象に収集しています。<br /> <pre class="prettyprint"># perf record -g fp -a sleep 3</pre> <br /> perf script コマンドでテキストデータで出力し、それを python スクリプトで d3js で読む JSON 形式に変換します。<br /> <pre class="prettyprint"># perf script | ./d3trace.py &gt; trace_json.js</pre> <br /> WEBブラウザで置いてある index.html を開きます。<br /> <pre class="prettyprint"># firefox index.html</pre> <br /> すると、<a href="http://etsukata.com/d3js_trace/sample1/">デモ</a>のようなページが表示されます。<br /> <br /> <b>TODO</b><br /> <b><br /></b> d3js_trace は作ってみたばかりなので、色々と改良したいことがあります。<br /> <br /> <ul> <li>色付け</li> <li>ズーム可能にする</li> <li>d3jsの他のExampleを使えるようにする</li> <li>ftrace のトレース情報などを利用し、レイテンシトレースを可視化できるようにする</li> </ul> <br /> ...などなど<br /> <br /> <b>Thanks to</b><br /> <br /> d3js_trace は <a href="http://www.brendangregg.com/">Brendan Gregg</a>&nbsp;さんの <a href="https://github.com/brendangregg/FlameGraph">FlameGraph</a> を参考につくりました。また、データ可視化部には d3js の <a href="http://sequence%20sunburst/">Sequence Sunburst</a> を使っています。<br /> 素晴らしいツールを開発された方々に感謝します。<br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-19459407263734988372013-10-12T10:51:00.001+09:002013-10-29T21:44:36.708+09:00Virsh で Qemu/KVM Live Block Migration<b>はじめに</b><br /> <br /> 仮想環境での Live Migration というと、仮想マシン移行元と移行先ホストでディスクを共有した上で行うのが一般的です。Live Block Migration は、共有ディスクが無い場合でも、仮想ストレージを移行させることにより Live Migration を実現する技術です。VMWare においては、<a href="http://www.vmware.com/jp/products/vsphere/features-storage-vmotion">Storage vMotion</a> と呼ばれています。今回は、Qemu/KVM 環境において virsh を使った Live Block Migration の使い方をご紹介します。検証環境は Fedora 19です。<br /> <br /> Live Block Migration には、仮想マシンの仮想ストレージすべてをコピーする Full モードと、Backing File との差分のみコピーする Incremental モードがあります。下記でそれぞれを紹介します。<br /> <br /> <b>Live Block Migration の使い方(Full編)</b><br /> <br /> Live Block Migration を行う前に、前準備として移行先において仮想ストレージのスタブを準備する必要があります。(libvirt-devel に事前にスタブを作成するパッチが投稿されているため、この作業は必要なくなるかもしれません。パッチ:&nbsp;<a href="https://www.redhat.com/archives/libvir-list/2013-September/msg00751.html">Pre-create storage on live migration</a>)<br /> まず、移行元ディスクの容量を調べます。ここではQcow2フォーマットを用いています。<br /> <pre class="prettyprint">[@src] # qemu-img info f19.img image: f19.img file format: qcow2 virtual size: 49G (52428800000 bytes) disk size: 4.9G cluster_size: 65536 </pre> <br /> 移行先にて、スタブを作成します。移行元と同じパスに、移行元のディスク容量と同じ容量のスタブを作成します。<br /> <pre class="prettyprint">[@dst] # qemu-img create -f qcow2 f19.img 52428800000 </pre> <br /> 移行元にて、virsh コマンドでマイグレーションを開始します。通常の Live Migrationでのオプションに加え、--copy-storage-all をつけることにより、Block Migration になります。<br /> <pre class="prettyprint">[@src] # virsh migrate --live --verbose --copy-storage-all f19 qemu+ssh://dst-ip/system </pre> デフォルトでは port : 45192 を使うので、開けておきましょう。<br /> すべてのストレージをコピーするため、マイクレーションには結構(数分)時間がかかります。<br /> マイグレーションが完了したら、移行先で仮想マシンが稼働していることを確認しましょう。<br /> <br /> <b>Live Block Migration の使い方(Incremental編)</b><br /> <br /> 仮想ストレージ全てをコピーする Full モードは、かなり時間がかかってしまうという問題があります。Qemu/KVM には、事前にベースとなる仮想ストレージ(backing)を作成し、それとの差分のみを記録する、スナップショット機能があります。この機能との組みあせで Live Block Migration を行うと、backing との差分のみがコピーされるため、マイグレーション時間を短縮できます。<br /> <br /> スナップショットの作成:<br /> 仮想マシンが稼働していない状態で行います。<br /> <pre class="prettyprint">[@src] # qemu-img create -f qcow2 -b base.img migrate-inc.img </pre> -b でベースとなる backing file を指定し、migrate-inc.img を作成しました。<br /> 移行先でも同じコマンドでスタブを作成しておきます。<br /> <pre class="prettyprint">[@dst] # qemu-img create -f qcow2 -b base.img migrate-inc.img </pre> <br /> 移行元で、仮想ストレージに migrate-inc.img を指定した仮想マシンを作成し、起動しておきます。(説明略)<br /> <br /> virsh コマンドで Incremental モードでの Live Block Migration を行います。<br /> <pre class="prettyprint">[@src] # virsh migrate --live --verbose --copy-storage-inc vm-name qemu+ssh://dst-ip/system </pre> Fullモードとは違い、--copy-storage-inc オプションを使います。<br /> backing との差分のみをコピーするので、Fullと比較して短い時間で完了します。<br /> <br /> <b>付録</b><br /> <br /> Qemu の Live Block Migration はQemu 0.12 の頃から QMP コマンドの migrate -b が使われていましたが、新しめの Qemu(1.3 以降)では nbd と drive-mirror コマンドが使われるようになっています。参考:<a href="http://fedoraproject.org/wiki/Features/Virt_Storage_Migration">Qemu Wiki:&nbsp;Features/Virt Storage Migration</a><br /> libvirt では Migration API がバージョン3まであり、Qemu が対応している場合は、新しい方法(nbd + drive-mirror)でマイグレーションし、対応していない場合は以前の方法にフォールバックするようになっています。参考:<a href="http://libvirt.org/html/libvirt-libvirt.html#virDomainMigrate3">libvirt : virDomainMigrate3</a><br /> nbd と drive-mirror による Live Block Migration については、以下のパッチに詳しい説明があります。<br /> <a href="https://www.redhat.com/archives/libvir-list/2013-February/msg00277.html">[libvirt] [PATCH v3 00/12 ] Rework Storage Migration</a><br /> <br /> <b>参考文献</b><br /> <br /> <a href="http://wiki.qemu.org/Features/LiveBlockMigration#Live_block_copy">Qemu Wiki: Features-Old/LiveBlockMigration</a><br /> <a href="http://fedoraproject.org/wiki/Features/Virt_Storage_Migration">Qemu Wiki:&nbsp;Features/Virt Storage Migration</a><br /> <a href="http://wiki.qemu.org/Features-Done/ImageStreamingAPI">Qemu Wiki:&nbsp;Features-Done/ImageStreamingAPI</a><br /> <span style="color: #0000ee; text-decoration: underline;">[libvirt] [PATCH v3 00/12 ] Rework Storage Migration</span><br /> <a href="http://libvirt.org/html/libvirt-libvirt.html#virDomainMigrate3">libvirt : virDomainMigrate3</a>Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-3780612463125750592013-09-28T11:42:00.001+09:002013-09-28T11:45:39.755+09:00Qemu/KVM で CPU Hotplug を使う<b>はじめに</b><br /> <div> <br /></div> <div> Hotplug とはマシンを停止せずにCPU、メモリなどのデバイスを追加する技術です。CPU Hotplug を仮想環境で用いると、仮想マシンを停止することなく仮想CPUを追加し、処理能力を強化することができます。これにより、仮想マシンの無停止スケールアップを実現できます。</div> <div> Qemuはversion1.5よりCPU Hotplug機能をサポートしています。今回はQemuでのCPU Hotplugの使い方についてご紹介します。</div> <div> 検証環境はFedora19です。</div> <div> <div> <br /></div> <div> <b>Qemu のコンパイル</b><br /> <b><br /></b></div> <div> Hotplugのサポートは1.5以降です。Qemuのversionが1.5未満の場合は最新のQemuをコンパイルしましょう。</div> <pre class="prettyprint"># git clone git://git.qemu.org/qemu.git # cd qemu # ./configure --target-list=x86_64-softmmu # make # make install </pre> <div> <br /> CPU Hotplugの使い方は、複数あります。以下では、QMPを用いる方法とlibvirt(virsh, virt-manager)経由でCPU Hotplugする方法を記載します。<br /> <br /> <b>共通の前提</b><br /> <br /> CPU Hotplug機能を使うためには、あらかじめ、Qemu を起動する時のパラメータ: maxcpus を2以上にしておく必要があります。CPU Hotplug可能な数の上限は maxcpus となります。例えば、<br /> <pre class="prettyprint">qemu ... -smp 1,maxcpus=4 </pre> といった具合です。virsh では &nbsp;vcpu タグの要素が maxcpus に対応し、current属性の値が -smp X の Xに相当します。<br /> <br /></div> <div> <b>QMPでのCPU Hotplug</b><br /> <b><br /></b></div> <div> QMP(Qemu Monitor Protocol)でQemuと通信してCPU Hotplugを実施します。<br /> QMPの使い方については下記のブログがとても詳しいです。<br /> <a href="http://kashyapc.wordpress.com/2013/03/31/multiple-ways-to-access-qemu-monitor-protocol-qmp/">Multiple ways to access Qemu Monitor Protocol(QMP)</a><br /> QMPで下記のコマンドを送信します。<br /> <pre class="prettyprint">&gt; {"execute":"cpu-add", "arguments" : { "id" : 1 } } </pre> arguments の id が Hotplug 対象の 仮想CPU です。この値は、0以上、maxcpus未満の整数をしていします。<br /> あとはゲスト内でCPUをonlineにします。<br /> <pre class="prettyprint"># echo 1 &gt; /sys/devices/system/cpu/cpu1/online </pre> /proc/cpuinfoなどで、Hotplugされたことを確認しましょう。<br /> <br /></div> <div> <b>virsh での CPU Hotplug</b></div> <div> <br /> libvirt が Qemu CPU Hotplug をサポートしているのは version 1.0.6.5 からですが、Fedora19 の libvirt 1.0.5.5 ではサポートされているので、それを使います。<br /> 関連コミット:<br /> <a href="http://libvirt.org/git/?p=libvirt.git;a=commit;h=c12b2be5169298708cf727ed9ccd42e9d89a9737">qemu: Implement new QMP command for cpu hotplug</a><br /> <br /> 注意: CPU Hotplugを使うには チップセットエミュレータのversionが1.5以上でないといけません。virsh edit で<br /> <pre class="prettyprint">&lt;os&gt; &lt;type arch="x86_64" machine="<b>pc-1.2</b>"&gt;hvm&lt;/type&gt; &lt;/os&gt; </pre> を <br /> <pre class="prettyprint">&lt;os&gt; &lt;type arch="x86_64" machine="<b>pc-1.5</b>"&gt;hvm&lt;/type&gt; &lt;/os&gt; </pre> に変更してください。<br /> virsh setvcpus コマンドで仮想CPUをHotplugします。仮想マシンの名前は hotplug としています。<br /> <pre class="prettyprint"># virsh vcpucount hotplug maximum config 4 maximum live 4 current config 1 current live 1 # virsh setvcpus hotplug # virsh vcpucount hotplug maximum config 4 maximum live 4 current config 1 current live 2 </pre> あとはQMPでの場合と同様に、ゲスト内でHotplugされたCPUをonlineにするだけです。<br /> <br /></div> <div> <b>virt-manager での CPU Hotplug</b><br /> <br /> 注意: virsh での CPU Hotplug と同様に、チップセットエミュレータのversionが1.5以上であることを確認しましょう。同様に、 libvirt の version についても確認しましょう。<br /> virt-manager での CPU Hotplugは実は簡単で、下記仮想マシンの詳細管理画面で、CPUの"現在の割り当て"部分をポチポチして"適用"ボタンを押すだけです。便利だなぁ。<br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJO7V_CN1eEe_CL8fLrdHDIk0b4w_T5anRxwfuzvUp48CIC9DLT6nzZJJ068eJY8JzQQ0vtBr-tTSH2hawRqtR82RKx-2ab_snAo209iXIxc_t8S44PlGRPCN71qQsoLM_FwQxb7vl44Bx/s1600/Screenshot_from_2013-09-28+10:29:02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="253" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJO7V_CN1eEe_CL8fLrdHDIk0b4w_T5anRxwfuzvUp48CIC9DLT6nzZJJ068eJY8JzQQ0vtBr-tTSH2hawRqtR82RKx-2ab_snAo209iXIxc_t8S44PlGRPCN71qQsoLM_FwQxb7vl44Bx/s320/Screenshot_from_2013-09-28+10:29:02.png" width="320" /></a></div> 後は、Hotplug されたCPUをゲスト内でonlineにしましょう。</div> <div> <br /></div> <div> <b>Qemu guest agent との連携</b></div> <div> <br /> Hotplug されたCPUをいちいちゲスト内で online にするの、めんどくさいですね。そんなときは Qemu guest agent と連携してホストから CPU を online にしましょう。ゲストにQemu 1.5 以降の guest agent をインストールして起動したあと、ホストから "guest-set-vcpus" コマンドで guest agent 経由で CPU を online にできます。<br /> <br /> guest agent の設定の仕方については下記の記事が詳しいです。<br /> <a href="http://lost-and-found-narihiro.blogspot.jp/2013/08/fedora-19-kvm-qemu-guest-agent.html">lost and found(for me?) : Fedora 19 KVM : qemu-guest-agent</a><br /> <br /> virsh で CPU を Hotplug したあと、guest agent 経由で online にします。<br /> <pre class="prettyprint"># virsh qemu-agent-command hotplug '{"execute":"guest-get-vcpus"}' {"return":[{"online":true,"can-offline":false,"logical-id":0},{"online":true,"can-offline":true,"logical-id":1},{"online":true,"can-offline":true,"logical-id":2}]} # virsh setvcpus hotplug 2 # virsh qemu-agent-command hotplug '{"execute":"guest-get-vcpus"}' {"return":[{"online":true,"can-offline":false,"logical-id":0},{<b>"online":false</b>,"can-offline":true,"logical-id":1}]} # virsh qemu-agent-command hotplug '{"execute":"<b>guest-set-vcpus</b>", "arguments" : { "vcpus" : [{"online":true,"can-offline":false,"logical-id":0},{<b>"online":true</b>,"can-offline":true,"logical-id":1}] }}' {"return":2} [root@edge2 qemu]# virsh qemu-agent-command hotplug '{"execute":"guest-get-vcpus"}' {"return":[{"online":true,"can-offline":false,"logical-id":0},{<b>"online":true</b>,"can-offline":true,"logical-id":1}]} </pre> <br /> /proc/cpuinfoでちゃんとonlineになってることを確認したらOKです。<br /> <br /></div> <div> <b>参考文献</b><br /> <b><br /></b></div> <div> <a href="http://wiki.qemu.org/Features/CPUHotplug">Qemu Wiki : CPU Hotplug</a></div> <div> <a href="http://git.qemu.org/?p=qemu.git;a=blob_plain;f=qmp-commands.hx;hb=HEAD">Qemu : qmp-commands.hx</a></div> <div> <a href="http://git.qemu.org/?p=qemu.git;a=blob_plain;f=qga/qapi-schema.json;hb=HEAD">Qemu : qga/qapi-schema.json</a><br /> <span style="color: #0000ee; text-decoration: underline;">Multiple ways to access Qemu Monitor Protocol(QMP)</span></div> <div> <span style="color: #0000ee; text-decoration: underline;">lost and found(for me?) : Fedora 19 KVM : qemu-guest-agent</span></div> <div> <br /></div> </div> Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-87184983347383538412013-08-29T21:21:00.000+09:002013-10-29T21:45:15.912+09:00FedoraでLXCを使う<b>はじめに</b><br /> <br /> LXCはLinux上で複数の仮想的なLinuxを動作させることのできるOSレベルの仮想化技術です。一言で言えば、chroot に cgroups でのリソース管理を追加して強化したようなものです。Qemu/KVMのようなエミュレーションを行う仮想化よりもオーバヘッドが少なく、軽量です。<a href="https://devcenter.heroku.com/articles/dynos">Heroku</a> のような PaaS 業者は、LXCの利点を生かし高集約なサービスを提供しています。さらには<a href="http://joes-cloud.com/lxc.html">Joe's Web Hosting</a>のように、VPSをLXCで提供しているサービスすらあります。しかし、LXCではQemu/KVMとは違い、ホストとゲストで異なるOSを動作させることができません。仮想化による性能劣化が低い一方、柔軟性は一歩劣ると言えます。<br /> 今回は Fedora 19 で手軽に LXC を使う方法をまとめました。(Fedora18でも可能ですが、virt-manager が安定しないので、Fedora19以降がお勧めです)<br /> <br /> <b>環境構築</b><br /> <br /> LXCでコンテナを構築する方法は大きく2つ有ります。一つは <a href="https://github.com/lxc/lxc">LXC 公式ツールキット</a>を使う方法です。これは、最近はやりの<a href="https://www.docker.io/">docker</a>でも使われている方法なのですが、Fedora との相性がいまいちです。もう一つは、Qemu/KVMの管理でもおなじみのlibvirtを使う方法です。libvirtを使うと、仮想ネットワークの管理(DHCP, NAT)を含む仮想マシンの操作を、Qemu/KVMと同じ感覚で扱えるので、Qemu/KVMに慣れた方にはとてもおすすめです。今回はlibvirtでLXCを扱う方法について説明します。<br /> <br /> <u>root 環境のインストール:</u><br /> yum で installroot を指定することで、init を実行する rootfs を構築できます。debootstrap と同じようなものです。<br /> <pre class="prettyprint"># 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 </pre> <br /> LXCは標準で/dev/pts/0 を使うので、securettyに追記して root でログインできるようにしておきます。<br /> <pre class="prettyprint"># echo "pts/0" &gt;&gt; /home/eiichi/lxc/base/etc/securetty </pre> <br /> <u>libvirtで使う:</u><br /> virt-manager 経由で使うのが便利です。<br /> ホストマシンに接続した後、新規仮想マシンの作成ボタンを押し、"コンテナーの種類"で"オペレーティングシステムコンテナ"を選択します。"既存のOSルートディレクトリを指定してください"ダイアログで、root環境を指定します。上記の例では、"/home/eiichi/lxc/base"となります(画像参照)。<br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkuYiBKM528ASIBl-s-disXazyytOpbQZPi8qyCC1T7TDo0FO2yxpKjUithMOSTWEcL18N2Vm1_OOHcPj35f_ovBTlB_mTuCyt5eD-XbH3OqCXxBeBJCD-f8D75KGQ3GXCAVhsVr-2wuMI/s1600/Screenshot_from_2013-08-29+20:39:16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="231" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkuYiBKM528ASIBl-s-disXazyytOpbQZPi8qyCC1T7TDo0FO2yxpKjUithMOSTWEcL18N2Vm1_OOHcPj35f_ovBTlB_mTuCyt5eD-XbH3OqCXxBeBJCD-f8D75KGQ3GXCAVhsVr-2wuMI/s320/Screenshot_from_2013-08-29+20:39:16.png" width="320" /></a></div> <br /> あとは流れに沿って進めば、コンテナが起動します。<br /> <br /> <u>ネットワーク設定:</u><br /> 無事ログインできたら、ネットワーク設定を確認します。下記コマンドで veth ネットワークデバイスがみえるはずです。 <br /> <pre class="prettyprint">-bash-4.2# ip -d l 1: lo: <loopback> 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: <broadcast> 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 </broadcast></loopback></pre> さらに、libvirtのdefaultネットワークを用いているのであれば、dhcpでipをもらえるはずです。<br /> <pre class="prettyprint"># dhclient eth0 </pre> これで、NATで外部に接続することが出来るようになります。<br /> 全て完了しました。とても簡単です。<br /> <br /> <b>おまけ: btrfs での運用</b><br /> 上記では一つのコンテナを作る際の手順について説明しましたが、複数のコンテナを作る場合はどうでしょう。rootfs をコピーして使うのでは、各コンテナでrootfsの内容がほとんど変わらないのでディスク容量の無駄です。こんな時は、COW(Copy on Write)なbtrfsのsnapshotを使って無駄を省きましょう。内容としては、dockerがUnion FS(AUFS)を用いてやっていることとほぼ同等です。<br /> <br /> <u>btrfsの準備:</u><br /> 普段btrfsを使っていない場合は、loopback device で btrfs を利用する準備をしましょう。<br /> <pre class="prettyprint"># 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 </pre> これで./mnt で btrfs が使えるようになりました。base となるコンテナとして、subvolumeを切り、そこに rootfs を構築します。<br /> <pre class="prettyprint"># 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 </pre> コンテナを新しく作る際には、base subvolume の snapshot を作成して構築します。 <br /> <pre class="prettyprint"># btrfs subvolume snapshot base f19-1 </pre> あとは、作ったsnapshot directoryをvirt-managerでOSコンテナとして登録すればO.K.です。<br /> <br /> <b>まとめ</b><br /> Fedoraにおいて、libvirtを使ってLXCを使う方法について説明しました。LXCは軽量な仮想化技術であり、とても簡単に管理できます。また、btrfs の snapshot と組み合わせることで、非常に効率的な運用が可能になります。<br /> <br /> <b>参考文献</b><br /> <a href="https://www.berrange.com/posts/2013/08/12/running-a-full-fedora-os-inside-a-libvirt-lxc-guest/">Daniel P. Berrangé:&nbsp;Running a full Fedora OS inside a libvirt LXC guest</a><br /> <a href="http://blog.vmsplice.net/2012/09/thoughts-on-linux-containers-lxc.html">Stefan Hajnoczi: Thoughts on Linux Containers (LXC)</a><br /> <a href="https://btrfs.wiki.kernel.org/index.php/Btrfs(command)">btrfs wiki: btrfs(command)</a><br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-2001920286607403772013-08-03T09:21:00.000+09:002013-11-05T19:13:41.949+09:00SystemTap 埋め込みC関数のAPI変更について<b>はじめに</b><br /> <br /> SystemTapはスクリプト内にC言語の関数を埋め込む機能を備えています。カーネル内の変数について詳しく調査したり、変数の内容を変更したりする際に埋め込みC関数がとても便利です。SystemTap 1.8 で埋め込みC関数内でのローカル変数アクセス方法が変更になりましたので、まとめておきます。さらに詳しい情報はSystemTap の <a href="https://github.com/fche/systemtap/blob/master/NEWS">NEWS</a>&nbsp;に記載されています。<br /> <br /> <b>従来API</b>(1.7以前)<br /> <br /> 従来、ローカル変数にアクセスする際、"THIS-&gt;var" 、"THIS-&gt;__retvalue" を用いていました。例えば以下のような感じです。<br /> <pre class="prettyprint">function add_one:long (val:long) %{ THIS-&gt;__retvalue = THIS-&gt;val + 1; %} </pre> <br /> &nbsp;<b>新API</b>(1.8以後)<br /> <br /> 新APIでは "THIS-&gt;var" ,"THIS-&gt;__retvalue" の代わりにマクロ "STAP_ARG_var", "STAP_RETVALUE" を用います。<br /> <pre class="prettyprint">function add_one:long (val:long) %{ STAP_RETVALUE = STAP_ARG_val + 1; %}</pre> APIが変更された理由は、tapset によりインクルードされたヘッダとの変数名の衝突を防ぐためです。詳しくは<a href="http://sourceware.org/bugzilla/show_bug.cgi?id=10299">Sources Bugzilla – Bug 10299</a>をご覧ください。<br /> <br /> <b>移行方法</b><br /> <br /> 1.7以前のAPIで書かれたstpスクリプトを1.8以後のSystemTapで実行すると、下記のようなエラーが起きるため、SystemTap のバージョンを1.8以後に移行する際には、なんらかの対処が必要になります。<br /> <pre class="prettyprint">/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] </pre> <br /> <u>移行方法1.&nbsp;&nbsp;--compatible=1.7 オプションの利用</u><br /> <br /> systemtap を実行する際に、--compatible=1.7 オプションをつけることで、スクリプトを変更せずに済みます。<br /> <u><br /></u> <u>移行方法2. /* unmangled */ pragma の利用</u><br /> <br /> systemtap スクリプトの埋め込みC関数に /* unmangled */ プラグマを付与することで、従来APIと新APIを混在させることができます。<br /> <pre class="prettyprint">function add_one:long (val:long) %{ /* unmangled */ THIS-&gt;__retvalue = THIS-&gt;val + 1; %} </pre> <br /> <b>余談</b><br /> <br /> この件、実は<a href="https://twitter.com/Etsukata/status/362940501860691970">Twitter上で埋め込みC関数APIの変更を嘆いていた</a>時に、SystemTap 主要開発者の Frank Ch. Eigler さん(<a href="https://twitter.com/fche">@fche</a>)から<a href="https://twitter.com/fche/status/362955332118519809">教えていただきました</a>。 Frank さん、どうもありがとうございました。<br /> <br /> <b>参考文献</b><br /> <br /> <a href="http://sourceware.org/systemtap/langref/Components_SystemTap_script.html#SECTION00045000000000000000">SystemTap Language Reference: 3.5 Embedded C</a><br /> <a href="https://github.com/fche/systemtap/blob/master/NEWS">systemtap/NEWS</a><br /> <br /> <br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-24504536364189622502013-07-21T18:05:00.005+09:002013-07-21T18:06:01.651+09:00VirtFS で Qemu ゲストホスト間ファイル共有<b>はじめに</b><br /> <br /> Qemu/KVM 環境において、ホストゲスト間でのファイル共有ができると、とても便利です。例えば、開発中の Linux Kernel をテストする時には、ホストのコンパイル済み Kernel ソースディレクトリをゲストでマウントし、Kernel のインストールができると捗ります。ファイル共有方法には NFS、CIFS、SSHFS などがありますが、Qemu にはより効率的な "VirtFS" という仕組みがあります。<br /> VirtFS は、ゲストの Linux マシンと virtio-9p デバイスを通じてファイル共有する仕組みです。ゲストホスト間で共有するリングバッファへの読み書きでデータをやり取りするため、他のネットワークファイルシステムなどより効率が良いのです。<br /> 今回は virt-manager での VirtFS を使ったファイル共有設定方法についてご紹介します。<br /> Fedora 18で検証しています。(Fedora 15以上であれば Qemu が対応しています。)<br /> <br /> <b>virt-manager でのホストゲスト間ファイル共有設定</b><br /> <br /> 仮想マシン詳細を開き、「ハードウェアを追加」で 「FileSystem」を選択します。<br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsrT0DCcDvyE2w_eUEPgJ9cQD6DnyHNDdOSFECDcbkWTgrlKKW0g5Jd99Xu3hp-kbl-4ua3UM1xiKRKwL-1v8zNv4YV1sWwTHc1I6bEARyrWqrVp7_3q6EXs4lJMgAWZHzFzRvsvmriB4V/s1600/Screenshot_from_2013-07-21+17:12:46.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsrT0DCcDvyE2w_eUEPgJ9cQD6DnyHNDdOSFECDcbkWTgrlKKW0g5Jd99Xu3hp-kbl-4ua3UM1xiKRKwL-1v8zNv4YV1sWwTHc1I6bEARyrWqrVp7_3q6EXs4lJMgAWZHzFzRvsvmriB4V/s320/Screenshot_from_2013-07-21+17:12:46.png" width="320" /></a></div> <br /> ファイルシステム・パススルーの各項目を設定していきます。<br /> 上記の設定のように設定し、ゲストを起動しましょう。<br /> <br /> 「ターゲットパス」に指定したワードが、ゲスト上で 9pfs をマウントする際のマウントタグになります。<br /> <br /> <u>ゲストでのマウント</u><br /> マウントタグの確認<br /> <pre class="prettyprint"># cat /sys/bus/virtio/drivers/9pnet_virtio/virtio&lt;n&gt;/mount_tag source_tag</pre> マウント<br /> <pre class="prettyprint"># mkdir source # mount -t 9p -o trans=virtio source_tag ./source/</pre> これでゲストから /path/to/source_dir がみえるようになりました。<br /> <br /> <b>注意</b><br /> <br /> 上記の設定では、ゲストからの共有ディレクトリへの書き込みが Permisson Denied になってしまいます。これは、qemu 起動ユーザが共有ファイルへのアクセス権を持っていない時に生じます。書き込みできるようにするためには、qemu を root で起動する必要があります。<br /> <br /> <u>qemu を root で起動するための libvirt 設定</u><br /> /etc/libvirtd/qemu.conf<br /> <pre class="prettyprint">user = "root" group = "root" </pre> をコメントアウトし、libvirtd を再起動。<br /> <br /> <b>参考文献</b><br /> <br /> <a href="https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/filesystems/9p.txt">Documentation/filesystems/9p.txt</a><br /> <a href="http://libvirt.org/formatdomain.html#elementsFilesystems">libvirt: Domain XML format</a><br /> <a href="http://wiki.qemu.org/Documentation/9psetup">Qemu Wiki : 9psetup</a><br /> <a href="http://www.linuxplumbersconf.org/2010/ocw/system/presentations/597/original/VirtFS_LPC_2010_Final.pdf">VirtFS LPC 2010</a><br /> <a href="http://d.hatena.ne.jp/kvm/20110518/1305719554">KVM日記 : Rootfs over Virtfsでゲストを起動する</a><br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-20536931123672207812013-07-18T18:42:00.000+09:002013-07-18T19:50:11.956+09:00virsh で 仮想マシンのスナップショットを取る<b>はじめに</b><br /> <br /> 仮想マシン上で頻繁に環境構築・破壊を繰り返す場合、仮想マシンのスナップショットを利用し、素早くディスク状態をもとに戻せると便利です。libvirt, Qemu/KVM は仮想マシンのスナップショット機能を実装しており、とても有用です。今回は virsh コマンドでのスナップショットの扱い方をご紹介します。検証環境は Fedora 18です。<br /> <br /> <b>スナップショットの種類</b><br /> <br /> libvirt, Qemu が実装している仮想マシンスナップショットの種類には、以下の2種類がありあます。<br /> <br /> - 1. 内部スナップショット<br /> - 2. 外部スナップショット<br /> <br /> <u>1. 内部スナップショット</u>は仮想マシンのスナップショットを一つの qcow2 ファイルで管理する方式です。スナップショット取得中は仮想マシンは一時停止状態になります。仮想マシンのディスクのスナップショットのみならず、RAM 状態やデバイス状態などの仮想マシン状態も保存できます。<br /> <br /> <u>2. 外部スナップショット</u>は仮想マシンのスナップショットを外部の qcow2 ファイルで管理します。なんと、<u>仮想マシンを停止することなくスナップショットを取得できます</u>。仮想マシンディスク以外の仮想マシン状態を保存することは、今のところできません。また、今のところ、仮想マシン停止中にはスナップショットを取ることができません。<br /> 現状動作が安定しておらず、非常に実験的な機能です。<br /> <br /> 以下、仮想マシンの名前を vm1 として、virsh コマンドの使い方を説明します。<br /> <br /> <b>内部スナップショット</b><br /> <br /> <u>内部スナップショットの作成</u><br /> <pre class="prettyprint"># virsh snapshot-create-as vm1 snap1 "snap1 description" ドメインのスナップショット snap1 が作成されました </pre> 内部スナップショットは仮想マシン稼働中でもスナップショットを作成できます(ただし、安定していません)。作成している間は、仮想マシンは一時停止状態になります。ストレージ性能や仮想ディスク容量にもよりますが、作成時間は数分かかります。<br /> <br /> <u>内部スナップショット確認</u><br /> <pre class="prettyprint"># virsh snapshot-list vm1 名前 作成時間 状態 ------------------------------------------------------------ snap1 2013-07-18 16:43:11 +0900 running </pre> <br /> <u>内部スナップショット復元</u><br /> <pre class="prettyprint"># virsh snapshot-revert vm1 snap1 </pre> スナップショットの復元についても、仮想マシン稼働中に実行可能です。ただし、復元中、仮想マシンは一時停止状態になります。<br /> <br /> <u>内部スナップショット情報の取得</u><br /> 指定のスナップショット情報を取得する際のコマンドは以下です。 <br /> <pre class="prettyprint"># virsh snapshot-info vm1 snap1 名前: snap1 ドメイン: vm1 カレント: はい (yes) 状態: running 親: - 子: 0 子孫: 0 メタデータ: はい (yes) </pre> <br /> スナップショット復元後は下記コマンドで現時点でどのスナップショットを使用しているか確認できます。<br /> <pre class="prettyprint"># virsh snapshot-info vm1 --current 名前: snap1 ドメイン: vm1 カレント: はい (yes) 状態: running 親: - 子: 0 子孫: 0 メタデータ: はい (yes) </pre> <br /> <u>スナップショット XML ファイルのダンプ</u><br /> 仮想マシンに関する設定情報(XML ファイル)を含んでいます。下記コマンドで設定情報を出力できます。<br /> <pre class="prettyprint"># virsh snapshot-dumpxml vm1 snap1 </pre> <br /> <u>スナップショットの削除</u><br /> <pre class="prettyprint"># virsh snapshot-delete vm1 snap1 </pre> <br /> <br /> <b>外部スナップショット</b><br /> <br /> <u>外部スナップショット作成</u><br /> <pre class="prettyprint"># virsh snapshot-create-as vm1 disksnap1 "disksnap1 description" --disk-only --atomic ドメインのスナップショット disksnap1 が作成されました </pre> 外部スナップショットは仮想マシン実行中のみ取得可能です。内部スナップショットとは異なり、仮想マシンを停止(一時停止)することなく取得可能(Live Snapshot)です。つまり、仮想マシン無停止での Live Backup が可能です。<br /> 外部スナップショット作成後はディスクスナップショットイメージが作成され、current snapshot が作成したスナップショットになります。<br /> <pre class="prettyprint"># virsh snapshot-info vm1 --current 名前: disksnap1 ドメイン: vm1 カレント: はい (yes) 状態: disk-snapshot 親: - 子: 0 子孫: 0 メタデータ: はい (yes) </pre> <br /> <u>外部スナップショット確認</u><br /> <pre class="prettyprint"># virsh snapshot-list vm1 名前 作成時間 状態 ------------------------------------------------------------ disksnap1 2013-07-18 17:39:44 +0900 disk-snapshot snap1 2013-07-18 16:43:11 +0900 running </pre> <br /> 外部スナップショットが作成されると、仮想マシンイメージファイルを格納してあるディレクトリ(デフォルトでは /var/lib/libvirt/images)にスナップショットファイル(vm1.disksnap1)が新たに作成されます。<br /> 仮想マシンは新たに作成されたスナップショットファイルを使用するようになります。<br /> <pre class="prettyprint"># virsh domblklist vm1 ターゲット ソース ------------------------------------------------ vda /home/eiichi/vmimg/vm1.disksnap1 hdc - </pre> <br /> <u>外部スナップショット復元</u><br /> 外部スナップショットの復元は、virsh edit で仮想マシン設定 XML ファイルを開き、disk タグの source タグのfile 属性を復元したいディスクスナップショットに指定します。現状では ディスクスナップショットへの snapshot-revert は対応していないようです。<br /> <pre class="prettyprint"># virsh snapshot-revert vm1 disksnap2 エラー: サポートされない設定: 外部ディスクスナップショットへの復元はまだサポートされていません </pre> <br /> <b>注意</b><br /> 外部スナップショットはまだまだ開発段階の機能です。無停止でスナップショットが取れますが、動作が安定しないのが難点です。また、内部スナップショット機能についても、仮想マシン起動中のスナップショット取得はやはり安定して動作しないことがあります。<br /> 安定した動作を希望する場合、一番安全な、"仮想マシン停止時" &nbsp;の "内部スナップショット" をおすすめします。<br /> <br /> <b>参考文献</b><br /> <a href="http://fedoraproject.org/wiki/Features/Virt_Live_Snapshots">fedoraproject :&nbsp;Features/Virt Live Snapshots</a><br /> <a href="http://wiki.qemu.org/Features/Snapshots">QEMU wiki : Features/Snapshot</a><br /> <a href="http://libvirt.org/formatsnapshot.html">libvirt : Snapshot XML Format</a><br /> <a href="http://kashyapc.fedorapeople.org/virt/lc-2012/snapshots-handout.html">kashyapc fedorapeople : snapshot handout</a><br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-59674027801639267432013-07-16T20:58:00.000+09:002013-07-16T20:58:40.149+09:00Linux で VXLAN を使う<b>はじめに</b><br /> <b><br /></b> VXLAN は VMware、Cisco、Redhat などが推進している VLAN に替わるネットワーク論理分割のための規格です。従来、IaaSなどのクラウド環境において、マルチテナントを実現するためには 802.1Q VLAN を用いるのが一般的な解決策でしたが、この VLAN には VLAN ID が 12bit しかないため、最大 4096 セグメントの分離しかできない、という問題があります。<br /> VXLAN はこの問題を解決します。VLAN ID に対応する VNI(VXLAN Network Identifier) に 24bit を設け、&nbsp;<u>1,677万セグメントの論理分割を実現</u>します。<br /> <br /> VXLAN 類似の技術には Microsoft、Intel、Dell などが推進している&nbsp;NVGRE(Network Virtualization using Generic Routing Encapsulation) があります。実装の進み具合で判断すると、やはり VXLAN のほうが勢いがあるため、今後<u> L2 over L3 を実現するネットワーク論理分割の主流は VXLAN になる</u>、と個人的には思っています。<br /> <br /> 今回はこの VXLAN の Linux での使い方をご紹介します。<br /> <br /> <b>環境</b><br /> <b><br /></b> Qemu/KVM を利用した仮想環境で実施しています。<br /> 下記のような簡単な環境です。<br /> <pre>|VM A(192.168.10.2/24)| --- | vbr | --- |VM B(192.168.10.3/24)| </pre> VM A、VM B の二台が物理マシン上に作った仮想ブリッジに接続されています。<br /> VM A、VM B の間に VXLAN で 仮想ネットワークを構築します。<br /> <br /> <b>使い方</b><br /> <br /> VXLAN の実装は<a href="https://github.com/upa/vxlan">ユーザ空間版</a>もあるのですが、ここでは Linux Kernel での実装を使います。前準備として、Linux Kernel のバージョンが 3.7 以上である必要があります。<br /> 関連コミット:<br /> <a href="http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d342894c5d2f8c7df194c793ec4059656e09ca31">vxlan: virtual extensible lan</a><br /> <br /> iproute2 コマンドスイートのバージョンが低いと、VXLAN がサポートされていません。その場合は最新版をソースからコンパイルして入れましょう。VM A および VM B で実施します。<br /> <pre class="prettyprint"># 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 } </pre> TYPE に vxlan が含まれていれば、OKです。<br /> もし、iproute2 のコンパイル時に "db_185.h がない" というエラーがでた場合は libdb-devel をインストルしましょう。<br /> <pre class="prettyprint"># yum install libdb-devel</pre> <br /> さて、VXLAN を張る作業に入ります。<br /> VM A、 B 上で下記のコマンドを入力します。<br /> <pre class="prettyprint"># ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev eth0 </pre> VXLAN は多くのトンネリング技術とことなり、<u>1対Nでのトンネリング</u>を行います。そのため、マルチキャストアドレスを指定します。<br /> <pre class="prettyprint"># ip -d link show vxlan0 4: vxlan0: <broadcast> 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) </broadcast></pre> ip maddr で 239.1.1.1 が表示されていれば、適切にアドレス設定できていると確認できます。<br /> <br /> VXLAN デバイスに アドレスを振って、疎通確認をします。<br /> On VM A<br /> <pre class="prettyprint">ip a add 192.168.42.2/24 dev vxlan0 </pre> <br /> On VM B<br /> <pre class="prettyprint">ip a add 192.168.42.3/24 dev vxlan0 </pre> <br /> 疎通確認<br /> On VM A<br /> <pre class="prettyprint">ping 192.168.42.3 </pre> あなたの予想に反せずに pong が返っているでしょうか。pong が返らないようであれば、VM A,B および物理マシンの firewall 設定を確認してみてください。<br /> <br /> 下記に ping を送った際の wireshark 通信ダンプ結果を載せておきます。<br /> <div class="separator" style="clear: both; text-align: center;"> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOHBqpQa30dWKjn2pXafHBqvdQhGO3xfAGPQrzIWhgE67J5AUrbW1_GB08YnnaeavUZZV1k1Y4PGGl5WO8L2uaxsnvOJJlRBmqzhIV61jq93Zs2sx-ML0F-SebNvnMIt8MDbkXgpXkuav1/s1600/vxlan.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOHBqpQa30dWKjn2pXafHBqvdQhGO3xfAGPQrzIWhgE67J5AUrbW1_GB08YnnaeavUZZV1k1Y4PGGl5WO8L2uaxsnvOJJlRBmqzhIV61jq93Zs2sx-ML0F-SebNvnMIt8MDbkXgpXkuav1/s320/vxlan.png" width="320" /></a></div> <br /> 最初に UDP Multicast で 239.1.1.1 に送信され、VM A の VXLAN トンネル終端がMAC アドレス学習後は Unicast で通信していることがわかります。<br /> <br /> fdb は下記コマンドで確認できます。<br /> On VM A<br /> <pre class="prettyprint"># bridge fdb show dev vxlan0 9e:03:cd:ab:b2:91 dst 192.168.10.3 self</pre> <br /> <b>参考文献</b><br /> <b><br /></b> <a href="http://lwn.net/Articles/518375/">Documentation/networking/vxlan.txt</a><br /> <a href="http://datatracker.ietf.org/doc/draft-mahalingam-dutt-dcops-vxlan/">VXLAN: A Framework for Overlaying Virtualized Layer 2 Networks over Layer 3 Networks&nbsp;draft-mahalingam-dutt-dcops-vxlan-04</a><br /> <a href="http://www.ipa.go.jp/security/fy23/reports/tech1-tg/b_06.html">IPA :&nbsp;VXLAN/NVGREによるネットワーク分離</a>Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-78886264489554429172013-07-15T00:11:00.000+09:002013-07-16T20:58:52.237+09:00iproute2 コマンドでルーティングテーブル/アドレス設定の保存/復元<b>はじめに</b><br /> <br /> iproute2 はLinuxでネットワーク関係の設定を変更するためのコマンドスイートです。もはや非推奨となった ifconfig の代替として利用が推奨されており、ifconfig コマンドではでは行えない設定も可能です。iproute2 コマンドスイートの中には、ルーティングやアドレス設定を行う ip、トラフィック制御を行う tc、ネットワーク統計情報を取得する lnstat, ifstat コマンドなどが含まれます。iproute2 には非常に多くの機能が含まれているのですが、linux-net に併せて開発がとても早く、ドキュメントの整備が追いついていないのが難点です。<br /> <br /> 今回は ip コマンドを用いてルーティングテーブルおよび、アドレス設定の保存/復元を行う方法についてご紹介します。仮想ネットワークの構築を行うとき等に、ネットワーク設定を素早く手軽に復元できるこれらの機能が有用です。<br /> <br /> <b>設定の保存</b><br /> <br /> 設定のルーティングテーブル/アドレス設定の保存はそれぞれ ip route/ ip addr コマンドにより行います。<br /> 設定はバイナリ形式です。<br /> ルーティングテーブルの保存:<br /> <pre class="prettyprint">% ip route save &gt; iproute.conf.bin </pre> <br /> アドレス設定の保存:<br /> <pre class="prettyprint">% ip addr save &gt; ipaddr.conf.bin </pre> <br /> <b>設定の確認</b><br /> <br /> 設定はバイナリ形式なので、人手での確認は showdump オプションを使います。<br /> <pre class="prettyprint">% ip addr showdump &lt; 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 &lt; 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 </pre> <br /> <b>設定の復元</b><br /> <br /> restoreコマンドで復元します。<br /> <pre class="prettyprint">% ip addr restore &lt; ipaddr.conf.bin % ip route restore &lt; iproute.conf.bin </pre> <br /> <b>余談</b><br /> <br /> iproute2 はドキュメント整備が不足していると述べましたが、これはかなりのつらみを感じます。今回もコマンドの使い方を学ぶためにソースコードとコミットログを参照しました。<br /> コミットログによると、今回紹介した ip addr save/restore や ip route save/restore はcheckpoint-restartでも用いられているようです。<br /> <a href="http://git.kernel.org/cgit/linux/kernel/git/shemminger/iproute2.git/commit/?id=f4ff11e3e298d457c4f30813e305f7d27d89823a">Add ip route save/restore</a><br /> <a href="http://git.kernel.org/cgit/linux/kernel/git/shemminger/iproute2.git/commit/?id=81824ac2285d4d82c74b6293bb23027ef5a3ec67">iproute: Add ability to save, restore and show the interfaces' addresses (resend)</a><br /> <br /> <b>参考文献</b><br /> <br /> <a href="http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2">iproute2 - official</a><br /> <a href="http://git.kernel.org/cgit/linux/kernel/git/shemminger/iproute2.git">kernel/git/shemminger/iproute2.git</a><br /> git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git<br /> <br />Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-26763391002980975312013-06-23T11:43:00.001+09:002013-06-23T11:50:28.578+09:00OSSの開発活動を可視化するWebサービスをつくりましたLinux Foundation の報告書 "<a href="http://www.linuxfoundation.org/sites/main/files/publications/whowriteslinux.pdf">Linux Kernel Development: How Fast it is Going, Who is Doing It, What They are Doing, and Who is Sponsoring It</a>" には Linux Kernle 開発の統計情報が数多く含まれており、開発の規模やスピード感が読み取れます。こういった統計情報は Linux Kernel のみならず、他の多くの OSS 開発プロジェクトにおいても有用です。今回作成した Web サービス "<a href="http://etsukata.com/ods/#/">OSS Development Statistics</a>" は git レポジトリのログから、月ごとのコミット数やディベロッパ数、コミット数ランキング、ホットな開発キーワードを表示します。<br /> <div> <br /></div> <div> <a href="http://etsukata.com/ods/#/">OSS Development Statistics</a><br /> <div class="separator" style="clear: both; text-align: center;"> <img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1AKj5Zhuy9QKOBicZHVNIr8qb7Mc5-aHT4-KN95BcTUy4X3-fu_RMWZ-Kqb79kjiRQuPxr934pMnRuXEfgPpapsT0P4ctXk2sJXOIRX58lyrkZCZXQXg1SuwwXK8GRVhE9Rj17MkWzeS0/s320/Screenshot_from_2013-06-23+11:14:12.png" width="320" /></div> <div> <br /></div> <div> <br /></div> </div> <div> 現在登録してあるプロジェクトは、自分が独断で選んだ以下のものです。要望があれば他にも追加いたします。<a href="https://twitter.com/Etsukata">@Etsukata</a>までお気軽にどうぞ。git レポジトリであれば簡単に追加できます。</div> <div> <ul> <li>libvirt&nbsp;</li> <li>linux&nbsp;</li> <li>mongo&nbsp;</li> <li>node&nbsp;</li> <li>openstack_nova</li> <li>openstack_quantum&nbsp;</li> <li>openstack_swift&nbsp;</li> <li>qemu&nbsp;</li> <li>riak</li> </ul> </div> <div> 現在、一日に一回 git pull してデータベースを更新しています。</div> <div> <br /></div> <div> サービスの作成にあたっては、<b>MEAN Stack</b> を使用しました。</div> <div> MEAN Stack とは M : <a href="http://www.mongodb.org/">Mongodb</a>, E : <a href="http://expressjs.com/">Express</a>, A : <a href="http://angularjs.org/">Angular JS</a>, N : <a href="http://nodejs.org/">Node.js</a> を指し示します。クライアントサイド、サーバサイドともにJavaScriptで記述できる生産性の高いフレームワーク群です。MEAN Stackについては、mongodb blog に寄稿されている記事:&nbsp;<a href="http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and">The MEAN Stack: MongoDB, ExpressJS, AngularJS and Node.js</a> が参考になります。</div> <div> <br /></div> <div> 類似のことをするソフトウェアとしては、<a href="http://lwn.net/">LWN.net</a> の Jonathan Corbet 氏が主に開発している <a href="http://repo.or.cz/w/git-dm.git">git-dm</a> があります。Corbet 氏は 新しい Linux Kernel がリリースされる度に開発の統計情報をまとめた記事を投稿しますが、git-dmはその記事を書くために用いられているようです。</div> <div> Linux Kernel 3.9 リリース時の記事:</div> <div> <a href="http://lwn.net/Articles/547073/">Statistics from the 3.9 development cycle</a></div> <div> <br /></div> <div> 今回本サービスを使ってみて、OpenStack-Nova の開発規模が QEMU を上回っていることに驚きました。OpenStack 全体では Linux Kernel の開発規模に迫りつつあるかもしれません。</div> <div> 今後は OSS プロジェクト相互の開発者の乗り入れ状況や、プロジェクト同士の開発規模、開発加速度の比較をしたいと考えています。</div> <div> <br /></div> <div> <b>参考情報</b></div> <div> <a href="http://lwn.net/Articles/507986/">Who wrote 3.5[LWN.net]</a></div> <div> <a href="http://lwn.net/Articles/507986/">Linux カーネル開発 その開発スピード、開発者、開発過程 および支援企業</a></div> Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-84851256462978121412013-05-16T21:41:00.001+09:002013-05-16T21:41:36.727+09:00CVE-2013-2094(perfバグによるLinux権限昇格の脆弱性)まとめNIST により2013/5/14 に、 RHEL6.1 - 6.4 をはじめとする Linux ディストリビューションに、perf のバグをついて権限昇格される脆弱性があることがアナウンスされました。<br /> <a href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-2094">Vulnerability Summary for CVE-2013-2094</a><br /> <br /> 影響範囲が大きいと思いますので、情報をまとめておきました。<br /> <br /> <a href="http://dl.packetstormsecurity.net/1305-exploits/semtex.c">Exploit Code:semtex.c</a><br /> 手元のCentOS 6.4 で試したところ、rootを取ることが出きました。<br /> <br /> <a href="https://access.redhat.com/site/solutions/373743">Does CVE-2013-2094 affect Red Hat Enterprise Linux and Red Hat Enterprise MRG?</a><br /> 解決策として、Systemtap スクリプトを guru モードで動作させ、動的にパッチを当てる手法が掲載されています。<br /> <br /> <a href="https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2013-2094">RedHat Bugzilla CVE-2013-2094</a><br /> 書き込み「Petr Matousek 2013-05-14 19:36:43 EDT」において、上記のexploit:semtex.cでroot権限昇格についての詳しい説明があります。ありがたや。<br /> <br /> <a href="http://www.itmedia.co.jp/enterprise/articles/1305/16/news030.html">IT media「Linuxに権限昇格の脆弱性、エクスプロイトも出まわる」</a><br /> <br /> <a href="http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b0a873ebbf87bf38bf70b5e39a7cadc96099fa13">原因となったコミット</a><br /> <br /> <a href="http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8176cced706b5e5d15887584150764894e94e02f">修正コミット</a>Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0tag:blogger.com,1999:blog-9025985108752510661.post-37702803751117462942013-05-09T00:07:00.000+09:002013-05-09T00:12:18.973+09:00Qemu のトレース新機能 "ftrace backend" 紹介<b>はじめに</b> <br /> <b><br /></b> Qemu のトレース新機能 "ftace backend" は Linux 標準のトレース機構 ftrace を使って Qemu と Linux Kernel(KVM) のトレース情報を併せて取得する機能です。Qemu で KVM を使う場合は、ユーザ空間(Qemu)とカーネル空間(Kernel)を頻繁に遷移するため、両空間のトレース情報を併せて取得できると、デバッグや性能解析がよりはかどります。<br /> <br /> ftrace backend の実装には ftrace marker が用いられています。ftrace marker は debugfs の marker file への書き込みを ftrace のリングバッファに送る機能です。Qemu ftrace backend は、Qemu のトレース情報出力先を marker file にすることで実現しています。<br /> <br /> 関連コミット:<br /> <a href="http://git.qemu.org/?p=qemu.git;a=commit;h=781e9545dbe54d2f0a530edbf48fd0ac1e77244c">trace: Add ftrace tracing backend</a><br /> <br /> 2013年5月3日に Qemu Mainline にマージされました。おそらく Qemu 1.5 で使用できるはずです。Author の名前が自分と酷似していますが。。。<br /> <br /> <b>使い方</b><br /> <br /> ftrace backend を使うためには、まず configure 時に trace backend として "ftrace" を指定する必要があります。<br /> <pre class="prettyprint"># ./configure --trace-backend=ftrace </pre> <br /> このままでも ftrace backend はトレース情報を debugfs の trace file に記録できますが、今回は KVM を使うのでKVM 関係のトレースイベント情報も併せて取得するよう設定します。 <br /> <pre class="prettyprint"># echo 1 &gt; /sys/kernel/debug/tracing/events/kvm/enable </pre> <br /> Qemu 起動時に、取得対象の Qemu trace イベントを指定します。<br /> Qemu のトレースイベント一覧は Qemu ソースコードの trace-events ファイルに記載されています。また、 Qemu monitor から "info trace-events" コマンドによっても取得できます。<br /> ここでは、すべてのイベントを取得するような設定にします。<br /> <pre class="prettyprint">% cat /home/eiichi/events * </pre> Qemu を起動します。ftrace を使うため、必ず root 権限で起動しましょう。 <br /> <pre class="prettyprint"># ./qemu-system-x86_64 -enable-kvm -trace events=/home/eiichi/events </pre> <br /> 得られるトレース出力は以下のようになります。 <br /> <pre class="prettyprint"> # less /sys/kernel/debug/tracing/trace snip... qemu-system-x86-23226 [002] d... 116142.685922: kvm_entry: vcpu 0 qemu-system-x86-23226 [002] d... 116142.685923: kvm_exit: reason IO_INSTRUCTION rip 0xc45b info 700040 0 qemu-system-x86-23226 [002] .... 116142.685924: kvm_pio: pio_write at 0x70 size 1 count 1 qemu-system-x86-23226 [002] .... 116142.685925: kvm_userspace_exit: reason KVM_EXIT_IO (2) qemu-system-x86-23226 [002] ...1 116142.685943: tracing_mark_write: cpu_set_apic_base 00000000fee00900 qemu-system-x86-23226 [002] ...1 116142.685946: tracing_mark_write: kvm_run_exit cpu_index 0, reason 2 qemu-system-x86-23226 [002] ...1 116142.685947: tracing_mark_write: cpu_out addr 0x70 value 143 qemu-system-x86-23226 [002] ...1 116142.685951: tracing_mark_write: kvm_vcpu_ioctl cpu_index 0, type 44672, arg (nil) qemu-system-x86-23226 [002] d... 116142.685954: kvm_entry: vcpu 0 snip... </pre> tracing_mark_write と書かれているのが、Qemu(ユーザ空間)のトレース情報です。ここでは、ゲストCPUのIO port write命令を受けて VM_EXIT(reason KVM_EXIT_IO) したのちユーザ空間でエミュレーションが行われているのがわかります。 <br /> <br /> <b>libvirt で使う場合</b><br /> <br /> libvirt で ftrace backend を使う場合は、libvirtd の設定ファイルを以下のように変更し、Qemu を起動するユーザを root にする必要があります。<br /> <u>/etc/libvirt/qemu.conf&nbsp;</u>に以下を追加:<br /> <pre class="prettyprint">user = "root" </pre> <br /> &nbsp;<b>余談</b><br /> <br /> ftrace marker を使うためのライブラリがあれば、もっとftrace backend のコードを短くできますし、他のアプリケーションでも手軽に ftrace marker が使えるようになって便利です。ライブラリ名は、"libftrace" などという名前になるのでしょうか。<br /> 最近、ftrace は <a href="https://patchwork.kernel.org/patch/1536311/">snapshot 機能</a>や、<a href="http://lwn.net/Articles/540280/">multiple buffer 機能</a>が追加されています。ユーザ空間のプログラム内からそれらの機能を利用するライブラリがあるとさらに嬉しいです。Etsukatahttp://www.blogger.com/profile/12706811820861626647[email protected]0