hidemium's blog

日々学んだことをアウトプットする。

HomelabのvSphere 7.0からvSphere 8.0へのアップグレード

以前、Homelabの構成について書いていました。なかなかHomelabのvSphere環境のアップグレードが出来ずにいましたが、vSphere8.0のUpdateも上がってきたこともありアップグレードについて書いてみようと思います。

hidemium.hatenablog.com

構成

  • vCenter 7.0U3→vCenter 8.0U2
  • ESXi 7.0U3→ESXi 8.0U2
    • USB Network Native Driver for ESXi v1.10→v1.13
  • Intel NUC NUC7i3BNH
    • Intel(R) Core(TM) i3-7100U CPU @ 2.40GHz

vCenterアップグレード

お手軽にアップグレードするために、WindowsGUI版のインストーラーでアップグレードを行いました。

  • インストーラーを起動して、Upgradeを選択します。
  • アップグレード対象の必要なパラメーターを入力していきます。
  • deployment sizeはhomelab用なので、Tinyを選択します。
  • 入力後、Finishボタンを押すと、Upgrade - Stage 1: Deploy vCenter Serverのポップアップが表示され、VCSAのVMのデプロイが開始されます。
  • デプロイ後、Step2を選択して、Nextをクリックします。
  • Pre-upgradeのチェックが行われ、いくつか注意事項が表示されます。
  • upgrade dataを選択します。homelabなので、タスクやイベントやパフォーマンスデータは移行せず、最低限のデータだけ移行します。
  • Step2が完了すると、アップグレードが完了します。
  • vSphere Clinetにログインし、アップグレードが完了したことを確認します。

ESXiアップグレード(workload側)

workload側は、vCenterで管理されたESXiで、vCenter VMは稼働していないホストになります。Lifecycle Managerを使ってアップグレードを行ってみました。

ESXiのアップグレードを行う前に、Nested ESXiで素振りを行ってみます。

  • Nested ESXiをデプロイし、物理ESXiとは別のクラスタを用意し、そのクラスタに配置します。
  • Nested ESXi用のクラスタで、イメージの管理を行い、ESXi 8.0U2をクラスタのイメージとして設定します。
  • すべて修正をクリックし、修正の開始をクリックし、アップグレードを開始します。
  • 自動的にメンテナンスモードに切り替え、アップグレードが行われます。
  • イメージ プロファイルは、(Updated) VMware Lifecycle Manager Generated Imageと表示されるようになっていました。

Product Interoperability Matrixを確認すると、vCenter 8.0U2ではESXi 8.0U3をサポートしていないため、同じマイナーマージョンを指定しました。

USB NICを利用しているため、FlingsのUSB Network Native Driver for ESXiのドライバを追加します。

以前だと、PowerCLIを使ってイメージを作成していましたが、UIでも同様の操作ができそうなので、試してみました。

  • VMware USB NIC Fling Driver for ESXi v1.13のバンドルイメージを入手しておきます。
  • Lifecycle Managerの画面で、アクション>更新のインポートをクリックし、USB Network Native Driver for ESXiのバンドルイメージを指定します。
  • Nested ESXi用のクラスタで、イメージの編集で、コンポーネントの追加で、VMware USB NIC Fling Driverを確認し、バージョンを指定して保存します。
  • すべて修正をクリックし、修正の開始をクリックし、アップグレードを開始します。
  • 自動的にメンテナンスモードに切り替え、アップグレードが行われます。
  • アップデート後のパッケージを確認すると、以下のようなバージョンとなっていました。
名前 説明 バージョン
vmkusb USB Native Driver for VMware 0.1-18vmw.802.0.0.22380479
vmkusb-nic-fling USB Native Driver Fling for VMware ESXi 1.12-2vmw.802.0.0.67561870

物理ESXiで上記と同様にアップグレードを実施し、うまくアップグレードすることができました。

ESXiアップグレード(management側)

management側は、vCenter VMが稼働しており、homelabのため、vMotionで他のホストにvCenter VMを移行できない状態となっています。

そこで、CLIによるアップグレードを実施します。

workload側とイメージを合わせるため、workload側のLifecycle Managerからバンドルイメージをダウンロードしておきます。

  • イメージ>エクスポートをクリックし、zipを選択し、バンドルイメージをダウンロードします。
  • management側のESXiのデータストアを選択し、データストアブラウザからデータストアへバンドルイメージをアップロードします。
  • まずは、dry-runを実行して、アップグレードか可能か確認すると、以下のようなエラーが表示されます。
esxcli software sources profile list -d /vmfs/volumes/datastore1/ISO/OFFLINE_BUNDLE_52e37739-9a0d-8d0f-1525-cd1365a880be.zip 
Name                                      Vendor        Acceptance Level  Creation Time        Modification Time
----------------------------------------  ------------  ----------------  -------------------  -----------------
VMware Lifecycle Manager Generated Image  VMware, Inc.  PartnerSupported  2024-08-15T20:43:12  2024-08-15T20:43:12
esxcli software profile update -d /vmfs/volumes/datastore1/ISO/OFFLINE_BUNDLE_52e37739-9a0d-8d0f-1525-cd1365a880be.zip -p "VMware Lifecycle Mana
ger Generated Image" --dry-run
 [ProfileValidationError]
 File path of '/lib64/python3.8/unittest/loader.pyc' is claimed by multiple non-overlay VIBs: {'VMware_bootbank_esxio-base_8.0.2-0.40.23825572', 'VMware_bootbank_esx-base_8.0.2-0.40.23825572'}

VMware docsにアップグレードする際に、dry-runが動作しないという記載がありました。

dry-runでの確認はできないですが、直接CLIでESXiのアップグレードを行っていきます。

  • vCenter VMを停止します。
  • ESXiをメンテナンスモードに変更します。
  • 以下のコマンドを実行します。vLCMでのアップグレードはprofile update相当なようなので合わせておきます。
esxcli software profile update -d /vmfs/volumes/datastore1/ISO/OFFLINE_BUNDLE_52e37739-9a0d-8d0f-1525-cd1365a880be.zip -p "VMware Lifecycle Mana
ger Generated Image"
Installation Result
   Message: The update completed successfully, but the system needs to be rebooted for the changes to be effective.
   Reboot Required: true
  • ESXiを再起動します。

vSphere Clientにログインし、ESXiのバージョンが8.0に変更されていることを確認します。

  • vCenter VMを起動します。
  • USB NICにvDSを接続している場合、ESXiのアップグレード後にアップリンクが外れることがあるため、アップリンクが外れた場合は、再度接続しておきます。

vDSアップグレード

vDSを7.0.3から8.0.0にアップグレードします。

  • vDSのアクションをクリックして、アップグレード>Distributed Switch のアップグレードをクリックします。
  • 8.0.0 - ESXi 8.0 以降を選択し、互換性に問題をないことを確認し、OKをクリックします。
  • vDSのバージョンが8.0.0に変更していることを確認ます。

まとめ

vSphere 7.0までだと、PowerCLIを使って、インストールイメージをビルドして、USBメモリにブートメディアを用意していましたが、vSphere 8.0ではほぼGUIの操作のみでアップグレードができるようになっており、homelabのアップグレードがとても楽になっていました。

  • Image BuilderやPowerCLIを使わずに、Lifecycle Managerでvibを追加してインストールイメージのビルドが可能に
  • Lifecycle ManagerでVMware USB NIC Fling Driver for ESXiの追加が可能
  • ESXi 7.0からESXi 8.0へのアップグレードの場合、esxcliでdry-runが動作せず
  • vSphere 8.0からvCenterはESXiより低いマイナーバージョンはサポート外に(vCenter 8.0U2+ESXi 8.0U3の組み合わせがサポート外に)

ESXiのCPUスケジューリングについてまとめてみた

vSphere環境を使っていて、仮想マシンのvCPUのサイジングについてベストプラクティスがいくつかありますが、そういう背景がどういったところにあるのか、ベストプラクティスから外れた場合にどうなるのか書いてみようと思います。

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Intel NUC NUC7i3BNH
    • Intel(R) Core(TM) i3-7100U CPU @ 2.40GHz
  • Ubuntu 22.04

よくありそうな疑問

仮想マシンのvCPUのサイジングとして、以下のものがあります。

  • 一般的なベストプラクティスとして、ハイパースレッディングが有効な環境で仮想マシンのvCPU数は物理コア数を超えないようにする

vSphereの設定として、仮想マシンのvCPUは、ハイパースレッディングが有効な場合、仮想マシンのvCPU数は論理コア数まで設定することが可能です。例えば、物理コアが24コアあった場合は、48vCPUまで設定することが可能です。

仮想マシンのvCPUを物理コアを超えた場合にどのようなことが起きるのか見ていこうと思います。

CPUスケジューリングについて

仮想マシンのvCPUがどの物理コアに割り当てされるかは、CPUスケジューラーが担っています。

複数vCPUを持った仮想マシン(SMP VM)の場合、仮想マシン内で実行されているゲスト OS とアプリケーションを、専用の物理マルチプロセッサ上で実行されているかのように見せており、ESXiが同期ケジューリングをサポートすることで実装しています。

ゲストOSは管理するすべてのプロセッサがほぼ同じ速度で実行されることを前提としています。ただし、仮想化環境では、ゲストOSによって管理されるプロセッサは、実際にはハイパーバイザーによってスケジュールされた仮想CPUであるため、複数の仮想マシン間で物理プロセッサをタイム スライスして利用しています。

Strict co-scheduling

ESX 2.xで実装されたアルゴリズムで、vCPUごとににスキューと呼ばれるずれが発生すると、さらなるスキューを防ぐために、同じ仮想マシン内のすべてのvCPUが強制的にスケジュール解除 (「同時停止」(co-stop)) されます。スキューが検出された後はすべての vCPUを同時にスケジュールする必要があるため、このアプローチは厳密な同期ケジューリングと呼ばれています。

Relaxed co-scheduling

ESX 3.xからStrict co-schedulingに代わって、Relaxed co-schedulingというアルゴリズムが実装されました.

すべてのvCPUに同期を求めるのではなく、処理を実行しているvCPUのみについてのみ同期を行う仕組みに変更されています。

ハイパースレッディングについて

ハイパースレッディングは、1つのプロセッサ上で2つスレッドからの命令を同時に実行できるようにすることで、物理的なCPU性能を向上させる技術です。

この技術により、スレッドレベルの並列処理により性能を向上させることができますが、計算リソースの総量は依然として1つの物理プロセッサが上限となります。

どの程度性能が向上するかは、処理内容によって変わり、性能が2倍になるわけではなく、1.2倍程度といわれています。

ハイパースレッディングによりアイドル リソースを有効に活用することによってパフォーマンスを向上でき、特定の重要なワークロード タイプについてスループットを向上させます。

ESXi ホストは、プロセッサ時間をインテリジェントに管理して、システム内のプロセッサ コア間で負荷が円滑に分散されるように保証します。CPU 0 と 1 はともに第 1 のコア上にあり、CPU 2 と 3 は第 2 のコア上にあるといったように、同じコア上の論理プロセッサは、連続した CPU 番号を持ちます。

仮想マシンは、同じコアの 2 つの論理プロセッサ上よりも、2 つの異なるコア上に優先的にスケジュール設定されます。

仮想マシンのvCPUを物理コアと一致した場合、物理コアを超えた場合の挙動

物理コアが2コア、ハイパースレッディングオンで4スレッドのサーバーで、仮想マシンAにはvCPU 2、仮想マシンBにはvCPU4を割り当てて、負荷ツールを実行して、挙動を確認してみます。

stressコマンド

まずは、stressコマンドですべてのvCPUに負荷をかけてみます。

2vCPUの場合、CPU使用率が高いコアはCPU番号がずれているため、2つの異なる物理コアでスケジューリングされていることが分かります。

4vCPUの場合、各コアの使用率は50%で止まっていることが分かります。

仮想マシン 物理ホストのクロック数(Mhz) 物理ホストのCPU使用率 CPU 0使用率(Core 1) CPU 1使用率(Core 1) CPU 2使用率(Core 2) CPU 3使用率(Core 2)
仮想マシンA(vCPU2) 4.7 100% 98% 2% 98% 2%
仮想マシンB(vCPU4) 4.7 100% 50% 50% 50% 50%

仮想マシンの観点で見ると、2vCPUの場合は、各コアのクロック数は物理クロック数と一致していますが、4vCPUの場合、各コアのクロック数は物理クロック数より小さい値になっていることが分かります。

4vCPUでは、仮想マシン全体のクロック数は、物理ホストのクロック数を超えて表示されていますが、実際の物理ホストのクロック数は変わらない状態となっています。

また、4vCPUでは、仮想マシンのCPU使用率は61%で止まっていることが分かります。

仮想マシン 仮想マシンのCPU使用率 仮想マシンのクロック数(Mhz) vCPU 0クロック数(Mhz) vCPU 1クロック数(Mhz) vCPU 2クロック数(Mhz) vCPU 3クロック数(Mhz)
仮想マシンA(vCPU2) 98% 4.7 2.35 - 2.35 -
仮想マシンB(vCPU4) 61% 5.9 1.47 1.47 1.47 1.47

pgbench

DBのベンチマークツールを実行して処理性能の比較をしてみようと思います。

PostgreSQLをインストールし、ベンチマークで使うでDBの作成とデータセットの初期化を行います。

$ sudo install postgresql postgresql-client postgresql-contrib
$ sudo -u postgres createdb test
$ sudo -u postgres pgbench -i -s 10 test

vCPU2とvCPU4の仮想マシンごとにベンチマークの取得を行います。

$ sudo -u postgres pgbench -c 16 -t 1000 test

各vCPUごとにベンチマークを取得してみましたが、vCPU4のほうが高い状態となりました。

これは、ハイパースレッディングにより、各コアでの性能が十分になくても、並列処理を行う場合は、パフォーマンスが出やすいことを示していそうです。

仮想マシン TPS
仮想マシンA(vCPU2) 2288
仮想マシンB(vCPU4) 3248

まとめ

試してみた内容についてまとめると以下のようなことになりそうです。

  • ハイパースレッディングが有効な環境で仮想マシンのvCPU数は物理コア数を超えると、物理ホストのCPU使用率は100%で頭打ちとなる
  • ハイパースレッディングが有効な環境で仮想マシンのvCPU数は物理コア数を超えると、並列処理を行う場合は、パフォーマンスを出しやすい
  • 仮想マシンが複数台あると、co-stopが発生しやすくなるため、物理ホスト上の仮想マシンは少なくしたほうがよいかもしれない

参考

govcを使ったvmdkイメージの移行

前回、govcを使ったフルクローンによるバックアップについて書きましたが、今回はgovcを使ってvmdkイメージの移行について試してみました。

hidemium.hatenablog.com

構成

vmdkイメージのダウンロード

今回の操作は、govcを使って仮想マシンのvmdkイメージを手元にダウンロードして、vmdkイメージをアップロードして別のVMとして作成します。

ovfファイルとしてダウンロードする方法が一番シンプルですが、動作を確認するためにあえて回り道をしつつ見ていきます。

govcのインストールと環境変数は前回と同様です。

govcでvmdkイメージをダウンロードするには以下のように実行します。

$ govc datastore.download -host hostname  -ds datastore "vm_name/vm_name-flat.vmdk" ./vm_name-flat.vmdk

ここでflat.vmdkファイルをしているのには理由があります。

データストアブラウザからだと、vmdkファイルしか見えませんが、ESXiにログインし、データストア内を見ると、flat.vmdkとvmdkファイルが存在することが分かります。

ls -lh
-rw-------    1 root     root       10.0G Jan 24 21:25 vm_name-flat.vmdk
-rw-------    1 root     root         527 Jan 24 21:27 vm_name.vmdk

xxx-flat.vmdkというファイルがダウンロードされますが、ファイルサイズがESXi内で見えるプロビジョニングサイズと同じサイズとなっていることが分かります。

flat.vmdkは、仮想マシンのデータ ディスクになります。実際のゲストOSのデータはこちらに格納されています。

vmdkは、ディスク記述子ファイルになります。関連するflat.vmdkに関する情報が記述されています。

VMDKフォーマットには複数の異なるサブフォーマットがあり、メタデータを外部記述子ファイルに保存するものと、メインデータとともに 1 つのファイルに埋め込むものがあります。

フラットイメージは事前にスペースを割り当てますが、スパースイメージは仮想マシンが書き込むにつれて増大するかたちになります。

次にgovcでflat.vmdkファイルをアップロードします。

$ govc import.vmdk -ds datastore -pool cluster-name/Resources ./vm_name-flat.vmdk new_vm_name
govc: vmdk: invalid format (must be streamOptimized)
The vmdk can be converted using one of:
  vmware-vdiskmanager -t 5 -r './vm_name-flat.vmdk' new.vmdk
  qemu-img convert -O vmdk -o subformat=streamOptimized './vm_name-flat.vmdk' new.vmdk

そうすると、streamOptimizedされたvmdkのフォーマットではないとアップロードできないと怒られます。

メッセージの通り、subformatにstreamOptimizedを指定してvmdkファイルを変換します。

$ qemu-img convert -O vmdk -o subformat=streamOptimized './vm_name-flat.vmdk' disk1.vmdk
$ ls -lh
-rw-r--r-- 1 user user 241M Jan 24 05:57 disk1.vmdk
-rw-rw-r-- 1 user user  10G Jan 23 09:31 vm_name-flat.vmdk

vmdkファイルのサイズがもともとはプロビジョニングサイズと同じサイズでしたが、変換することで実用量のサイズに変換されました。

streamOptimizedについては、こちらに記載がありました。

govcを使って、変換したvmdkファイルをアップロードします。

govcの引数には、今後作成するVM名と同じデータストア内のフォルダ名を指定しておきます。

$ govc import.vmdk -ds datastore -pool cluster-name/Resources ./disk1.vmdk new_vm_name

アップロードしたvmdkファイルを指定して、VMを作成します。

$ govc vm.create -m 2048 -c 2 -g ubuntu64Guest -host hostname -ds datastore -net network -on=false -disk="new_vm_name/disk1.vmdk" new_vm_name

vmdkをダウンロードする際に、flat.vmdkのファイルサイズでダウンロードされるため、事前にファイルサイズを小さくするには、ovfとしてダウンロードするのが一番シンプルそうではあります。

手元にvmdkファイルがあり、それを利用したい場合は、今回の方法が利用できるかもしれません。

govcを使った仮想マシンのバックアップ

前回、スナップショット技術やバックアップについて書きましたが、今回はgovcを使ってバックアップの取得について試してみました。

hidemium.hatenablog.com

構成

バックアップ

今回の操作は、govcを使って仮想マシンのクローンを用いたフルバックアップになります。

以下の手順でgovcのインストールを行います。また、合わせて環境変数を設定しておきます。

$ curl -L -o - "https://github.com/vmware/govmomi/releases/latest/download/govc_$(uname -s)_$(uname -m).tar.gz" | sudo tar -C /usr/local/bin -xvzf - govc
$ export GOVC_URL=https://<vcenter fqdn>/sdk
$ export GOVC_USERNAME="username"
$ export GOVC_PASSWORD="password"
$ export GOVC_INSECURE="1"
$ export VM="/Datacenter/vm/path/to/vm_name"
$ govc vm.info "${VM}"
Name:              vm_name
  Path:            /Datacenter/vm/path/to/vm_name
  Guest name:      Ubuntu Linux (64-bit)
  Memory:          2048MB
  CPU:             1 vCPU(s)
  Power state:     poweredOn
  Boot time:       
  IP address:   
  Host:            hostname

govcで同一のデータストアにクローンを行うには以下のように実行します。

$ govc vm.clone -vm $VM -on=false -host hostname -folder "/Datacenter/vm/path/to/folder" clone_vm_name
Cloning /Datacenter/vm/path/to/vm_name to /Datacenter/vm/path/to/folder/clone_vm_name...OK

電源オン状態でクローンをすると、スナップショットを自動で取得を行ったうえで、クローンを行ってくれます。

clone-temp-xxx という名称のスナップショットが自動で作成され、ゲストファイルシステムを静止するがオンになっており、ファイルシステムコンシステントとアプリケーションコンシステントに該当しそうです。

govcの -on オプションを false に指定することで、クローン先のVMは電源オフ状態のままとなります。

次に、govcで異なるデータストアにクローンを行うには以下のように実行します。クローン先のデータストアが異なるため、こちらはバックアップ相当になると思います。

$ govc vm.clone -vm $VM -on=false -host hostname -ds dest_datastore -folder "/Datacenter/vm/path/to/folder" clone_vm_name
Cloning /Datacenter/vm/path/to/vm_name to clone_vm_name...OK

次に、仮想マシンをダウンロードして、他の領域へ転送する想定でovaファイルをダウンロードします。

export.ovf を実行すると、ovfファイルとvmdkファイルがダウンロードされます。

$ govc export.ovf -vm $VM /path/to/folder/
Downloading vm_name-disk-0.vmdk... OK
$ ls -la /path/to/folder/vm_name
vm_name-disk-0.vmdk
vm_name.ovf

似たコマンドで datastore.download がありますが、こちらではvmdkの仮想ディスクの記述子データしかダウンロードされず、仮想ディスクの実際のデータは含まれません。

$ govc datastore.download -ds datastore folder_name/vm_name.vmdk vm_name.vmdk
$ cat vm_name.vmdk
# Disk DescriptorFile
version=1
encoding="UTF-8"
:

スナップショットを指定したバックアップ

以下のようにすると、指定したスナップショットからクローンを作成することができます。

$ govc snapshot.create -vm $VM snapshot_name
$ govc vm.clone -vm $VM -snapshot snapshot_name -on=false -host hostname -ds dest_datastore -folder "/Datacenter/vm/path/to/folder" clone_vm_name
Cloning /Datacenter/vm/path/to/vm_name to clone_vm_name...OK
$ govc snapshot.remove -vm $VM snapshot_name

バックアップではないですが、以下のようにするとリンククローンを作成することができます。

$ govc vm.clone -vm $VM -snapshot snapshot_name -link -on=false -host hostname -ds dest_datastore -folder "/Datacenter/vm/path/to/folder" clone_vm_name
Cloning /Datacenter/vm/path/to/vm_name to clone_vm_name...OK

リストア

フルクローンを使ったバックアップのため、リストアは指定したデータストアに対してもう一度クローンを実行するかたちで実施できます。

$ govc vm.clone -vm clone_vm_name -on=false -host hostname -ds dest_datastore -folder "/Datacenter/vm/path/to/folder" restore_vm_name
Cloning /Datacenter/vm/path/to/clone_vm_name to restore_vm_name...OK

ovfファイルからのリストアは、以下のように行います。

$ govc import.ovf -host hostname -ds datastore /path/to/folder/vm_name.ovf
Uploading vm_name-disk-0.vmdk... OK

First Class Disk (FCD)

govcコマンドを触っていると、First Class Disk (FCD) 関連のものがあることに気が付きます。

$ govc disk
  disk.create
  disk.ls
  disk.register
  disk.rm
  disk.snapshot.create
  disk.snapshot.ls
  disk.snapshot.rm
  disk.tags.attach
  disk.tags.detach

FCDのディスクを作成してみます。

$ govc disk.create -ds datastore -size 10G my-disk
Creating my-disk...OK
7584edf7-6bbd-4435-9762-609d32564d28
$ govc disk.ls -ds datastore  -l
7584edf7-6bbd-4435-9762-609d32564d28  my-disk  10.0G

試しに、スナップショットを作成してみます。

$ govc disk.snapshot.create -ds datastore 7584edf7-6bbd-4435-9762-609d32564d28 my-disk-snapshot
Snapshot 7584edf7-6bbd-4435-9762-609d32564d28...OK
d86d3465-8fa5-40c2-ac02-44ee3d15809b
$ govc disk.snapshot.ls -ds datastore 7584edf7-6bbd-4435-9762-609d32564d28
d86d3465-8fa5-40c2-ac02-44ee3d15809b  my-disk-snapshot

スナップショットからの切り戻しするサブコマンドは存在しなさそうでした。

後片付けでFCDのスナップショットとディスクを削除しておきます。

$ govc disk.snapshot.rm -ds datastore 7584edf7-6bbd-4435-9762-609d32564d28 d86d3465-8fa5-40c2-ac02-44ee3d15809b
Deleting d86d3465-8fa5-40c2-ac02-44ee3d15809b...OK
$ govc disk.rm -ds datastore 7584edf7-6bbd-4435-9762-609d32564d28
Deleting 7584edf7-6bbd-4435-9762-609d32564d28...OK

これらのコマンドの用途としては、CNSで作成した仮想ディスクのトラブルシュート周りで役に立ちそうなように見えています。

https://blogs.vmware.com/affiliates/vmware-cloud-native-storage-be-aware-of-dangling-virtual-disks-and-snapshots

スナップショット技術とVMwareのスナップショットとバックアップについて

コピーオンライト(Copy-On-Write)といったスナップショット技術とVMwareのスナップショットやバックアップについて整理をしてみたいと思います。

スナップショット技術

コピーオンライト(Copy-On-Write)スナップショット

CoWは、スナップショットデータ用に専用のストレージ領域が予約されます。

このストレージ領域にメタデータと元のデータブロックに変更が発生するたびに元データのコピーが作成されます。

ブロックを上書きする前に、その前の値を読み取って別の場所に書き込んでから、新しいデータを書き込む必要があり、書き込みごとに3つのIO操作(読み取り1回と書き込み2回)が使用されます。

このアプローチにはいくつか利点があり、新しいコピーが正常に書き込まれるまで、元のデータはそのまま残るため、データ破損のリスクが最小限に抑えられます。

リダイレクトオンライト (Redirect-on-Write) スナップショット

RoWは、更新時に元のデータをスナップショット領域にコピーはせずに、ブロックへの変更はブロックに関連図けられたポインタを別のブロックにリダイレクトし、そこにデータを書き込みます。

特定のスナップショットにアクセスしようとする場合、ブロック位置に対応するポインタを利用して、別の場所からブロックを取得します。

RoWでは、保護されたブロックを変更する際にIO操作がCoWに比べると書き込み1回に削減できます。

スプリットミラー スナップショット

スナップショットを作成中に同一のコピーを作成します。データコピー操作により、スナップショットの作成時に2倍のストレージ容量が消費され、時間がかかる場合があります。

バックグラウンドコピーを利用したCopy-On-Writeスナップショット

バックグラウンドコピーを利用したCopy-On-Writeでは、CoWの瞬時スナップショットデータを、バックグラウンドプロセスを利用して元の場所からスナップショット用ストレージにコピーする。これにより、元データのクローンまたはミラーが作成されます。

クラッシュコンシステントとアプリケーションコンシステント

スナップショットの整合性によって以下の種類があります。

  • クラッシュコンシステントは、仮想ディスクに既に書き込まれたデータのみを記録し、メモリ内のデータまたは保留中のI/O操作は含まれません。仮想マシンが正常なシャットダウンをせずに強制終了した状態と同じになります。
  • ファイルシステムコンシステントは、仮想ディスク上のデータに加えて、メモリ内のすべてのデータと保留中のI/O操作を記録します。
  • アプリケーションコンシステントは、メモリ上のデータ、及びすべてのトランザクションのスナップショットが取得されます。

VMwareスナップショットについて

VMwareのスナップショットを作成する際は、元の仮想ディスクの状態が維持され、新たに差分ディスクが作成されます。

スナップショットを取得すると、edit settingの画面では、VM名-000001.vmdkといった差分ディスクが接続された状態となります。

スナップショットはチェーン構造になっており、複数スナップショットを取得した場合、間の差分ディスクが破損すると、リストアができなくなります。

また、スナップショットを取得したり、統合する際に性能影響があります。

VMwareのスナップショットは、長期保存の想定ではなく、バージョンアップ直前の一時的なバックアップなどに利用されます。

差分ディスクへの書き込みは元の仮想ディスクのサイズまで最大拡張されます。

差分ディスクをスパースディスクと呼んでおり、コピーオンライトを利用しているようです。

vSphere VMFS データストアの概念と操作

スナップショットのオプションごとの違い

電源オンの場合、スナップショットのオプションを選択することができます。

  • 仮想マシンのメモリを含めるにチェックがない場合は、クラッシュコンシステントなスナップショットになります。電源オフの状態で静止点に戻ります。
  • 仮想マシンのメモリを含めるにチェックがある場合は、メモリの状態もスナップショットに含まれます。電源オンの状態で静止点に戻ります。おそらくファイルシステムコンシステント相当になるのではと思いますが、記述は見つけられませんでした。
  • ゲスト ファイル システムの静止は、仮想マシンのメモリを含めるがオフの時に選択できるオプションになっており、チェックをがある場合、進行中のプロセスを一時停止することで一貫性がある状態となり、ファイルシステムコンシステントとアプリケーションコンシステントになります。

VMware Knowledge Base

ゲスト ファイル システムの静止は、ゲストOS側での違いや対応が必要となり、WindowsではVSSを利用できるようにし、Linuxではスクリプトを配置することで、静止時に事前スクリプトを実行し、スナップショット取得後に事後スクリプトを実行することで、アプリケーションコンシステントなスナップショットを取ることができます。

VMware Knowledge Base

VMwareでのバックアップ

VMwareでバックアップをする方法はいくつかあります。

一番簡単なのは、クローンで仮想マシンをコピーをとっておくことです。

次に、VADP(vStorage APIs for Data Protection)という、バックアップ用のAPIを利用することでバックアップを取得することができます。Changed Block Tracking (CBT) と呼ばれる増分バックアップカニズムも提供されます。 こちらはバックアップ製品で利用されているものになり、LANを経由してバックアップを取得することができます。ただし、スナップショット機能を利用していることもあり、スナップショットを取得したり、統合する際に性能影響が出てしまいます。

VMware Knowledge Base

VADPで使われているVDDKについて前回記事にしてみました。

hidemium.hatenablog.com

VMwareのスナップショット単体の利用は、長期保存の想定ではなく、バージョンアップ直前の一時的なバックアップなどに利用されるため、長期のバックアップには向きません。

vSphere Replicationを使う方法もあり、こちらはスナップショットではなく、VMkernelのVSCSI filterを通じて、IOを途中で吸い出して転送しており、RPOを短くすることができるようです。

Failover and Remediation Techniques

参考

VDDKのサンプルコードを動かしてみる

VDDK (Virtual Disk Development Kit) は、VMDKの作成やアクセスを行うことができるC ライブラリになります。VDDKのサンプルコードを動かしてみたので、書いてみようと思います。

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • VDDK 7.0.3.3
  • Ubuntu 22.04 (作業用VM)

VDDKとは

VDDKの中には以下のコンポーネントが含まれています。

  • 仮想ディスク ライブラリ。VMDK ファイルを操作するための C 関数呼び出しのセット
  • ディスク マウント ライブラリ。VMDK ファイル システムをリモート マウントするための C 関数呼び出しのセット
  • Visual Studio または GNU C コンパイラコンパイルできる C++ コード サンプル
  • PDF マニュアルとオンライン HTML リファレンス

仮想ディスク ライブラリには、以下の機能があります。

  • 仮想ディスク ファイルの作成、変換、拡張、最適化、縮小、および名前変更
  • REDO ログ (親子ディスク チェーン、つまりデルタ)の作成、VMDK ファイルの削除
  • VMDK ファイル内の任意の場所のデータへのランダムな読み取り/書き込みアクセス、メタデータを読み取り
  • SANやHotAddといった高度なトランスポートを使用したリモートの vSphere ストレージ接続

インストール

それでは、VDDKをインストールしてみます。

以下からVDDKをダウンロードします。

VMware Virtual Disk Development Kit (VDDK) - 7.0

ダウンロードすると、以下のファイルを入手することができます。

  • VMware-vix-disklib-7.0.3-21933544.x86_64.tar.gz

以下のディレクトリに移動し、解凍を行います。

cd /u
sudo tar xzf ./VMware-vix-disklib-7.0.3-21933544.x86_64.tar.gz 

以下の設定を行い、共有ライブラリをシステムに認識させます。

sudo vi /etc/ld.so.conf.d/vmware-vix-disklib.conf 
/usr/lib/vmware-vix-disklib-distrib/lib64
sudo ldconfig

共有ライブラリのパスが通っていない場合、以下のようなエラーが出ます。

./vixDiskLibSample: error while loading shared libraries: libvixDiskLib.so.7.0.3: cannot open shared object file: No such file or directory

サンプルコードのあるディレクトリに移動します。

cd /usr/lib/vmware-vix-disklib-distrib/doc/samples/diskLib/
tree
.
├── Makefile
├── vixDiskLibSample
├── vixDiskLibSample.cpp
└── vixMntapiSample

usageを見てみます。

sudo ./vixDiskLibSample
Usage: vixdisklibsample.exe command [options] diskPath

List of commands (all commands are mutually exclusive):
 -create : creates a sparse virtual disk with capacity specified by -cap
 -redo parentPath : creates a redo log 'diskPath' for base disk 'parentPath'
 -info : displays information for specified virtual disk
 -dump : dumps the contents of specified range of sectors in hexadecimal
 -mount : mount the virtual disk specified
 -fill : fills specified range of sectors with byte value specified by -val
 -wmeta key value : writes (key,value) entry into disk's metadata table
 -rmeta key : displays the value of the specified metada entry
 -meta : dumps all entries of the disk's metadata
 -clone sourcePath : clone source vmdk possibly to a remote site
 -compress type: specify the compression type for nbd transport mode
 -readbench blocksize: Does a read benchmark on a disk using the 
specified I/O block size (in sectors).
 -writebench blocksize: Does a write benchmark on a disk using the
specified I/O block size (in sectors). WARNING: This will
overwrite the contents of the disk specified.
 -readasyncbench blocksize: Does an async read benchmark on a disk using the 
specified I/O block size (in sectors).
 -writeasyncbench blocksize: Does an async write benchmark on a disk using the
specified I/O block size (in sectors). WARNING: This will
overwrite the contents of the disk specified.
 -getallocatedblocks : gets allocated block list on a disk using the 
specified I/O block size (in sectors).
 -check repair: Check a sparse disk for internal consistency, where repair is a boolean value to indicate if a repair operation should be attempted.

各オプションについてイメージが付きづらいですが、vixDiskLibSampleに対応する関数とコールされるVDDKの関数の詳細はドキュメントに記載あります。

Home

vixDiskLibSampleを実行するために、vCenterのThumbprintが必要なため、govcで取得します。

govc about.cert
:
Thumbprints:                 
  SHA-256 Thumbprint:      <Thumbprint> 
  SHA-1 Thumbprint:        <Thumbprint> 

まずは、仮想マシンのVMDKの情報を取得してみます。

sudo ./vixDiskLibSample -info -host <vCenter IPアドレス> -user <username> -password <password> -vm "moref=<仮想マシンのmoid>" -thumb  <Thumbprint> "[Datastore] test/test.vmdk"

Disk[0] "[Datastore] test/test.vmdk is opened using transport mode "nbd".
capacity             = 209715200 sectors
logical sector size  = 512 bytes
physical sector size = 512 bytes
number of links      = 1
adapter type         = LsiLogic SCSI
BIOS geometry        = 0/0/0
physical geometry    = 13054/255/63
Transport modes supported by vixDiskLib: file:nbdssl:nbd
Disk[0] is closed.

VMDKの容量やトランスポートモードでNBDがサポートされることが確認できます。

次に、dumpというものを試し見ます。dumpは VixDiskLib_Read() を呼び出してセクタを取得し、セクタの内容を16 進数で出力します。

sudo ./vixDiskLibSample -dump -host <vCenter IPアドレス> -user <username> -password <password> -vm "moref=<仮想マシンのmoid>" -thumb  <Thumbprint> "[Datastore] test/test.vmdk"
Disk[0] "[Datastore] test/test.vmdk is opened using transport mode "nbd".
0000 : eb 63 90 00 00 00 00 00 00 00 00 00 00 00 00 00   .c..............
0010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050 : 00 00 00 00 00 00 00 00 00 00 00 80 00 08 00 00   ................
0060 : 00 00 00 00 ff fa 90 90 f6 c2 80 74 05 f6 c2 70   ...........t...p
0070 : 74 02 b2 80 ea 79 7c 00 00 31 c0 8e d8 8e d0 bc   t....y|..1......
0080 : 00 20 fb a0 64 7c 3c ff 74 02 88 c2 52 bb 17 04   . ..d|<.t...R...
0090 : f6 07 03 74 06 be 88 7d e8 17 01 be 05 7c b4 41   ...t...}.....|.A
00a0 : bb aa 55 cd 13 5a 52 72 3d 81 fb 55 aa 75 37 83   ..U..ZRr=..U.u7.
00b0 : e1 01 74 32 31 c0 89 44 04 40 88 44 ff 89 44 02   ..t21..D.@.D..D.
00c0 : c7 04 10 00 66 8b 1e 5c 7c 66 89 5c 08 66 8b 1e   ....f..\|f.\.f..
00d0 : 60 7c 66 89 5c 0c c7 44 06 00 70 b4 42 cd 13 72   `|f.\..D..p.B..r
00e0 : 05 bb 00 70 eb 76 b4 08 cd 13 73 0d 5a 84 d2 0f   ...p.v....s.Z...
00f0 : 83 d0 00 be 93 7d e9 82 00 66 0f b6 c6 88 64 ff   .....}...f....d.
0100 : 40 66 89 44 04 0f b6 d1 c1 e2 02 88 e8 88 f4 40   @f.D...........@
0110 : 89 44 08 0f b6 c2 c0 e8 02 66 89 04 66 a1 60 7c   .D.......f..f.`|
0120 : 66 09 c0 75 4e 66 a1 5c 7c 66 31 d2 66 f7 34 88   f..uNf.\|f1.f.4.
0130 : d1 31 d2 66 f7 74 04 3b 44 08 7d 37 fe c1 88 c5   .1.f.t.;D.}7....
0140 : 30 c0 c1 e8 02 08 c1 88 d0 5a 88 c6 bb 00 70 8e   0........Z....p.
0150 : c3 31 db b8 01 02 cd 13 72 1e 8c c3 60 1e b9 00   .1......r...`...
0160 : 01 8e db 31 f6 bf 00 80 8e c6 fc f3 a5 1f 61 ff   ...1..........a.
0170 : 26 5a 7c be 8e 7d eb 03 be 9d 7d e8 34 00 be a2   &Z|..}....}.4...
0180 : 7d e8 2e 00 cd 18 eb fe 47 52 55 42 20 00 47 65   }.......GRUB .Ge
0190 : 6f 6d 00 48 61 72 64 20 44 69 73 6b 00 52 65 61   om.Hard Disk.Rea
01a0 : 64 00 20 45 72 72 6f 72 0d 0a 00 bb 01 00 b4 0e   d. Error........
01b0 : cd 10 ac 3c 00 75 f4 c3 00 00 00 00 00 00 00 00   ...<.u..........
01c0 : 02 00 ee ff ff ff 01 00 00 00 ff ff 7f 0c 00 00   ................
01d0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
01e0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
01f0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa   ..............U.

Disk[0] is closed.

createを使うと、vmdkの作成ができます。

また、fillをつかうと VixDiskLib_Write() を呼び出して、ディスク セクタを 1 で埋めることができます。

createとfillとdumpを組み合わせて、動作を見てみます。

sudo ./vixDiskLibSample  -create sample.vmdk  # VMDK作成
sudo ./vixDiskLibSample  -fill -val 1 sample.vmdk # ディスクセクタを1で埋める
sudo ./vixDiskLibSample  -fill -val 2 -start 1 -count 1 sample.vmdk # ディスクセクタを2で埋める
sudo ./vixDiskLibSample  -dump -start 0 -count 2 sample.vmdk # セクタの内容を表示
Disk[0] "sample.vmdk" is opened using transport mode "file".
0000 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0010 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0020 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0030 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0040 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0050 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0060 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0070 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0080 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0090 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00a0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00b0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00c0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00d0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00e0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00f0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0100 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0110 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0120 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0130 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0140 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0150 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0160 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0170 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0180 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0190 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01a0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01b0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01c0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01d0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01e0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01f0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................

0000 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0010 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0020 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0030 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0040 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0050 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0060 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0070 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0080 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0090 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00a0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00b0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00c0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00d0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00e0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00f0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0100 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0110 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0120 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0130 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0140 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0150 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0160 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0170 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0180 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0190 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01a0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01b0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01c0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01d0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01e0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01f0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................

Disk[0] is closed.

vixDiskLibSampleコマンドはC++で書かれており、ソースコードはvixDiskLibSample.cppで確認することができます。

エラー関連

共有ライブラリ

共有ライブラリの設定後、aptを実行すると、以下のようなエラーが発生します。

sudo apt
apt: /usr/lib/vmware-vix-disklib-distrib/lib64/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by /lib/x86_64-linux-gnu/libapt-private.so.0.0)
apt: /usr/lib/vmware-vix-disklib-distrib/lib64/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by /lib/x86_64-linux-gnu/libapt-pkg.so.6.0)

VDDKの「libstdc++.so.6」がどのバージョンまでの GLIBCXX に対応しているか確認したところ、GLIBCXX_3.4.28までしか対応していないことが分かります。

strings /usr/lib/vmware-vix-disklib-distrib/lib64/libstdc++.so.6 | grep GLIBCXX
GLIBCXX_3.4
:
GLIBCXX_3.4.27
GLIBCXX_3.4.28

VDDKのバージョンを新しくするとこのあたりは変わってくるかもしれません。

/etc/ld.so.conf/ ではなく、環境変数でライブラリを指定する場合は、上記のようなエラーは発生しませんした。

export LD_LIBRARY_PATH=/usr/lib/vmware-vix-disklib-distrib/lib64

sudoで実行する場合、環境変数を渡すことができない場合があり、以下のようなエラーがでることがあります。

./vixDiskLibSample: error while loading shared libraries: libvixDiskLib.so.7.0.3: cannot open shared object file: No such file or directory

その場合は、以下のようにsudoに環境変数を指定して実行することもできます。

sudo env LD_LIBRARY_PATH=/usr/lib/vmware-vix-disklib-distrib/lib64 <command>

vixDiskLibSample

-thumbを指定しない場合、以下のエラーが表示されます。

サンプルコードはパラメーターのエラーが分かる程度で、どのパラメータが間違っているかは教えてくれません。

Error: [bora/apps/vixDiskLib/sampleProgram/vixDiskLibSample.cpp:1545]  3 One of the parameters was invalid

コンパイル

vixDiskLibSample.cppのコンパイルを試してみます。

G++のコンパイルに必要なパッケージをインストールします。G++ 10のパッケージもインストールしておきます。

sudo apt install build-essential
sudo apt install g++-10
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 11
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 10
sudo update-alternatives --config g++
# g++-10を選択します。

g++ 11でmakeした際に、以下のエラーメッセージが発生しコンパイルに失敗しました。

g++ --version
g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ sudo make 
g++ -std=c++1y -lpthread -o vix-disklib-sample -I../../../include -L../../../lib64 vixDiskLibSample.cpp -ldl -lz -lsqlite3 -lcurl -lssl -lcrypto -lcares -lvixDiskLib
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `std::__exception_ptr::exception_ptr::exception_ptr(std::__exception_ptr::exception_ptr const&)':
vixDiskLibSample.cpp:(.text._ZNSt15__exception_ptr13exception_ptrC2ERKS0_[_ZNSt15__exception_ptr13exception_ptrC5ERKS0_]+0x36): undefined reference to `std::__exception_ptr::exception_ptr::_M_addref()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `std::__exception_ptr::exception_ptr::~exception_ptr()':
vixDiskLibSample.cpp:(.text._ZNSt15__exception_ptr13exception_ptrD2Ev[_ZNSt15__exception_ptr13exception_ptrD5Ev]+0x24): undefined reference to `std::__exception_ptr::exception_ptr::_M_release()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<DiskIOPipeline::DiskInfo*>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorIPN14DiskIOPipeline8DiskInfoEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorIPN14DiskIOPipeline8DiskInfoEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::thread>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt6threadE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt6threadE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<DiskIOPipeline::DiskInfo>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorIN14DiskIOPipeline8DiskInfoEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorIN14DiskIOPipeline8DiskInfoEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<VixDiskLibBlock>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorI15VixDiskLibBlockE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorI15VixDiskLibBlockE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `std::__atomic_futex_unsigned<2147483648u>::_M_load_and_test_until_steady(unsigned int, unsigned int, bool, std::memory_order, bool, std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >)':
vixDiskLibSample.cpp:(.text._ZNSt23__atomic_futex_unsignedILj2147483648EE29_M_load_and_test_until_steadyEjjbSt12memory_orderbNSt6chrono8durationIlSt5ratioILl1ELl1EEEENS3_IlS4_ILl1ELl1000000000EEEE[_ZNSt23__atomic_futex_unsignedILj2147483648EE29_M_load_and_test_until_steadyEjjbSt12memory_orderbNSt6chrono8durationIlSt5ratioILl1ELl1EEEENS3_IlS4_ILl1ELl1000000000EEEE]+0x7c): undefined reference to `std::__atomic_futex_unsigned_base::_M_futex_wait_until_steady(unsigned int*, unsigned int, bool, std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >)'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<ThreadData>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorI10ThreadDataE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorI10ThreadDataE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<unsigned long>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorImE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorImE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::_Fwd_list_node<std::future<std::shared_ptr<VixDisk> > > >::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt14_Fwd_list_nodeISt6futureISt10shared_ptrI7VixDiskEEEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt14_Fwd_list_nodeISt6futureISt10shared_ptrI7VixDiskEEEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<std::mutex, std::allocator<std::mutex>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceISt5mutexSaIS2_ELNS_12_Lock_policyE2EEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceISt5mutexSaIS2_ELNS_12_Lock_policyE2EEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<VixDisk, std::allocator<VixDisk>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI7VixDiskSaIS2_ELNS_12_Lock_policyE2EEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI7VixDiskSaIS2_ELNS_12_Lock_policyE2EEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o:vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt10_List_nodeIPhEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt10_List_nodeIPhEE8allocateEmPKv]+0x49): more undefined references to `std::__throw_bad_array_new_length()' follow
collect2: error: ld returned 1 exit status
make: *** [Makefile:14: vix-disklib-sample] Error 1

vix-disklib-sampleのビルドは無事成功しましたが、vix-mntapi-sample側で以下のエラーが発生しました。

$ sudo make
g++ -std=c++1y -lpthread -o vix-disklib-sample -I../../../include -L../../../lib64 vixDiskLibSample.cpp -ldl -lz -lsqlite3 -lcurl -lssl -lcrypto -lcares -lvixDiskLib
g++ -std=c++1y -lpthread -o vix-mntapi-sample -DFOR_MNTAPI -I../../../include -L../../../lib64 vixDiskLibSample.cpp -ldl -lz -lsqlite3 -lcurl -lssl -lcrypto -lcares \
   -lfuse -lvixDiskLib -lvixMntapi
/usr/bin/ld: cannot find -lfuse: No such file or directory
collect2: error: ld returned 1 exit status
make: *** [Makefile:17: vix-mntapi-sample] Error 1

Ubuntu 22.04で libfuseが 3系がデフォルトになってしまっており、libfuse2をインストールする必要があります。

ldconfig -p | grep fuse
        libfuse3.so.3 (libc6,x86-64) => /lib/x86_64-linux-gnu/libfuse3.so.3
sudo apt install libfuse2
ldconfig -p | grep fuse
        libfuse3.so.3 (libc6,x86-64) => /lib/x86_64-linux-gnu/libfuse3.so.3
        libfuse.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/libfuse.so.2
ls -la /usr/lib/x86_64-linux-gnu | grep fuse
lrwxrwxrwx  1 root root       18 Mar 23  2022 libfuse3.so.3 -> libfuse3.so.3.10.5
-rw-r--r--  1 root root   252088 Mar 23  2022 libfuse3.so.3.10.5
lrwxrwxrwx  1 root root       16 Mar 23  2022 libfuse.so.2 -> libfuse.so.2.9.9
-rw-r--r--  1 root root   260376 Mar 23  2022 libfuse.so.2.9.9

libfuse.soというファイル名でしたが、libfuse.so.2という末尾にバージョン情報が入ってしまい、修正が必要になります。

sudo ln -s libfuse.so.2 /usr/lib/x86_64-linux-gnu/libfuse.so
ls -la /usr/lib/x86_64-linux-gnu | grep fuse
lrwxrwxrwx  1 root root       18 Mar 23  2022 libfuse3.so.3 -> libfuse3.so.3.10.5
-rw-r--r--  1 root root   252088 Mar 23  2022 libfuse3.so.3.10.5
lrwxrwxrwx  1 root root       12 Dec 25 02:19 libfuse.so -> libfuse.so.2
lrwxrwxrwx  1 root root       16 Mar 23  2022 libfuse.so.2 -> libfuse.so.2.9.9
-rw-r--r--  1 root root   260376 Mar 23  2022 libfuse.so.2.9.9

こちらで無事vix-mntapi-sampleのコンパイルも完了しました。

sudo make
g++ -std=c++1y -lpthread -o vix-mntapi-sample -DFOR_MNTAPI -I../../../include -L../../../lib64 vixDiskLibSample.cpp -ldl -lz -lsqlite3 -lcurl -lssl -lcrypto -lcares \
   -lfuse -lvixDiskLib -lvixMntapi

参考

Ansibleとcloud-initを利用した仮想マシンのカスタマイズ

以前、AnsibleのVMwareモジュールとcloud-initを利用した仮想マシンのカスタマイズを書きましたが、今回はAnsibleを使ったcloud-initを利用した仮想マシンのカスタマイズ方法について書いてみたいと思います。

hidemium.hatenablog.com

hidemium.hatenablog.com

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Ubuntu 22.04 (テンプレートVM)

インストール

前回の手順と比べてAnsibleのバージョンがだいぶ新しくなっているため、再度インストール手順を載せておきます。

community.vmware を利用したAnsibleが実行できる環境を準備してみようと思います。

Ansible 7(ansible-core 2.14)以降をインストールする場合は、Python 3.9以降がインストールされている必要があります。

Releases and maintenance — Ansible Documentation

venvの環境をインストールします。(Ansibleの公式サイトでは、pipxによるインストール手順に変更されてました。)

$ python3 --version
Python 3.9.5
$ sudo apt install python3.9-venv
$ mkdir ansible-vmware
$ cd ansible-vmware
$ python3 -m venv venv (python3 -m venv [仮想環境ディレクトリ名])

venvというフォルダが作成されました。

仮想環境のアクティベートし、Ansibleをインストールします。

$ source venv/bin/activate
(venv) $ python -m pip install ansible

Collectionをインストールします。requirements.ymlを用意し、インストールしてみます。

(venv) $ vi requirements.yml
collections:
- name: community.vmware
(venv) $ ansible-galaxy collection install -r requirements.yml

collectionのフォルダ内にrequirements.txtがあり、こちらを利用し、pyvmomiとvSphere Automation Python SDKをインストールします。

(venv) $ python -m pip install -r ~/.ansible/collections/ansible_collections/community/vmware/requirements.txt
(venv) $ pip list
Package                            Version
---------------------------------- ---------
ansible                            8.5.0
ansible-core                       2.15.5
certifi                            2023.7.22
cffi                               1.16.0
charset-normalizer                 3.3.0
cryptography                       41.0.4
idna                               3.4
importlib-resources                5.0.7
Jinja2                             3.1.2
lxml                               4.9.3
MarkupSafe                         2.1.3
nsx-policy-python-sdk              4.1.0.1.0
nsx-python-sdk                     4.1.0.1.0
nsx-vmc-aws-integration-python-sdk 4.1.0.1.0
nsx-vmc-policy-python-sdk          4.1.0.1.0
packaging                          23.2
pip                                23.3
pkg_resources                      0.0.0
pycparser                          2.21
pyOpenSSL                          23.2.0
pyvmomi                            8.0.2.0
PyYAML                             6.0.1
requests                           2.31.0
resolvelib                         1.0.1
setuptools                         44.0.0
six                                1.16.0
urllib3                            2.0.6
vapi-common-client                 2.44.0
vapi-runtime                       2.44.0
vcenter-bindings                   4.2.0
vmwarecloud-aws                    1.64.0
vmwarecloud-draas                  1.23.0
vsphere-automation-sdk             1.86.0

前回のAnsibleの構成をもとに、cloud-initを利用した仮想マシンをカスタマイズするロールを書いていきます。

hidemium.hatenablog.com

まず、rolesに guest_cloud_init というロールを作成します。ロールの作成はansible-galaxyコマンドを利用します。

$ cd ~/ansible-vmware
$ cd roles
$ ansible-galaxy init guest_cloud_init
$ tree -L 2
.
└── guest_cloud_init
    ├── defaults
    │   └── main.yml
    ├── files
    ├── handlers
    │   └── main.yml
    ├── meta
    │   └── main.yml
    ├── README.md
    ├── tasks
    │   └── main.yml
    ├── templates
    ├── tests
    │   ├── inventory
    │   └── test.yml
    └── vars
        └── main.yml

taskのmain.ymlを編集します。テンプレートからのクローンと、仮想マシンメタデータとユーザデータを設定を行っています。

$ cd guest_cloud_init
$ vi task/main.yml
---
- name:  Clone a virtual machine from Linux template
  community.vmware.vmware_guest:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ datacenter_name }}"
    validate_certs: no
    state: present
    template: "{{ template }}"
    folder: "/{{ datacenter_name }}/vm"
    name: "{{ vm_name }}"
    cluster: "{{ cluster_name }}"
  delegate_to: localhost

- name:  Set the metadata and userdata for the virtual machine
  community.vmware.vmware_guest:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: no
    state: present
    name: "{{ vm_name }}"
    advanced_settings:
      - key: guestinfo.metadata
        value: "{{ lookup('template', 'metadata.j2') | b64encode }}"
      - key: guestinfo.metadata.encoding
        value: base64
      - key: guestinfo.userdata
        value: "{{ lookup('template', 'userdata.j2') | b64encode }}"
      - key: guestinfo.userdata.encoding
        value: base64
  delegate_to: localhost

templateのmetadata.j2を編集します。このようにすることで、仮想マシン名とゲストOS内のホスト名を同一にすることができます。テンプレート内ではネットワーク設定は固定としていますが、変数として定義も可能です。

$ vi template/metadata.j2
---
instance-id: {{ vm_name }}
local-hostname: {{ vm_name }}
hostname: {{ vm_name }}
network:
  version: 2
  ethernets:
    ens192:
      dhcp4: false
      addresses:
      - 192.168.1.2/24
      gateway4: 192.168.1.1
      nameservers:
        addresses:
        - 192.168.1.1

templateのuserdata.j2を編集します。

$ vi template/userdata.j2
---
#cloud-config

users:
  - default
  - name: username
    ssh_authorized_keys:
      - 公開鍵をペースト
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: sudo, wheel
    lock_passwd: true
    shell: /bin/bash
 
packages:
    - 必要なパッケージ名を追加

次に、taskの実行に必要な変数について homelab_vm グループの情報として取得できるように、group_vars配下にフォルダを作成して記載していきます。

$ cd ~/ansible-vmware/group_vars
$ mkdir homelab_vm
$ cd homelab_vm
$ vi main.yml
---
vcenter_hostname: vCenter Name
datacenter_name: Datacenter Name
cluster_name: Cluster Name
template: Template Name
vm_name: VM Name

playbookを実行できるか確認します。

$ ansible-playbook -i hosts pb_guest_cloud_init.yml
PLAY [homelab_vm] ************************************************************************************************************************************************************************

TASK [guest_cloud_init : Clone a virtual machine from Linux template] ******************************************************************************************************
changed: [VM name -> localhost]

TASK [guest_cloud_init : Set the metadata and userdata for the virtual machine] ******************************************************************************************
changed: [VM name -> localhost]

PLAY RECAP *****************************************************************************************************************************************************************
VM name : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

全体構成として以下のようになりました。

$ tree -L 4
.
├── ansible.cfg
├── group_vars
│   ├── all
│   │   ├── main.yml
│   │   └── vault.yml
│   └── homelab_vm
│       └── main.yml
├── host_vars
├── hosts
├── pb_guest_cloud_init.yml
├── requirements.yml
├── roles
│   └── guest_cloud_init
│       ├── defaults
│       │   └── main.yml
│       ├── files
│       ├── handlers
│       │   └── main.yml
│       ├── meta
│       │   └── main.yml
│       ├── README.md
│       ├── tasks
│       │   └── main.yml
│       ├── templates
│       │   ├── metadata.j2
│       │   └── userdata.j2
│       ├── tests
│       │   ├── inventory
│       │   └── test.yml
│       └── vars
│           └── main.yml
└── venv
    ├── bin
~ 中略 ~

VMが起動後に、ホスト名が変更されたことや、公開鍵認証を利用してsshでログインできることを確認します。

$ ssh username@192.168.1.2
$ hostnamectl

Ansibleのvmware_guestモジュールは、カスタマイズ仕様を使ったゲストOSの設定が可能ですが、cloud-initには対応はしていなかったため、このような方法でcloud-initが利用できるようになり、より自動化がしやすくなっていくのでは思います。

参考