EsxiとHyper-VのエクスポートをTrueNasにしたかった。

今回はじめて記事を書きますので、
暖かい目で見て頂ければ幸いです。
## 前提
TrueNasやEsxi、Windowsサーバーの詳しい設定や説明はここではしません。
それぞれのホストOSたちはping疎通ができる状態です。
windowsサーバーに関してはアップデート済みです。
## 目的
Esxiの仮想マシンとWindowsServer2012Hyper-Vの仮想マシンのバックアップを、
定期的にホストマシン以外の場所に取ること。

・[TrueNas](#truenas)
・[Esxi](#esxi)
・・[バックアップ手順](#esxiのバックアップ)
・・[自動バックアップ手順](#esxiの自動バックアップ)
・・[リストア手順](#esxiのリストア手順)
・[Hyper-V](#hyper-v)
・・[バックアップ手順](#hyper-vのバックアップ)
・・[自動バックアップ手順](#hyper-vの自動バックアップ)
・・[リストア手順](#hyper-vのリストア手順)
## TrueNas
TrueNasScale24.04を物理で構築しました。
容量は2TBです。
IPを192.168.100.100/24に設定。
プールとデータセットを作成。

プールフルパス
/mnt/data

データセットフルパス
/mnt/data/test

testにてSMBとNFS共有設定
またsshサービスも有効にしておきます。
TrueNasScaleではデフォルトでパスワードでのログインができないので、
必要に応じてユーザー設定からSSH password login enabled
にチェックを入れておきます。
TrueNas側での設定はこれくらいです。

## Esxi
Esxi6.5を物理で構築しました。
IPを192.168.100.101/24に設定
ストレージタブから、新しいデータストアの追加でNFS共有設定したtestを追加、
/vmfs/volumes/TrueNasでボリュームが作成されます。
仮想マシンを2つ作成。
・windows0
OS:WindowServer2012
ストレージでdatastore1を選択
ハードディスクは40GB
その他はデフォルトです。
テストファイルkenshiro.txtを作成
・centos7
OS:centos7
ストレージでdatastore1を選択
ハードディスクは16GB
その他はデフォルトです。
テストファイルzako.txtを作成

## Hyper-V
WindowsServer2012を物理で構築構築
IPを192.168.100.103に設定
エクスプローラーにて
\\192.168.100.100\data\test
をTドライブにマウント
hyper-vマネージャーにて仮想マシンを作成
・windows1
OS:WindowsSerber2012
40GBの仮想ハードディスクを作成
メモリは512MBで設定
その他はデフォルト

## Esxiのバックアップ
基本的には下記サイトを参考にしています。
ghettoVCBというツールを使います。
バックアップ方法は差分ではなくフルバックアップです。
シェルでバックアップを取る方式になります。
https://note.com/hitoshiarakawa/n/n7c05b548f556
ホストの設定からsshを有効にしておきます。

### (1) ghettoVCBのZipファイルをgithubからダウンロード
https://github.com/lamw/ghettoVCB/
ホストのPCにダウンロードしてもEsxi上にアップロードできます。

### (2) 解凍しdatastoreにアップロードする
場所はどこでもよいのですが、僕は仮想マシンと同じdatastore直下に配置します。
(3) コンフィグファイルとシェルスクリプトを編集する。
Esxiにsshして下記コマンドを実行する。

カレントディレクトリを移動
```
cd /vmfs/volume/datastore1/ghettoVCB-master
```
コンフィグファイルを編集
1行目と3行目のみ変更
1行目はsnapshotの保存先を指定
3行目は世代数を指定
```
vi ghettoVCB.conf

VM_BACKUP_VOLUME=/vmfs/volumes/TrueNas
DISK_BACKUP_FORMAT=thin
VM_BACKUP_ROTATION_COUNT=10
POWER_VM_DOWN_BEFORE_BACKUP=0
ENABLE_HARD_POWER_OFF=0
ITER_TO_WAIT_SHUTDOWN=3
POWER_DOWN_TIMEOUT=5
ENABLE_COMPRESSION=0
VM_SNAPSHOT_MEMORY=0
VM_SNAPSHOT_QUIESCE=0
ALLOW_VMS_WITH_SNAPSHOTS_TO_BE_BACKEDUP=0
ENABLE_NON_PERSISTENT_NFS=0
UNMOUNT_NFS=0
NFS_SERVER=172.30.0.195
NFS_VERSION=nfs
NFS_MOUNT=/nfsshare
NFS_LOCAL_NAME=nfs_storage_backup
NFS_VM_BACKUP_DIR=mybackups
SNAPSHOT_TIMEOUT=15
EMAIL_ALERT=0
EMAIL_LOG=0
EMAIL_SERVER=auroa.primp-industries.com
EMAIL_SERVER_PORT=25
EMAIL_DELAY_INTERVAL=1
EMAIL_USER_NAME=
EMAIL_USER_PASSWORD=
EMAIL_TO=auroa@primp-industries.com
EMAIL_ERRORS_TO=
EMAIL_FROM=root@ghettoVCB
WORKDIR_DEBUG=0
VM_SHUTDOWN_ORDER=
VM_STARTUP_ORDER=
```
シェルスクリプトを編集
編集箇所は16行目と26行目の2か所のみです。
中身は長すぎるので省略します
先ほど編集したコンフィグファイルと同じようにします。

```
vi ghettoVCB.sh

# directory that all VM backups should go (e.g. /vmfs/volumes/SAN_LUN1/mybackupdir)
VM_BACKUP_VOLUME=/vmfs/volumes/TrueNas

# Format output of VMDK backup
# zeroedthick
# 2gbsparse
# thin
# eagerzeroedthick
DISK_BACKUP_FORMAT=thin

# Number of backups for a given VM before deleting
VM_BACKUP_ROTATION_COUNT=10
```
このシェルを実行すると、指定したディレクトリに
```
仮想マシン名/仮想マシン名-yyyy-mm-dd_hh-mm-ss 
```
のフォルダが作成されます。
### (4)テキストファイルを作成する
ディレクトリはどこでもよいので、
(ここでは/vmfs/volume/datastore1/ghettoVCB-masterに作成しておく)
シェルを動かす際の仮想マシンリストを記載したテキストファイルを作成する。
```
vi vmlist.txt

windows0
centos7
```
### (5)シェルを実行してバックアップを開始する。
これでバックアップの下準備は終わったので
シェルを動かしてみます。
```
./ghettoVCB.sh -f vmlist.txt
```
シェルが走りますが、バックアップの進捗が10%になったところで
失敗するときが30％くらいの確率で起きます。
試しにバックアップ保存先をローカルに設定すると、
99.9%の確率で成功しますが、NFSでマウントした状態だと失敗します。
理由は分かったのですが原因は不明なので、別の策を考えます。

#### 代替え案その1、ローカルに保存後TrueNASに移動
正直面倒ですがローカルに空きがあればできます。
やりかたは簡単で、スナップショットをmvかcpで移すだけ。
問題なく成功しました。

#### 代替え案その2、iISCSIを使う。
TrueNas側でISCSI用のデータセットtestvol2を作成しISCSIサービスを
開始し設定をする。
そしてEsxiのGUI操作でISCSIデータセットアダプタに追加する。
その後、新しいデータストアにてパーティション作成する。
※この時、vmfsタイプを5にしないと再起動にパーティションが消えてしまいます。
※理由は不明です。

無事追加できましたら、(3)で編集したパスを変更する。
そして、シェルを実行すると見事99.9％の確率で成功します。
リストアもiSCSIに保存したデータからできます。
方法は後述します。

# Hyper-Vのバックアップ

基本的に下記サイトを参考にしています。
PowerShellでバックアップをとります。
https://wp.jisaba.life/2019/02/24/%E4%BB%AE%E6%83%B3%E3%83%9E%E3%82%B7%E3%83%B3%E3%81%AE%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88%E3%82%92%E8%87%AA%E5%8B%95%E5%8C%96%E3%81%99%E3%82%8B%E3%80%82/

### (1)iSCSIに頼る
大前提としてHyper-Vのエクスポートはネットワークドライブ上に指定すると失敗どころか、
開始すらされません。インポートも当然開始すらされません。
なので、最初にTドライブをマウントしましたが、バックアップには使いません。
ローカルに保存後Tドライブへ移動し、リストア時にまた移動だと時間があまりにもかかり、復旧が遅れます。

なのでEsxiと同様にISCSIを使います。
TrueNas側の設定は同じなので省略。
WindowsのコントロールパネルからiSCSIイニシエーターにてTrueNasで設定したターゲットを指定しデバイスを追加します。
その後追加したデバイスをIドライブにマウントします。
### (2) スクリプトを作る
上記サイトに従いスクリプトを作ります。
Cドライブ直下にDaili_VM_Export.ps1ファイルを作成し編集。
エクスポートする仮想マシン名とエクスポート先のパス指定、バックアップする世代数を編集します。

```
<###
    このスクリプトについて：
 
    仮想マシンをエクスポートする処理を行います。
    （併せて、古いエクスポートを削除します）
 
    このシェルはタスクスケジューラーによって毎日 AM 2:00 に起動されます。
 
 
    STEP1: 仮想マシンをエクスポート
    STEP2: 古いエクスポートを削除
###>
 
<### 
    【STEP 1】
    仮想マシンを下記パスにエクスポートする
        E:\Daily_ExportedVM\<日付>\
###>
 
### 日付の取得
$NowDate = Get-Date -Format "yyyyMMdd_HHmmss"
 
### エクスポート対象の仮想マシンの指定
$TragetVM01 = "windows1"
 
### エクスポート先のパスを指定
$ExportPath = "I:\Daily_ExportedVM\$NowDate\"
 
### Exportの成功 or 失敗の判断を STEP2(古いフォルダの削除）でするための変数($CheckCode)を定義
### 最初に0(=成功)を代入しておく。（失敗を検知したら 1 が代入される）
$CheckCode = 0
 
 
### コマンドエラー時に Try-Catch の Catch 部分が実行されるように、デフォルトパラメータを Stopに変更
$ErrorActionPreference = "stop"
 
 
### TargetVM01のエクスポート
### try-catch を使って、エラー発生時の処理を実装（catch内がエラー時のアクション）
Try {
    ### 仮想マシンのエクスポートを実行
    Export-VM -Name $TragetVM01 -Path $ExportPath -ErrorVariable ExportError01 |
    Out-Null   ### Out-Null することで Export-VM が終了するのを待つ。
 
### Export-VM がエラーになったら、エラー内容をファイルに出力して、$CheckCode に 1を代入
}Catch{
 
    ### エラーを　このファイルに出力→　E:\Daily_ExportedVM\ExportErrorCode-$TragetVM01-$NowDate.txt 
    echo $ExportError01 | 
    Out-File T:\Daily_ExportedVM\ExportErrorCode-$TragetVM01-$NowDate.txt
 
    ### エラーの場合は変数 'CheckCode'に 1 を代入（0以外の代入でSTEP2の際に中止ルーチンに移行）
    $CheckCode = 1
 
}
Out-Null
 
<### 
    【STEP 2】
    古いエクスポートの削除
    2世代(過去2日分)のこす　← HDDの使用状況を見ながら調整
 
    $DeleteTime で経過時間を指定。
    $DeleteTime = "-n" 　n(分/min)。　n分前を指定するので、マイナスをつける
    2日 = 2880min (60min * 24hour * 2day)
    3日 = 4320min (60min * 24hour * 3day)
    10日 = 14400min (60min * 24hour * 10day)
###>
#$DeleteTime = "-5"       ### 5分経過（テスト用）
$DeleteTime = "-4320"   ### 3日 ← 4320だと3日前のが残っちゃうので、4300 とかちょっと減らしておくのがいいかも。
 
 
### Export-VM が例外処理で停止していなければ（$CheckCode = 0）、古いフォルダの削除実施
if ( $CheckCode -eq 0 ) {
    ### 対象フォルダ配下のリストを取得
    Get-ChildItem I:\Daily_ExportedVM |
    ### 作成されてから $DeleteTime の値を超過したフォルダ・ファイルを選択/抽出
    Where-Object {
    $_.CreationTime -lt (Get-Date).AddMinutes($DeleteTime)
    } |
    ### 対象を絶対パスに変換し、削除
    foreach {
        Remove-Item -Recurse -Force $_.FullName
    }
### $Code != 0 であれば（1が代入されていれば）処理を中止。
} else {
    exit
}
```
実行する前にIドライブにDaily_ExportedVMというフォルダを作っておきましょう。

### (3)スクリプトを実行する
PowerShellなどで実行しましょう。
無事成功します。

## Esxiの自動バックアップ
Esxiにはcrontabはないので
```
/var/spool/cron/crontabs/root
```
を直接編集します。
その前に編集できるように権限を変更します。
```
chmod +w /var/spool/cron/crontabs/root
```
これで書き込みができるようになるので編集します。
バックアップのシェル実行時のコマンドをフルパスで書き込みます。
```
0 0 * * * /vmfs/volumes/datastore1/ghettoVCB-master/ghettoVCB.sh -f /vmfs/volumes/datastore1/ghettoVCB-master/vmlist.txt
```
ここでは0時にバックアップが作成されるように設定します。
また再起動した際にデータが初期化されるので、
/etc/rc.local.d/local.shに下記のように追記します。
```
vi /etc/rc.local.d/local.sh

cat<<_EOT_>>/var/spool/cron/crontabs/root
0 0 * * * /vmfs/volumes/datastore1/ghettoVCB-master/ghettoVCB.sh \
-f /vmfs/volumes/datastore1/ghettoVCB-master/vmlist.txt
_EOT_

/bin/kill $(cat /var/run/crond.pid)
/bin/crond

exit 0

```

## Hyper-Vの自動バックアップ

タスクスケジューラを使います。
タスクスケジューラにて任意の日程でDaili_VM_Export.ps1が実行されるように設定します。

## Esxiのリストア手順
### (1)リストア用のファイルを作成する。
リストア時に使うシェルはオプションでファイル指定する必要があります。
なのでそのファイルを作成する必要がありますがリストア時に作成しては遅いので、
cronでバックアップが作られる都度そのファイルも作成するようにします。
リストアファイルの形式は以下のようにします。
```
"<DIRECTORY or .TGZ>;<DATASTORE_TO_RESTORE_TO>;<DISK_FORMAT_TO_RESTORE>;<NEW_VM_NAME>"
```
このリストア元のフォルダが毎回日付と時刻によって変わるので世代分作成するようにします。
また仮想マシン分必要になります。
その前に必要なフォルダとファイルを作成しておきます。
```
mkdir /vmfs/volumes/datastore1/make_template
mkdir /vmfs/volumes/datastore1/restore_list
mkdir /vmfs/volumes/datastore1/restore_list/centos7
touch /vmfs/volumes/datastore1/restore_list/centos7/centos7_restore_1.txt
touch /vmfs/volumes/datastore1/restore_list/centos7/centos7_restore_2.txt
touch /vmfs/volumes/datastore1/restore_list/centos7/centos7_restore_3.txt
```
これを仮想マシン分行います。
仮想マシン名_restore_N.txtは世代分作ります。
```
vi /vmfs/volumes/datastore1/make_template/centos7_make_template.sh
#!/bin/sh

# スナップショットディレクトリとテンプレート出力先のディレクトリを設定
snapshot_dir="/vmfs/volumes/testvol2/EsxiSnapshot/centos7"
restore_list_dir="/vmfs/volumes/datastore1/restore_list/centos7"

# スナップショットのファイルリストを最新順で取得
snapshot_files=$(ls -1t "$snapshot_dir")

# すべてのスナップショットについてテンプレートを作成
index=1
for snapshot in $snapshot_files; do
  # テンプレートファイル名の設定
  template_file="$restore_list_dir/centos7_restore_$index.txt"

  # テンプレート内容の書き換え
  cat <<EOT > "$template_file"
#"<DIRECTORY or .TGZ>;<DATASTORE_TO_RESTORE_TO>;<DISK_FORMAT_TO_RESTORE>;<NEW_VM_NAME>"
/vmfs/volumes/testvol2/EsxiSnapshot/centos7/$snapshot;/vmfs/volumes/datastore1;3;centos7-restored
EOT

  # 次のインデックスへ
  index=$((index + 1))
done
```
こちらに記載している通り
/vmfs/volumes/datastore1/restore_list/centos7/centos7_restore_世代分.txt
ファイルに
/vmfs/volumes/testvol2/EsxiSnapshot/centos7
から取得した日付を元に
リストアファイルへ書き込んでいます。
3世代分あるので
```
centos7_restore_1.txt
centos7_restore_2.txt  
centos7_restore_3.txt
```
の中身が変わるようになります。
また、同じ仮想マシン名が2つ存在できないので
NEW_VM_NAMEの部分を
仮想マシン名-restoredにしています。
仮想マシン分同じことをやります。

### (2)cronに追加する
仮想マシン名_make_template.shをcronで毎日実行するようにします。
```
vi /var/spool/cron/crontabs/root
0 4 * * * /vmfs/volumes/datastore1/make_template/仮想マシン名_make_template.sh
```
時刻をバックアップ終了後に調整します。
仮想マシン分追加します。
また再起動した際にデータが初期化されるので、
/etc/rc.local.d/local.shに下記のように追記します。
```
vi /etc/rc.local.d/local.sh

cat<<_EOT_>>/var/spool/cron/crontabs/root
0 0 * * * /vmfs/volumes/datastore1/ghettoVCB-master/ghettoVCB.sh \
-f /vmfs/volumes/datastore1/ghettoVCB-master/vmlist.txt
* 4 * * * /vmfs/volumes/datastore1/make_template/仮想マシン名_make_template.sh
_EOT_

/bin/kill $(cat /var/run/crond.pid)
/bin/crond

exit 0

```

### (3)シェルでリストアする。
ghettoVCBにリストア用のシェルがあるので、特に編集せずそのまま実行します。

```
/vmfs/volume/datastore1/ghettoVCB-master/ghettoVCB-restore.sh -c /vmfs/volume/datastore1/restore_list/仮想マシン名_restore_N.txt
```
しばらく待つと仮想マシン一覧に仮想マシン名-restoredが追加されます。

## Hyper-Vのリストア手順
### (1) Hyper-Vマネージャーからリストア
Hyper-Vマネージャーの仮想マシンを新規作成からiSCSIに保存してあるvmdxファイルを選択して
新規で作成するとリストアされます。
Esxiと同じで同じ仮想マシン名は登録することはできないので、仮想マシン名-restoredなどで作成する。
しばらく待つと、Hyoer-Vマネージャーの仮想マシン一覧に仮想マシン名-restoredが追加されているはずです。

以上になります。

## 結論
TrueNasにエクスポートすることは失敗しましたが
iSCSIで何とか結果的には同じになるので良いのかなと思います。
TrueNas側からデータが見れないというデメリットはありますが...