ESX鯖にSSDを入れてswap先に使っていたのだが、まぁそれはそれで快適にゃ動作ににゃったものの、各vmが自力で吐くswapはやはりストレージ上に溜まるわけで、蓄積するとやはり遅い。
spoolだののtmp的にゃ用途には、いろんにゃ所でキャッシュが効くので、writeが極端に遅くにゃければストレージ上で問題にゃいのだが。
というわけで、ESXのローカルSSDを活用すべく、vmのvmdk構成を変更。各vmにswap用ドライブを追加し、そのvmdkをSSD上に配置すれば・・・と思ったのだがこれはとりあえず失敗。snapshotを使うとsnapshotはvmxのディレクトリに保存されてしまうらしく、これの変更は手間というか周囲の動作保証が取れんのでやりたくにゃい。
そこで発想を逆転して、vmxはSSD上に、vmdkはNAS上に配置することに。これはディスクの場所変更でオフィシャルに出来る。vmの構成ディレクトリが複数ににゃってバックアップがめんどくさい等のデメリットもあるが、ひとまずsnapshotと併用するとかにゃり高速化するので非常に使える。つまりNASにはWriteが一切行かにゃいのでRAID5でも構わにゃいしIOpsも稼げる。頻繁に書かれる場所はsnapshotとしてSSD上に自動的に配置される。vmが自前でswapすれば必ずSSDに書かれるわけで合理的。膨大にゃ量を書くvmはsnapshot取らにゃいだろうからSSDの容量も80G有れば十分すぎる。
さすがにプチフリというか、WriteによってReadが阻害されまくるSSDではvm数次第で苦しくにゃってくるが、ある程度キャッシュ等積んだSSDにゃら問題は起きにゃいだろう。にゃんだかESXのローカルWrite自体がいまいちにゃ速度だし・・・これは不具合かにゃぁ?
ESX# dd if=/dev/zero of=xxx bs=1024000 count=100
100+0 records in
100+0 records out
102400000 bytes (102 MB) copied, 4.96558 seconds, 20.6 MB/s
とかいう状態でSSDとは思えにゃい低速っぷり(笑) HDDより遅いし。理屈ではもっと高速でsnapshot時のメモリダンプとかも高速にゃはず。にゃんだろう、キャッシュ?
いろいろ溜まってきたのでメモ。
ds2fullpath (){
sed -e 's/\[\([^]]\+\)\] \([^[ ]\+\)/\/vmfs\/volumes\/\1\/\2/'
}
get_ds_from_path (){
sed -e 's/\/vmfs\/volumes\/\([^\/]\+\)\/.*/\1/'
}
inner_quote (){
sed -e 's/[^"]*"\(.*\)".*/\1/'
}
get_allvms_vmid_name (){
vim-cmd vmsvc/getallvms | sed -e 's/\[.*\] .*//' -e 's/ \+$//' -e 's/\([0-9\]\+\) \+\(.*\)/\1 "\2"/' |
awk 'NR!=1'
}
get_allvms_vmid_datastore (){
vim-cmd vmsvc/getallvms | sed -e 's/\([0-9\]\+\) \+.* \+\[\(.*\)\] \(.*\/.*\.vmx\) \+.*/\1 "\2" "\3"/' |
awk 'NR!=1'
}
get_allvms_vmid_vmxpath (){
vim-cmd vmsvc/getallvms | sed -e 's/\([0-9\]\+\) \+.* \+\[\(.*\)\] \(.*\/.*\.vmx\) \+.*/\1 "[\2] \3"/' |
ds2fullpath |
awk 'NR!=1'
}
get_vmid_from_name (){
get_allvms_vmid_name | grep \"$1\" |awk '{print $1}'
}
get_vmxpath_from_vmid (){
get_allvms_vmid_vmxpath | awk '/^'$1' /{print $2}' | sed -e 's/^\"//' -e 's/\"$//'
}
get_vmxpath_from_name (){
vmid=`get_vmid_from_name $1`
get_vmxpath_from_vmid ${vmid}
}
get_vmsdpath_from_vmid (){
get_vmxpath_from_vmid $1 | sed -e 's/\.vmx$/.vmsd/'
}
get_datastore_from_vmid (){
get_allvms_vmid_datastore | awk '/^'$1' /{print $2}' | sed -e 's/^\"//' -e 's/\"$//'
}
get_vmname_from_vmid (){
get_allvms_vmid_name | grep "^$1 " | sed -e 's/.*"\(.*\)".*/\1/'
}
get_vmdk_data_file_from_vmdk (){
test -f "$1" || return
f=$(grep " \".*\.vmdk\"$" $1 | grep -v parentFileNameHint | sed -e 's/.* "\(.*\)"$/\1/')
echo "$f"|grep ^/ >/dev/null && echo "$f"
echo "$f"|grep ^/ >/dev/null || echo "`dirname "$1"`/$f"
}
is_there_snapshot (){
vmid=$1
snapshot_name=$2
vim-cmd vmsvc/snapshot.get $vmid | grep "\-\-Snapshot Name" | sed -e 's/-\+Snapshot Name \+: //' |
grep "$snapshot_name" >/dev/null
}
remove_last_snapshot (){
vmid=$1
if [ "`get_vmxpath_from_vmid $vmid`" = "" ] ; then return 255 ; fi
lv=`vim-cmd vmsvc/snapshot.get $vmid |grep CHILD |wc -l`
vim-cmd vmsvc/snapshot.remove $vmid 0 $lv
}
get_alltaskids (){
vim-cmd vimsvc/task_list | grep vim.Task: | sed -e 's/.*vim.Task://' -e 's/,//' -e "s/'//"
}
get_taskids_from_vmid (){
vim-cmd vimsvc/task_list | grep vim.Task:haTask-$1 | sed -e 's/.*vim.Task://' -e 's/,//' -e "s/'//"
}
filter_running_taskids (){
while read taskid ; do
is_task_running $taskid && echo $taskid
done
}
get_running_alltaskids (){
get_alltaskids | filter_running_taskids
}
get_running_taskids_from_vmid (){
get_taskids_from_vmid $1 | filter_running_taskids
}
get_taskname_from_taskid (){
vim-cmd vimsvc/task_info $taskid | grep "name = " | sed -e 's/.*"\(.*\)".*/\1/'
}
is_there_running_task_vmid (){
get_running_taskids_from_vmid $1 | grep .
}
is_task_running (){
taskid=$1
vim-cmd vimsvc/task_info $taskid | grep 'state = "running",' >/dev/null
}
wait_while_task_running_vmid (){
while : ; do
taskid=`get_running_taskids_from_vmid $1 | head -1`
if [ "$taskid" == "" ] ; then break ; fi
echo waiting `get_vmname_from_vmid $1` `get_taskname_from_taskid $taskid`
sleep 3
done
}
path_r2a (){
echo `cd "$1" && pwd`
}
path_cut (){
src_dir=`path_r2a "$1"`
file=$2
basedir=$(dirname "$src_dir")
c=$(echo "$basedir" | wc -c)
echo "$file" | cut -c ${c}- | sed -e 's/^\///'
}
cp_dir_sparse (){
cp_prog=$1
SRC=`path_r2a "$2"`
DST=`echo "$3" | sed -e 's/\/$//'`
find "$SRC" -type f | while read a ; do
$cp_prog "$a" $DST/`path_cut "$SRC" "$a"`
done
}
get_datafiles_from_filelayout_with_vmdk_data (){
get_datafiles_from_filelayout $1 |
while read a ; do
echo "$a"
echo "$a"|grep \.vmdk$ >/dev/null && get_vmdk_data_file_from_vmdk "$a"
done
}
get_datafiles_from_filelayout (){
vim-cmd vmsvc/get.filelayout $1 |
grep "\"\[.*\] .*\..*\"" |
inner_quote |
ds2fullpath
}
get_vmx_dir (){
d=`get_vmxpath_from_vmid $1`
dirname "$d"
}
get_logfile_dir (){
vim-cmd vmsvc/get.filelayout $1 |sed -e 's/^ \+//'| grep "^logDirectory *=" | inner_quote | ds2fullpath
}
get_logfiles_from_filelayout (){
d=`get_logfile_dir $1`
vim-cmd vmsvc/get.filelayout $1 | sed -e ':a;$!N;$!b a;s/\n//g' |
sed -e 's/.*,.*logFile *=[^\[]\+\[\([^]]*\)\].*/\1/' -e 's/",/"\n/g' |
inner_quote |
while read a ; do
echo "$d/$a"
done
}
get_configfiles_from_filelayout (){
d=`get_vmx_dir $1`
vim-cmd vmsvc/get.filelayout $1 |
sed -e ':a;$!N;$!b a;s/\n//g' |
sed -e 's/.*,.*configFile *=[^\[]\+\[\([^]]*\)\].*/\1/' -e 's/",/"\n/g' |
inner_quote |
while read a ; do
echo "$d/$a"
done
}
get_allfiles_from_filelayout (){
sub (){
get_configfiles_from_filelayout $1
get_logfiles_from_filelayout $1
get_datafiles_from_filelayout_with_vmdk_data $1
}
sub $1 | sort | uniq
}
vmname="$1"
backupdir="$2"
copy_p="python -u cp_sparse.py"
vmid=`get_vmid_from_name "$vmname"`
vmx="`get_vmxpath_from_vmid $vmid`"
vmsd="`get_vmsdpath_from_vmid $vmid`"
vim-cmd vmsvc/snapshot.create $vmid __backup __backup 1 1
wait_while_task_running_vmid $vmid
vim-cmd vmsvc/snapshot.get $vmid
dupfile=0
get_allfiles_from_filelayout $vmid | while read a ; do
basename "$a"
done | sort | uniq -d | grep . >/dev/null && dupfile=1
get_allfiles_from_filelayout $vmid | while read a ; do
dst="$backupdir/$vmname"
if [ "$dupfile" = "1" ] ; then
ds="`echo "$a" | get_ds_from_path`"
dst="$backupdir/$vmname/$ds"
fi
$copy_p "$a" "$dst/`basename "$a"`"
done
remove_last_snapshot $vmid
wait_while_task_running_vmid $vmid
get_datafiles_from_filelayout $vmid | grep \.vmdk$ | while read a ; do
dst="$backupdir/$vmname"
if [ "$dupfile" = "1" ] ; then
ds="`echo "$a" | get_ds_from_path`"
dst="$backupdir/$vmname/$ds"
fi
$copy_p "$a" "$dst/`basename "$a"`"
done
get_datafiles_from_filelayout $vmid | sort | uniq | grep \.vmdk$ | while read a ; do
if [ "$dupfile" = "1" ] ; then
continue
fi
parent_path=`grep "^parentFileNameHint *=" "$a" |inner_quote`
echo "$parent_path" | grep "^/" >/dev/null || continue
vmdk=`basename "$parent_path"`
cat $a | sed -e 's/^\(parentFileNameHint *= *\)".*"/\1"'"$vmdk"'"/' > "$backupdir/$vmname/`basename $a`"
done
分散datastoreに対応しようと思ったけどめどくにゃっていい加減にゃ対応のまま放置してあります状態(笑)
ま、そのままコピーするだけで、一応バックアップにはにゃるわけだし、複雑にゃことしにゃい方が良いんだけど、やっぱりそのままインポートしてrevert押せばすぐ起動ってのはバックアップとして有用だと思うわけで。
freenasのnfsやiSCSIとESXの組み合わせでかにゃり格闘したのだが、いい加減疲れた上に、ESXiを入れ直そうと思ったらUSBがエラー吐いて止まるとかいろいろ霊障も起き出したので、freenasを捨てることに。
Ubuntuを実機に入れてInst時にmd raidを組む。ExpertModeでinstしたら不明にゃ選択肢も出て少々時間がかかる。めどい。
とりあえずnfsをいれて
ESX# dd if=/dev/zero of=test bs=1024000 count=1000
1000+0 records in
1000+0 records out
1024000000 bytes (1.0 GB) copied, 24.115 seconds, 42.5 MB/s
おお、まともだ!
ESX# time cat test >/dev/null
real 0m54.728s
・・・あるぇ?
ESX# dd if=test of=/dev/null bs=1024000
1000+0 records in
1000+0 records out
1024000000 bytes (1.0 GB) copied, 49.9522 seconds, 20.5 MB/s
にょ〜ん・・・
まぁFreeNASよりは倍ほどマシだが。
因みにcat file >/dev/nullでnfsdが5%+5%ほどCPU食ってる。ちょっと食い過ぎじゃね? まぁCPUが1コアの2.7GHzとか速度求めるには微妙ではあるとしても。
nfs遅いよねー!(?)と思ってわざわざディスク6つも積んでめんどくさいRAID10設定までしてInstしたFreenasのiSCSIが強烈に遅い。10MB/s出てにゃいんだが?
これはESX側との相性もあるんだろうと思うがやっぱりこれは使えん。
nfsが複数繋いで15MB/sとかでまだマシだったり。
vm上で使ってるので、vmware toolsとの相性という可能性も結構あるのだが、どうせvm上でしか使わにゃいのでとりあえず、ページファイル無し設定にすると動作が怪しい。
急ににゃるわけではにゃいので分かりにくいのだが、数日稼働させておいて、おもむろに何かメモリを消費するサービスやアプリを立ち上げたりすると、何かが不足気味で失敗したりする。ページファイル有りだとにゃらにゃい。
もちろんメモリはキャッシュに食われているが余っている状態にゃので、結局のところMSキモイといういつもの結論に至るのだが、まぁ2008R2がvmで動くようににゃれば状況が変わるだろうから、結構どうでもいいネタかも
USBで繋いだ古いIDEのHDDからファイルをコピーしていたら、突然FFCがエラー吐いて落ち。FS破損とか出ててreaddirが通らにゃい。chkdskがエラー検出して止まる。USB-IDE変換コネクタの不調が最も考えられたが、10回つにゃぎ替えて回復しにゃかったので、chkdks /f。
壊れた属性レコード (128、"") を
ファイル レコード セグメント 0 から削除します。
壊れた属性レコード (128、"") を
ファイル レコード セグメント 2 から削除します。
壊れた属性レコード (592、"") を
ファイル レコード セグメント 3 から削除します。
壊れた属性レコード (128、"") を
ファイル レコード セグメント 383 から削除します。
ファイルの検査を完了しました。
CHKDSK はインデックスを検査しています (ステージ 2/3)...
ファイル 25 のインデックス $O からインデックス エントリを削除します。
ファイル 25 のインデックス $O へインデックス エントリを挿入しています。
ファイル 0 内の軽度にゃファイル名エラーを修復します。
ファイル 1 内の軽度にゃファイル名エラーを修復します。
ファイル 2 内の軽度にゃファイル名エラーを修復します。
ファイル 3 内の軽度にゃファイル名エラーを修復します。
ファイル 5 内のインデックス $I30 のインデックス エントリ $LogFile を削除します。
ファイル 5 内のインデックス $I30 のインデックス エントリ $MFT を削除します。
ファイル 5 内のインデックス $I30 のインデックス エントリ $MFTMirr を削除します。
ファイル 5 内のインデックス $I30 のインデックス エントリ $Volume を削除します。
インデックスの検査を完了しました。
ドライブ上の軽度にゃ矛盾をクリーンアップしています。
CHKDSK は破損ファイルを回復しています。
孤立したファイル $MFT (0) をディレクトリ ファイル 5 に回復します。
孤立したファイル $MFTMirr (1) をディレクトリ ファイル 5 に回復します。
孤立したファイル $LogFile (2) をディレクトリ ファイル 5 に回復します。
孤立したファイル $Volume (3) をディレクトリ ファイル 5 に回復します。
CHKDSK はセキュリティ記述子を検査しています (ステージ 3/3)...
セキュリティ記述子の検査を完了しました。
データ属性をファイル 0 に挿入します。
データ属性をファイル 2 に挿入します。
データ属性をファイル 383 に挿入します。
マスタ ファイル テーブル (MFT) のミラーでエラーを修復します。
マスタ ファイル テーブル (MFT) のミラーでエラーを修復します。
ログ ファイル エラーを修復します。
マスタ ファイル テーブル (MFT) のデータ属性エラーを修復します。
CHKDSK はマスタ ファイル テーブル (MFT) ビットマップに割り当て済みとして
マークされている空き領域を検出しました。
ボリューム ビットマップ エラーを修復します。
ファイル システムを修正しました。
と、修復は問題にゃく行われ、とりあえず全ファイルの見た目存在は復活。不良セクタ0。ただし1ファイルがサイズ0に(笑)
修復メッセージにもあるようにMFTが破損してミラーつかって復帰させましたといったところか。
chkdsk後の初回自動マウントで長時間ごりごり言ってたのでたぶんそこでMFT相当のコピーが行われた模様。
ま、NTFS堅牢ですね、という結論でもいいのだが、にゃぜにファイルをreadしてただけでいきにゃり破損するか。atimeは切ってあるが、にゃにかwriteする要因があるのか? かにゃり不気味。
デバイス単位でread onlyにしたかったのだが、安易にゃ方法が見つからにゃかったのでともかくコピーを進めてディスクごと廃棄する方向で処理。