nkjmkzk.net

powered by Kazuki Nakajima

OCFS2はcache-coherentなクラスターファイルシステム。それってどういうこと?

OCFS2はcache-coherentなクラスターファイルシステムです。

これは結構すごい機構だと思うのですが、整理して理解するにはすこしばかり低レイヤの技術知識が必要になります。ということでまず一般的なI/Oの仕組みをおさらいした上で、cache-coherentではないNFSとcache-coherentなOCFS2を比較することによってcache-coherent何たるかを理解していきたいと思います。

とあるプロセスからI/O、つまりディスクへのデータ書き込みまたはディスクからのデータ読み込みを行うにはいくつかの経路があります。

一つはlibcのstdioを用いたbuffered I/Oで、fwrite()等がそれです。このライブラリを使ったI/Oではユーザ空間でキャッシュが行われます。

もう一つはカーネルのシステムコールを用いたより低レイヤで実装されているwrite()等です。これは通常カーネルが管理するキャッシュを経由してディスクにアクセスを行います。

また、同じwrite()であってもそもそもファイルをオープンするときにO_DIRECTフラグを立ててオープンしたファイルへの読み書きはキャッシュを迂回して最短距離でアクセスされます。

キャッシュはI/O高速化のためにとても重要ですが、OSはディスク上のデータとキャッシュ上のデータの差異を意識してアクセスをハンドリングし、あるタイミングで同期させなければなりません。

スタンドアロンの環境ではOSはうまく複数プロセスからのアクセスをさばきつつ同期を行います。例えばプロセスAがとあるファイルを更新し、その更新データがキャッシュ上にのみ存在してディスクとは同期されていない状態で、プロセスBがそのファイルにアクセスしたとします。そうするとOSはキャッシュ上の最も新しいデータをプロセスBに返します。もしプロセスBが読み込みではなく更新処理を行う場合でも最新のデータを踏まえて更新が行われるので整合性は保たれます。

しかしこれが複数のOSにまたがった場合はそう簡単ではありません。例えばサーバA、サーバBという2ノードからNFSでファイルシステムを共有している場合の挙動を見てみましょう。サーバAがNFS上にあるファイルにwirte()システムコールで更新(追記)処理を行ったとします。この場合もデータはまずカーネル空間のキャッシュに書き込まれ、ディスクへの同期は保証されません。したがってまだディスクに追記データが書き込まれていない状態だとサーバBからは更新情報を知る術がなく、その状態で同じく更新(追記)処理を行ってしまうとデータが破壊されてしまいます。なので2ノードから同一ファイルを同時更新するような構成はとることができません。

ここで活躍するのがcache-coherentなクラスターファイルシステム、OCFS2です。OCFS2では前述のように2ノードから同一ファイルを同時更新するような構成をとることができます。それも同期書き込み等を必要とするわけではなく、一般的なwrite()システムコールでいままで通りカーネルのキャッシュを利用した書き込みでそれを実現します。それはOCFS2がcache-coherentだから、つまり、カーネル空間のキャッシュも含めてクラスターを構成するノード間でデータの整合性が取れるような仕組みが備わっているからです。先ほどNFSで行ったのと同じ処理をOCFS2に置き換えて実施してみると、サーバBはサーバAがまだディスクに書き出していないキャッシュ上の更新データを認識し、サーバAの更新を踏まえて自身の更新処理を行います。したがってすべての更新データは保護されます。

*ちなみにcoherentとなるキャッシュはカーネルが管理するキャッシュです。ユーザ空間で管理されるキャッシュは対象となりません。

この挙動を実際のアプリケーションで確認するのに僕は2ノードでクラスター化されているnginxのアクセスログを用いました。通常Webサーバを並べてクラスター化する場合、読み取り専用のコンテンツファイル、あるいは実行ファイルも含めNFSをはじめとした共有ストレージで共有されることが多いと思いますが、更新が行われるログファイルは各ノードがそれぞれ自身のファイルを持つことが一般的だと思います。

アプリケーションによって確実に同期書き込みが行われるデータベースのトランザクションログとは異なり、Webサーバのアクセスログは通常のカーネルキャッシュを経由した書き込みが行われる実装が多いと思います(多分ね)。実際にnginxも僕がソースを見た限りではwrite()(非同期)でログを更新しており、特にfsync()で都度同期させるようなことも行っていませんでした。その実装で、cache-coherentでないファイルシステム上のログファイルに対して複数ノードから同時更新を行うと更新内容はランダムに失われてしまいます。

挙動を確認するために実際にNFS上にログファイルを置き、それを2ノードから共有させてテストを行いました。2ノードに並行して負荷をかけ、アクセスした数とアクセスログに出力されたログの件数をつき合わせてみるというものです。

負荷はクライアントマシンから下記のようなスクリプトを実行してかけています。

#!/bin/bash

for i in {1..10000}
do
    curl http://172.22.0.61/61.html
done &

for i in {1..10000}
do
    curl http://172.22.0.62/62.html
done &

本来であれば20000件のログがアクセスログに出力されるはずですが、この構成での結果は下記の通りです。

[root@server-a]# cat /srv/nginx/log/access.log | wc -l
11740

多くのログが失われていることがわかります。
したがってNFSで共有ストレージが使える環境でもアクセスログは複数ノードに分散させることになるため解析時には分散したログファイルをマージしなければなりません。

そこでcache-coherentなOCFS2の出番です。OCFS2はこれまで見てきたとおり別ノード上のカーネルキャッシュ上の更新データに関して把握しながら更新を行うことができます。したがって複数ノードから一つのアクセスログを共有して同時更新することができるはずです。こちらも実際にテストを行いました。

この構成での結果は下記の通りです。

[root@server-a]# cat /srv/nginx/log/access.log | wc -l
20000

出力されるはずのログ数がすべて出力されていることがわかります。
そもそもアクセスログを共有するのがよいのかどうか、というような議論はさておき、OCFS2上だと複数ノードからカーネルキャッシュを経由した同時更新を行っても完全性のある出力結果が得られることがわかりました。これがcache-coherentである、ということになります。

without comments

Written by 中嶋 一樹

3月 3rd, 2011 at 2:19 pm

Posted in Uncategorized

Tagged with

Leave a Reply