1. miyanaga

    Posted

    miyanaga
Changes in title
+vSphere Hypervisor (ESXi)でカジュアルにホットバックアップするスクリプト
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,155 @@
+# 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には便宜的にでも一つ以上のスナップショットを作っておくと、高速な差分バックアップが実現します。
+
+```bash
+#!/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"
+```