VMWare ESXi 2台を vcenter で集中管理
2台の1Uサーバ(2CPUモデル)を、VMWareホストとして仕立てました。
VMWareホストに必要なのは、有り余るメモリと有り余るCPUパワーです。ディスク領域は原則としてNASからネットワーク経由で供給されるので、内蔵ディスクの役割は限られます。
起動ディスク・スワップ領域として、起動ディスクとなるSSDを利用します。
なお、最優先で稼働させたい仮想マシン1、2のためにも、内蔵SSDを割り当てます。
NFSの性能問題(ESXiとの相性)
仮想マシンのディスクとして、FreeNASが公開するNFSストレージを割り当てたところ、極めて劣悪なディスクIO性能に悩まされました。データのコピーがとにかく遅いのです。
これは、ESXiサーバがNFSストレージに対する書き込み要求を、毎回同期要求とともに行うためです。
FreeNAS側でZFSプールの属性に、「sync=standard」となっていると遅いのですが、これを「sync=disabled」に変更することで、劇的に(10倍から100倍に)高速化できることがわかりました。
そこで、私たちはFreeNAS上で下記のように設定しました。
zfs set sync=disabled POOL1
zfs set atime=off POOL1
sync=disabled の状態では、書き込みの最中に電源切断などが発生すると、データが破損するようになります。私たちは、データ破損のリスクをあえて冒しても、10倍から100倍の高速化のメリットがそれを上回ると考えて、確信犯でこの設定を行っています。
それでも通常、5秒に一回はデータがディスクに書き出されていることもわかりました。
電源切断などが発生した場合、おそらく単純に5秒前の状態に戻る、というのが一番ありえる症状ですが、最悪の場合にはZFSファイルシステムが破損して読み出しができなくなります。
そこで、FreeNASの機能で、30分おきにZFSプールのスナップショップをとるように設定し、3日間保持することとしました。こうすると、ZFSが破損してしまった最悪のケースでも、最後のスナップショットを復元することで、最悪でも30分前の状態に復元することができるのです。
vcenterの導入
VMWare ESXi をそれぞれにインストールした上で、2台のESXiホストをWebブラウザで集中管理するために、vcenterサーバを導入しました。vcenterサーバを稼働させるために、有償のVMWare Essentials ライセンスを購入しています。
1か所で2台のESXiホスト上のすべての仮想マシンを管理できることは、もちろんvcenterのメリットです。
しかし、それだけが決め手ではありません。
vcenterを用いることで、仮想マシンのスナップショット作成や、仮想マシンの複製が可能になります。この「仮想マシンの複製」を応用して、仮想マシンのバックアップ運用の自動化ができるのです。
vcenterサーバのバックアップ
vcenterサーバは、ESXiホスト上の仮想マシンアプライアンスとして用意され、通常はどちらか一方のESXiホスト上で稼働します。この仮想マシンの複製を作成して、もう一方のESXiホスト上に置いておけば、万が一ホストマシンに障害が起きた場合でも、無事だったもう一方のESXiホスト上のvcenterサーバを起動すれば大丈夫です。
仮想マシンのバックアップ自動運用
VMWare 仮想マシンのバックアップをどう運用するか、エンタープライズ環境の場合には、Veeam Bacupなどのサードパーティ製のバックアップソリューションを購入するのが一般的ではないかと思います。
貧しいながらも技術で勝負する私たちとしては、多少面倒でも安い方法が無いかと探してみた結果、よい方法を見つけました。
PowerCLI を使う
PowerCLIとは、VMWareが無償で提供するWindowsPowerShell向けのライブラリであり、vcenter API をPowerShellスクリプトで利用可能にするものです。
https://code.vmware.com/web/tool/vmware-powercli
これを使って、vcenterサーバにログインし、仮想マシンのスナップショットを作成し、スナップショットをコピーするが可能になります。ESXiホストに接続された二つのNFSボリュームにまたがるコピーができることによって、いずれか一方のNASサーバに障害が発生したときにも、生き残った方で運用を継続できることが可能になります。
NFSを使うもう一つの理由
ESXiホストにまたがる仮想マシンのコピーは、”VMotion" と呼ばれていて、VMWare Essentials Kitのライセンスでは実行ができません。(VMWare Essentials「Plus」が必要です)
しかし、NFSに仮想マシンイメージをバックアップしておけば、このNFSボリュームは両方のESXiホストから参照できます。コピー元と異なるESXiホストから参照できてしまうということは、実質的にはESXiホストにまたがった仮想マシンのコピーができたようなものです。
いずれか一方のESXiサーバに障害が発生したときは、生き残った方のESXiホストからNFSボリューム上のバックアップ仮想マシンイメージを参照してマウントすれば稼働させることができるのです。
バックアップスクリプト実行を定期実行
PowerCLIは、今回の仮想インフラとは別に、弊社の別サーバ上にインストールして、バックアップジョブを定期実行するようにタスクスケジューラに登録しました。
苦労した点は下記となります。
-
タスクスケジューラからPowerCLIスクリプトを実行したとき、vcenterに自動ログイン できるようにするのに苦労しました。
-
あらかじめ下記コマンドでVMWareのCredential Storeにログオン情報を保存しておきます。
New-VICredentialStoreItem -Host "vcenter.teppi.net" -User "administrator@vsphere.local" -Password "password"
C:\Users\Administrator.TEPPI\AppData\Roaming\VMWare\credstore\vicredstore.xml
これを使うには、タスクスケジューラでPowerCLIスクリプトを(ユーザがログインしているかどうかにかかわらず、Administrator権限、かつ最上位特権付き)で実行せねばなりません。
稼働イメージ
たとえば、ESXiホスト olive.teppi.net 上では avocado2/avocado3/avocado5/papaya3/support_private/vcenter の 6台の仮想マシンを常時稼働させています。
zz_avocado2-02-201909250241 は、9月25日2時41分に取得したavocado2のスナップショットのバックアップで、世代番号02です、各サーバについて、バックアップは最大3世代まで取るようになっていて、世代番号は 01->02->03->04->01.... とローテーションしつつ、次回取得分世代のファイルは欠番となるようにしています。
参考(バックアップスクリプト)
ー動作保証一切無し!動かす前に自己責任でよく読んで理解してください。
Function Teppi-Backup-VM{ Param( [Parameter(Mandatory=$true)][string]$vmNameKey, [Parameter(Mandatory=$false)][int]$maxBackupIdx = 3, [string]$backupDataStoreName = "NFS_durian" ) #end Param $clonePrefix="zz_" $hostnamePattern = $vmNameKey+"*" # Connect-VIServer が成功するためには、あらかじめ # New-VICredentialStoreItem -Host "vcenter.teppi.net" -User "administrator@vsphere.local" # -Password "password" # のようなコマンドラインを実行して # VMWareのCredentialStoreファイル(たとえばTEPPI\Administratorの場合) # C:\Users\Administrator.TEPPI\AppData\Roaming\VMWare\credstore\vicredstore.xml # に暗号化した情報を書き込んでおきます。 # タスクスケジューラから呼び出す場合、スクリプトの実行アカウントを一致させる必要があることに注意! $dummy = Connect-VIServer -Server "vcenter.teppi.net" try { $vm = Get-VM |Where-Object {$_.Name -like "$hostnamePattern*" } If ($vm.Count -ne 1){ $msg = "hostname("+$hostnamePattern+") not unique." Write-Error -Message $msg -Category:ResourceUnavailable -ErrorId:9 # Teppi-SendStartMail $vmNameKey $clonePrefix $msg $backupDataStoreName }else{ $vmName = $vm.Name Write-Output "-- Creating backup of $vmName" # 削除する旧クローンの世代番号と、次に取得する世代番号を決定する $currentCloneIdx = 0 For ($i=1; $i -le $maxBackupIdx; $i++){ $cloneName = GetBackupVMName $clonePrefix $i $vmNameKey $cloneVm = Get-VM | Where-Object {$_.Name -like "$cloneName*"} $cloneAlreadyExists = ($cloneVm.Count -ge 1) # Write-Output $cloneName $cloneAlreadyExists.ToString() If ($cloneAlreadyExists -eq $false) { $currentCloneIdx = $i # 最初の欠番を見つける Break } } If ($currentCloneIdx -le 1){ $currentCloneIdx = 1 $prevCloneIdx = 2 }Else{ If ($currentCloneIdx -eq $maxBackupIdx){ $prevCloneIdx = 1 }else{ $prevCloneIdx = $currentCloneIdx+1 } } Write-Output "-- Current:$currentCloneIdx, Prev:$prevCloneIdx, Max:$maxBackupIdx" # スナップショットを作る $cloneSnap = $vm | New-SnapShot -Name "Clone Snapshot" # Teppi-SendStartMail $vmNameKey $clonePrefix $vm.Name $backupDataStoreName $vmView = $vm | Get-View # Build clone specification $cloneSpec = new-object Vmware.Vim.VirtualMachineCloneSpec $cloneSpec.Snapshot = $vmView.Snapshot.CurrentSnapshot # Make linked disk specification $cloneSpec.Location = new-object Vmware.Vim.VirtualMachineRelocateSpec $cloneSpec.Location.Datastore = (Get-Datastore -Name $backupDataStoreName | Get-View).MoRef $cloneSpec.Location.Transform = [Vmware.Vim.VirtualMachineRelocateTransformation]::sparse $cloneBaseName = GetBackupVMName $clonePrefix $currentCloneIdx $vmNameKey $datetime = Get-Date -Format "yyyyMMddHHmm" $cloneName = "$cloneBaseName-$datetime" Write-Output "-- Creating new clone: $cloneName" $oldCloneVM = Get-VM | Where-Object { $_.Name -like "$cloneBaseName*" } If ($oldCloneVM.Count -eq 1) { #if $cloneVM already exists, delete it $oldCloneVMName = $oldCloneVM.Name $oldCloneVM | Remove-VM -DeletePermanently -Confirm:$false Write-Output "-- Deleted existing clone: $oldCloneVMName" } $vmView.CloneVM( $vmView.parent, $cloneName, $cloneSpec ) # move clone VM to backup folder in the VM Inventory $cloneFolder = Get-Folder -Type VM -Name $cloneFolderName | Get-View | Get-VIObjectByVIView Move-VM $cloneName -InventoryLocation $cloneFolder # Remove Snapshot created for clone Get-Snapshot -VM (Get-VM -Name $hostnamePattern) -Name $cloneSnap | Remove-Snapshot -confirm:$False $prevCloneName = GetBackupVMName $clonePrefix $prevCloneIdx $vmNameKey $prevCloneVM = Get-VM | Where-Object { $_.Name -like "$prevCloneName*" } If ($prevCloneVM.Count -eq 1) { #Delete old generation backup clone VM $prevCloneVMName = $prevCloneVM.Name $prevCloneVM | Remove-VM -DeletePermanently -Confirm:$false Write-Output "-- Deleted existing old clone: $prevCloneVMName" } #Teppi-SendCompletedMail $vmNameKey $cloneName $backupDataStoreName } }finally{ Disconnect-VIServer -Server vcenter.teppi.net -Confirm:$false } } #How to use #下記のように使う #Teppi-Backup-VM "okn_demo02" "zz1_" "mangoNFS"