質問:NFSを介して/ usr / binなどのバイナリを起動すると(ネットワークブートシステムなど)、NFSが遅くなる可能性があります。 RAMバッファキャッシングは、速度低下を防止するのに十分ではないかもしれない。
アイデア:NFSからファイルをインポートしながら、ローカルにファイルを保存できるローカルディスクキャッシュを持つことができるようにする必要があるようです。
質問:UNIXシステムで似たようなものを見た人はいますか?
背景:
FreeBSDには、Unionfsを使用して素晴らしいスタックファイルシステムを構築するための素晴らしい方法がたくさんあります。現在、AWSには/ usrファイルシステムツリーの大部分がNFS経由でマウントされているため、1 GBのディスクのみを使用するシステムがあります。以前は、デフォルトのブートに/usrが必要でなかったため、これを簡単に実行できました。これはもう少し難しくなりましたが(特に起動に失敗したときにコンソールから出ることができないAWSでは)、ローカルドライブの/ usrツリーから必要な最小値を取得して管理しました。が表示されたら、/ usrツリーにNFSをマウントしました。
実行中のシステムで何かを更新する必要がある場合に備えて、デフォルトの最小ローカルハードドライブ/ usrツリーに書き込むことができるバックドアもあります。
それは美しいです。
NFS(Amazon EFS)は非常に遅いです。そして、バッファキャッシュは正しく動作しません。たとえば、AWSリソースを管理するためのawsコマンドラインインターフェイスは、awsコマンドが呼び出されるたびに多数のインクルードを吸収するPythonを使用します。単純なaws CLIコマンドの実行には20秒かかります。繰り返し実行しても、キャッシュ、NFS属性のキャッシュなどが役に立ちそうですが、そうではありません。
考えられる解決策(FreeBSDで):
だから私がやりたいことは、ローカルディスクベースのUFSファイルシステムであるNFSレイヤーの上に別のUnionfsレイヤーを置くことです。ただし、起動時には空であり、NFSから何かをロードするたびに(今は動的に更新されるデータではなく安定したバイナリであると仮定)、ディスクにコピーを残します。
このソリューションの実装:
だから私が考えるのは次のとおりです。存在する/usr/src/sys/fs/unionfs/union_vnops.c次のような非常に単純なコードがあります。
static int
unionfs_open(struct vop_open_args *ap)
{
...
if (targetvp == NULLVP) {
if (uvp == NULLVP) {
if ((ap->a_mode & FWRITE) && lvp->v_type == VREG) {
error = unionfs_copyfile(unp,
!(ap->a_mode & O_TRUNC), cred, td);
if (error != 0)
goto unionfs_open_abort;
targetvp = uvp = unp->un_uppervp;
} else
targetvp = lvp;
} else
targetvp = uvp;
}
(ap->a_mode & FWRITE)
書き込み用に下位レイヤーのみにあるファイルにアクセスすると、この部分が上位レイヤーにコピーされます(uvp == NULLVP) && lvp->v_type == VREG
。
すべてのファイル(読み取り専用アクセス権を持つファイルも含む)のコピーを作成する機能を追加するのは十分簡単です。その後、そのコピーも作成し、次回はディスクからファイルを読み込みます。
そのために新しいオプションを追加します。/usr/src/sys/fs/unionfs/union.h新しいオプションであるコピー戦略を追加します。
/* copy policy of upper layer */
typedef enum _unionfs_copypolicy {
UNIONFS_COPY_ON_WRITE = 0,
UNIONFS_COPY_ALWAYS
} unionfs_copypolicy;
struct unionfs_mount {
struct vnode *um_lowervp; /* VREFed once */
struct vnode *um_uppervp; /* VREFed once */
struct vnode *um_rootvp; /* ROOT vnode */
unionfs_copypolicy um_copypolicy;
unionfs_copymode um_copymode;
unionfs_whitemode um_whitemode;
uid_t um_uid;
gid_t um_gid;
u_short um_udir;
u_short um_ufile;
};
正直なところ、私はこれらすべてのパターンをスペースのビットフィールドとして扱いたいと思います。とにかく、上記のコードを次のように変更できます。
unp = VTOUNIONFS(ap->a_vp);
ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
...
if (targetvp == NULLVP) {
if (uvp == NULLVP) {
if (((ap->a_mode & FWRITE) || (ump->um_copypolicy == UNIONFS_COPY_ALWAYS)) && lvp->v_type == VREG) {
error = unionfs_copyfile(unp,
!(ap->a_mode & O_TRUNC), cred, td);
if (error != 0)
goto unionfs_open_abort;
targetvp = uvp = unp->un_uppervp;
これが必要なすべてです。つまり、プロパティとシャドウディレクトリのすべての処理は、必要に応じてUnionfs_copyfile関数内で処理されることを願っています。
この場合、カーネルモジュール内によく配置されたmount_unionfsに新しい読み取り時にコピーポリシーオプションを追加するだけです。/usr/src/sys/fs/unionfs/union_vfsops.c
static int
unionfs_domount(struct mount *mp)
{
int error;
...
u_short ufile;
unionfs_copypolicy copypolicy;
unionfs_copymode copymode;
unionfs_whitemode whitemode;
...
ufile = 0;
copypolicy = UNIONFS_COPY_ON_WRITE; /* default */
copymode = UNIONFS_TRANSPARENT; /* default */
whitemode = UNIONFS_WHITE_ALWAYS;
...
if (vfs_getopt(mp->mnt_optnew, "copypolicy", (void **)&tmp,
NULL) == 0) {
if (tmp == NULL) {
vfs_mount_error(mp, "Invalid copy policy");
return (EINVAL);
} else if (strcasecmp(tmp, "always") == 0)
copypolicy = UNIONFS_COPY_ALWAYS;
else if (strcasecmp(tmp, "onwrite") == 0)
copypolicy = UNIONFS_COPY_ON_WRITE;
else {
vfs_mount_error(mp, "Invalid copy policy");
return (EINVAL);
}
}
if (vfs_getopt(mp->mnt_optnew, "copymode", (void **)&tmp,
...
}
if (vfs_getopt(mp->mnt_optnew, "whiteout", (void **)&tmp,
...
}
}
...
UNIONFSDEBUG("unionfs_mount: uid=%d, gid=%d\n", uid, gid);
UNIONFSDEBUG("unionfs_mount: udir=0%03o, ufile=0%03o\n", udir, ufile);
UNIONFSDEBUG("unionfs_mount: copypolicy=%d, copymode=%d, whitemode=%d\n", copypolicy, copymode, whitemode);
したがって、これはFreeBSDで私が望むことを達成するでしょう。今、私のシステムのソースコードを入手してこのパッチを適用し、Unionfs.koカーネルモジュールを再コンパイルし、私のシステムに置き換えて、それが機能できることを確認する必要があります。
# Custom /etc/fstab for FreeBSD VM images
/dev/gpt/rootfs / ufs rw 1 1
/dev/gpt/varfs /var ufs rw 1 1
fdesc /dev/fd fdescfs rw 0 0
proc /proc procfs rw 0 0
/usr /.usr nullfs rw 0 0
fs-xxxxxxxx.efs.rrrr.amazonaws.com:/ /usr nfs rw,nfsv4,minorversion=1,oneopenown,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport,late,bg 0 0
/var/cache/usr /usr unionfs rw,copypolicy=always 0 0
その他の改善点:キャッシュエントリの削除
今、私は別のホワイトニングモードを追加したいかもしれないことに気づきました。つまり、そうではありません。つまり、キャッシュからファイルを削除する効果を使用して上位レイヤーからファイルを削除できるはずですが、下位レイヤーでファイルをマスクするホワイトニング効果はないため、空のように見えます。 Union.h に UNIONFS_WHITE_NEVER を追加する方法は次のとおりです。
/* whiteout policy of upper layer */
typedef enum _unionfs_whitemode {
UNIONFS_WHITE_ALWAYS = 0,
UNIONFS_WHITE_WHENNEEDED,
UNIONFS_WHITE_NEVER
} unionfs_whitemode;
次に、Union_vnops.cで:
static int
unionfs_remove(struct vop_remove_args *ap)
{
...
if (uvp != NULLVP) {
/*
* XXX: if the vnode type is VSOCK, it will create whiteout
* after remove.
*/
if (ump == NULL || ump->um_whitemode == UNIONFS_WHITE_ALWAYS ||
(lvp != NULLVP && ump->um_whitemode != UNIONFS_WHITE_NEVER))
cnp->cn_flags |= DOWHITEOUT;
error = VOP_REMOVE(udvp, uvp, cnp);
} else if (lvp != NULLVP && ump->um_whitemode != UNIONFS_WHITE_NEVER)
error = unionfs_mkwhiteout(udvp, cnp, td, path);
これにより、rmdirに関する内容がある可能性があります。
static int
unionfs_rmdir(struct vop_rmdir_args *ap)
{
...
if (uvp != NULLVP) {
if (lvp != NULLVP) {
error = unionfs_check_rmdir(ap->a_vp, cnp->cn_cred, td);
if (error != 0)
return (error);
}
ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS ||
(lvp != NULLVP && ump->um_whitemode != UNIONFS_WHITE_NEVER))
cnp->cn_flags |= DOWHITEOUT;
error = unionfs_relookup_for_delete(ap->a_dvp, cnp, td);
if (!error)
error = VOP_RMDIR(udvp, uvp, cnp);
}
else if (lvp != NULLVP && ump->um_whitemode != UNIONFS_WHITE_NEVER)
error = unionfs_mkwhiteout(udvp, cnp, td, unp->un_path);
これも退去作業を行う必要があります。
しかし、そのすべてをする前に、人々がすでに見つけた既存のトリックがあるかどうか疑問に思います。
PS:ここに私の全体的な違いとテスト結果があります。https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=251363
短い答えは次のとおりです。実際にはかなりうまくいきます。不明な点がもう1つあります。 Unionfsはブロックデバイスを使用しませんが、ディレクトリを使用します!本当に素敵ですね。デバイスを作成する必要もありません。提案したようにfstabを更新しましたが、後でNFSをマウントするまで遅らせる必要があるため、まったく使用しません。したがって、これを削除して後でUnionfsベースのキャッシュをオンにすることをお勧めします(例:/etc/rc.local)。これは簡単です。
mount -t unionfs -o copypolicy=always /var/cache/usr /usr
また、/var/cache/usrディレクトリがまだ直接利用可能であることがわかったので、そこからファイルを削除するとキャッシュから削除されます。つまり、ホワイトニング設定をまったく台無しにする必要はありません。
代わりに、Unionfs_copyfile(...) 呼び出しが「デバイスに空き領域がありません」というエラーを返す場合、キャッシュから古い atime のファイルを削除し、古いファイルを削除するまで古いファイルを削除する自動キャッシュ除去戦略を思い出さなければなりません。スペースが回収されてからもう一度お試しください。これは仕事です。非常に簡単です(古いatimeファイルを見つけることを除いて)。
貧しい人の単純キャッシュ退去
数日に 1 回実行して、1 日間find /var/cache/usr -atime 2 -exec rm \{\}\;
アクセスしていないアイテムを削除します。
より興味深いより深い質問は、ブロックを読み取るときに上位層にブロックを書き込むことで、Unionfs_copyfile(...)関数をより効率的にすることができるかどうかです。たぶん、すべてをブロック指向にすることもできます。これにより、下位階層のファイルがリーンになると、上位階層でもリーンになります。
答え1
NFS v3またはv4.xは遅くはありません。だから私はあなたがNFS v2について話していると仮定します。
ちょうどマニュアルページに移動しましたman 5 nfs
。このオプションを偶然発見しました金融委員会。
これはあなたが使用したいことをしているようですcachefilesd
。おそらく/dev/shmでキャッシュを見つけることができます。これにより、作業速度がさらに速くなります。
NFSを介して多くの同時クライアントにCDを提供する700MBのRAMキャッシュがあったSolarisでこのようなことを行ったことを覚えています。