2020/02/12追記
復元するときにシンプルで、仕組みをわかっている方が安全だろうと自作してみましたが、結局こちらに落ち着きました。
https://github.com/lamw/ghettoVCB
似たような処理を行っており、使い勝手や安定性も遥かに上です。
ESXiのバックアップに苦労した過去
久々にESXiを触りました。名前がvSphere Hypervisorになっていたとは。
以前バージョン5のときに社内でサーバーを運用するのに使っていましたが、バックアップの運用に苦労しました。
色々探してもちょうどいいソリューションがなく、諦めてバックアップにはActonis製のソフトウェアを使っていました。
当時はもうちょっと安かったですが、それでもいい値段しました。
https://www.acronis.com/ja-jp/business/backup/virtual-machine/
でもこれが、まあ安定して動いてくれない。動かしてた環境がしょぼかったのもあると思いますが。
この前、VirtualBoxをハイパーバイザーとして使う際のバックアップスクリプトを作ったので、そのvSphere版を作ってみました。
VirtualBoxのVMをライブ/オンライン/無停止バックアップするスクリプト
https://qiita.com/miyanaga/items/39c19f1aa1171b2b402e
個人用なので作りが甘く、もっとよいやり方があると思いますが、プライベートな仮想環境のバックアップに参考ください。
やってること
各VMについて、.vmx
ファイル、.vmdk
ファイル、.log
ファイルをバックアップします。
バックアップの直前にスナップショットを作成し、バックアップ後にそのスナップショットを削除します。
起動中のVMは、ブロックデバイスのイメージファイルがロックされるのでコピーできません。仮にコピーできても整合性が保たれないので危険です。
そこでスナップショットをとると、イメージファイルが一度凍結され、差分が別のファイルに書き込まれます。その差分ファイルのみロックされるので、ファイルコピーで安全にイメージファイルをバックアップできます。
最初はVMのディレクトリにある全ファイルをコピーしていたのですが、メタデータとの不整合のためかVMを起動できませんでした。それで上記の拡張子のみコピーしています。
有償版のvSphereではライブマイグレーションができたと記憶していますが、それに比べると貧弱です。
スナップショットの作成と削除で謎の標準エラーが出ます。
スナップショットのIDはインクリメントされるのでどんどん増えていきます(特に不都合はないですが)。
まあ個人用なのでよしとします。
使い方
vSphere Hypervisor上で実行します。
backup=""
を実際のバックアップ先に変更してください。
各VMには便宜的にでも一つ以上のスナップショットを作っておくと、高速な差分バックアップが実現します。
#!/bin/sh
# vsphare Hypervisor(6.7で動作確認)のホットバックアップスクリプト
# TODO
# * スナップショットの作成と削除で'Create Snapshot:: not found'という標準エラー
# * スペースを含むVMへの対応
# * 名称が重複するVMへの対処
# * バックアップ先の同期削除(rsync的な動作)
# datastoreが存在するvolumesパスとバックアップ先
volumes="/vmfs/volumes"
backup="/backup"
completed=""
# VMをリストアップ
vms=$(vim-cmd vmsvc/getallvms | tail -n +2)
# スナップショットを作成する関数
# 引数は整数によるVMのID、戻り値はスナップショットID(失敗した場合は""か0)
create_snapshot() {
vmid="$1"
# 日付付きスナップショットラベル
ts=$(date '+%Y-%m-%d %H:%M:%S')
label="backup $ts"
# スナップショットを作成
$(vim-cmd vmsvc/snapshot.create "$vmid" "$label" 2>/dev/null)
# スナップショットIDはインクリメントされるので数値として最も大きなIDを取得
# 正常に取得できない場合は初期値0のまま
snapshotid=$(vim-cmd vmsvc/snapshot.get $vmid | grep 'Snapshot Id' | awk 'BEGIN{ id=0 } { if ($4>id) id=$4 } END{ print id }')
echo $snapshotid
}
# スナップショットを削除する関数
# 引数は整数によるVMのIDとスナップショットID
remove_snapshot() {
vmid="$1"
snapshotid="$2"
# スナップショットを削除
$(vim-cmd vmsvc/snapshot.remove "$vmid" "$snapshotid" 2>/dev/null)
}
# VMレコードを解析する正規表現
re_vm_record='^([0-9]+) +([^ ]+) +\[([^\]+)\]\s+([^ ]+).+'
# リストアップしたVMを1件ずつ処理
IFS=$'\n'
for record in $vms; do
# レコードを解析して整数のID、データストア、.vmxファイルパス、vmパスを取得
vmid=$(echo "$record" | sed -r "s!$re_vm_record!\1!")
datastore=$(echo "$record" | sed -r "s!$re_vm_record!\3!")
vmx=$(echo "$record" | sed -r "s!$re_vm_record!\4!")
vm=$(dirname "$vmx")
# 意図しないレコードフォーマットなどで.vmxファイルパスを取得できなかった場合は次のVMへ
if [ "$vmx" = "" ]; then
echo "Could not parse vmx in '$record'"
continue
fi
# .vmxファイルのフルパス
path="$volumes/$datastore/$vmx"
file=$(basename "$path")
dir=$(dirname "$path")
# .vmxファイルがもし存在しない場合は次のVMへ
if [ ! -f "$path" ]; then
echo "'$path' does not exists"
continue
fi
# バックアップ先ディレクトリ
dest="$backup/$vm"
mkdir -p "$dest"
# 一旦、現時点のvmxをコピーしておく
cp -au "$path" "$dest/$file.tmp"
# バックアップ用のスナップショット
snapshot=$(create_snapshot "$vmid")
# スナップショットIDが""または0の場合は作成に失敗したため次のVMへ
if [ "$snapshot" = "" -o "$snapshot" = "0" ]; then
echo "Failed to create snapshot of '$vmx'"
continue
fi
# .vmemと.vmsnはスナップショットごとに追加されるので蓄積しないように削除
# rm -f "$dest/"*.vmem "$dest/"*.vmsn
# cp -au "$dir/"* "$dest"
# vmdkファイルのコピーだけでも復元する
cp -au "$dir/"*.vmdk "$dir/"*.log "$dest"
# 一時ファイルからスナップショット作成まえの.vmxファイルを復元
mv -f "$dest/$file.tmp" "$dest/$file"
# バックアップ用のスナップショットを削除
$(remove_snapshot "$vmid" "$snapshot")
# 完了したVMを記録
completed="$completed $vm"
done
echo "Backup complated:$completed"